Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
73a2ae9
feat: beautified lot of terminal output (thanks cursor!)
arkanoider Oct 17, 2025
7b4ef40
fix: fix test for gh actions
arkanoider Oct 17, 2025
ede4da5
CodeRabbit Generated Unit Tests: Add 78 unit tests and comprehensive …
coderabbitai[bot] Oct 18, 2025
2c7ef7e
chore: fix in cargo.toml to remove openssl
arkanoider Oct 18, 2025
1ae0701
chore: fix fmt checks
arkanoider Oct 18, 2025
f30909e
fix: tests now are ok fixed docs of code coverage
arkanoider Oct 18, 2025
a2b4c9c
fix: clippy in gh actions fixes
arkanoider Oct 18, 2025
8e34570
fix: removed one test related to HOME folder
arkanoider Oct 18, 2025
77641c9
Merge pull request #147 from MostroP2P/coderabbitai/utg/7b4ef40
arkanoider Oct 18, 2025
e36431a
chore: typo on test number
arkanoider Oct 18, 2025
cb5bc35
Adjust text to fir smaller screens
grunch Oct 18, 2025
cd63ce0
Fix race condition
grunch Oct 18, 2025
2a6d88f
feat: big refactor in all the cosmetics of mostro-cli interface with …
arkanoider Oct 18, 2025
6dfe647
Merge commit '2a6d88f82ad5ebf59a2414be81cd8e819c5c3e6f' into terminal…
arkanoider Oct 18, 2025
05210e4
chore: fixed typo
arkanoider Oct 18, 2025
fa44980
chore: some fixes on logs and added a check on take dispute to verify…
arkanoider Oct 18, 2025
3b5747c
chore: fix fmt
arkanoider Oct 18, 2025
5dc45a6
fix: fixed from field in getdm command
arkanoider Oct 19, 2025
dbd2fba
chore: removed typo
arkanoider Oct 19, 2025
a0aaaad
Fix clippy errors
grunch Oct 20, 2025
8227d1d
feat: automatic update of child order in database when a range sale o…
arkanoider Oct 20, 2025
69c4ded
Merge commit 'a0aaaad2041709b1784cddb1b92b0e90450ad903' into terminal…
arkanoider Oct 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
432 changes: 105 additions & 327 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,10 @@ uuid = { version = "1.18.1", features = [
] }
dotenvy = "0.15.6"
lightning-invoice = { version = "0.33.2", features = ["std"] }
reqwest = { version = "0.12.23", features = ["json"] }
reqwest = { version = "0.12.23", default-features = false, features = ["json","rustls-tls"] }
mostro-core = "0.6.56"
lnurl-rs = "0.9.0"
lnurl-rs = { version = "0.9.0", default-features = false, features = ["ureq"] }
pretty_env_logger = "0.5.0"
openssl = { version = "0.10.73", features = ["vendored"] }
sqlx = { version = "0.8.6", features = ["sqlite", "runtime-tokio-rustls"] }
bip39 = { version = "2.2.0", features = ["rand"] }
dirs = "6.0.0"
Expand Down
13 changes: 10 additions & 3 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod last_trade_index;
pub mod list_disputes;
pub mod list_orders;
pub mod new_order;
pub mod orders_info;
pub mod rate_user;
pub mod restore;
pub mod send_dm;
Expand All @@ -25,6 +26,7 @@ use crate::cli::last_trade_index::execute_last_trade_index;
use crate::cli::list_disputes::execute_list_disputes;
use crate::cli::list_orders::execute_list_orders;
use crate::cli::new_order::execute_new_order;
use crate::cli::orders_info::execute_orders_info;
use crate::cli::rate_user::execute_rate_user;
use crate::cli::restore::execute_restore;
use crate::cli::send_dm::execute_send_dm;
Expand Down Expand Up @@ -295,6 +297,12 @@ pub enum Commands {
},
/// Get last trade index of user
GetLastTradeIndex {},
/// Request detailed information for specific orders
OrdersInfo {
/// Order IDs to request information for
#[arg(short, long)]
order_ids: Vec<Uuid>,
},
}

fn get_env_var(cli: &Cli) {
Expand Down Expand Up @@ -363,8 +371,6 @@ pub async fn run() -> Result<()> {
cmd.run(&ctx).await?;
}

println!("Bye Bye!");

Ok(())
}

Expand Down Expand Up @@ -512,8 +518,9 @@ impl Commands {

// Simple commands
Commands::Restore {} => {
execute_restore(&ctx.identity_keys, ctx.mostro_pubkey, &ctx.client).await
execute_restore(&ctx.identity_keys, ctx.mostro_pubkey, ctx).await
}
Commands::OrdersInfo { order_ids } => execute_orders_info(order_ids, ctx).await,
}
}
}
33 changes: 25 additions & 8 deletions src/cli/add_invoice.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use crate::parser::common::{
create_emoji_field_row, create_field_value_header, create_standard_table,
};
use crate::util::{print_dm_events, send_dm, wait_for_dm};
use crate::{cli::Context, db::Order, lightning::is_valid_invoice};
use anyhow::Result;
Expand All @@ -17,15 +20,29 @@ pub async fn execute_add_invoice(order_id: &Uuid, invoice: &str, ctx: &Context)
.ok_or(anyhow::anyhow!("Missing trade keys"))?;

let order_trade_keys = Keys::parse(&trade_keys)?;
println!(
"Order trade keys: {:?}",
order_trade_keys.public_key().to_hex()
);

println!(
"Sending a lightning invoice for order {} to mostro pubId {}",
order_id, ctx.mostro_pubkey
);
println!("⚡ Add Lightning Invoice");
println!("═══════════════════════════════════════");

let mut table = create_standard_table();
table.set_header(create_field_value_header());
table.add_row(create_emoji_field_row(
"📋 ",
"Order ID",
&order_id.to_string(),
));
table.add_row(create_emoji_field_row(
"🔑 ",
"Trade Keys",
&order_trade_keys.public_key().to_hex(),
));
table.add_row(create_emoji_field_row(
"🎯 ",
"Target",
&ctx.mostro_pubkey.to_string(),
));
println!("{table}");
println!("💡 Sending lightning invoice to Mostro...\n");
// Check invoice string
let ln_addr = LightningAddress::from_str(invoice);
let payload = if ln_addr.is_ok() {
Expand Down
29 changes: 24 additions & 5 deletions src/cli/adm_send_dm.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
use crate::cli::Context;
use crate::parser::common::{
create_emoji_field_row, create_field_value_header, create_standard_table,
};
use crate::util::send_admin_gift_wrap_dm;
use anyhow::Result;
use nostr_sdk::prelude::*;

pub async fn execute_adm_send_dm(receiver: PublicKey, ctx: &Context, message: &str) -> Result<()> {
println!(
"SENDING DM with admin keys: {}",
ctx.context_keys.public_key().to_hex()
);
println!("👑 Admin Direct Message");
println!("═══════════════════════════════════════");
let mut table = create_standard_table();
table.set_header(create_field_value_header());
table.add_row(create_emoji_field_row(
"🔑 ",
"Admin Keys",
&ctx.context_keys.public_key().to_hex(),
));
table.add_row(create_emoji_field_row(
"🎯 ",
"Recipient",
&receiver.to_string(),
));
table.add_row(create_emoji_field_row("💬 ", "Message", message));
println!("{table}");
println!("💡 Sending admin gift wrap message...\n");

send_admin_gift_wrap_dm(&ctx.client, &ctx.context_keys, &receiver, message).await?;

println!("Admin gift wrap message sent to {}", receiver);
println!(
"✅ Admin gift wrap message sent successfully to {}",
receiver
);

Ok(())
}
16 changes: 15 additions & 1 deletion src/cli/conversation_key.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
use crate::parser::common::{
print_info_line, print_key_value, print_section_header, print_success_message,
};
use anyhow::Result;
use nip44::v2::ConversationKey;
use nostr_sdk::prelude::*;

pub async fn execute_conversation_key(trade_keys: &Keys, receiver: PublicKey) -> Result<()> {
print_section_header("🔐 Conversation Key Generator");
print_key_value("🔑", "Trade Keys", &trade_keys.public_key().to_hex());
print_key_value("🎯", "Receiver", &receiver.to_string());
print_info_line("💡", "Deriving conversation key...");
println!();

// Derive conversation key
let ck = ConversationKey::derive(trade_keys.secret_key(), &receiver)?;
let key = ck.as_bytes();
Expand All @@ -11,7 +20,12 @@ pub async fn execute_conversation_key(trade_keys: &Keys, receiver: PublicKey) ->
ck_hex.push(format!("{:02x}", i));
}
let ck_hex = ck_hex.join("");
println!("Conversation key: {:?}", ck_hex);

println!("🔐 Conversation Key:");
println!("─────────────────────────────────────");
println!("{}", ck_hex);
println!("─────────────────────────────────────");
print_success_message("Conversation key generated successfully!");

Ok(())
}
16 changes: 12 additions & 4 deletions src/cli/dm_to_user.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use crate::parser::common::{
print_info_line, print_key_value, print_section_header, print_success_message,
};
use crate::{db::Order, util::send_gift_wrap_dm};
use anyhow::Result;
use nostr_sdk::prelude::*;
Expand All @@ -22,12 +25,17 @@ pub async fn execute_dm_to_user(
};

// Send the DM
println!(
"SENDING DM with trade keys: {}",
trade_keys.public_key().to_hex()
);
print_section_header("💬 Direct Message to User");
print_key_value("📋", "Order ID", &order_id.to_string());
print_key_value("🔑", "Trade Keys", &trade_keys.public_key().to_hex());
print_key_value("🎯", "Recipient", &receiver.to_string());
print_key_value("💬", "Message", message);
print_info_line("💡", "Sending gift wrap message...");
println!();

send_gift_wrap_dm(client, &trade_keys, &receiver, message).await?;

print_success_message("Gift wrap message sent successfully!");

Ok(())
}
10 changes: 9 additions & 1 deletion src/cli/get_dm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use nostr_sdk::prelude::*;

use crate::{
cli::Context,
parser::common::{print_key_value, print_section_header},
parser::dms::print_direct_messages,
util::{fetch_events_list, Event, ListKind},
};
Expand All @@ -14,6 +15,13 @@ pub async fn execute_get_dm(
from_user: &bool,
ctx: &Context,
) -> Result<()> {
print_section_header("📨 Fetch Direct Messages");
print_key_value("👤", "Admin Mode", if admin { "Yes" } else { "No" });
print_key_value("📤", "From User", if *from_user { "Yes" } else { "No" });
print_key_value("⏰", "Since", &format!("{} minutes ago", since));
print_key_value("💡", "Action", "Fetching direct messages...");
println!();

// Get the list kind
let list_kind = match (admin, from_user) {
(true, true) => ListKind::PrivateDirectMessagesUser,
Expand All @@ -34,6 +42,6 @@ pub async fn execute_get_dm(
}
}

print_direct_messages(&dm_events, &ctx.pool).await?;
print_direct_messages(&dm_events, Some(ctx.mostro_pubkey)).await?;
Ok(())
}
51 changes: 15 additions & 36 deletions src/cli/get_dm_user.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::cli::Context;
use crate::db::Order;
use crate::parser::common::{
print_info_line, print_key_value, print_no_data_message, print_section_header,
};
use crate::parser::dms::print_direct_messages;
use crate::util::{fetch_events_list, Event, ListKind};
use anyhow::Result;
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
use comfy_table::presets::UTF8_FULL;
use comfy_table::Table;
use mostro_core::prelude::*;
use nostr_sdk::prelude::*;

Expand All @@ -23,15 +24,19 @@ pub async fn execute_get_dm_user(since: &i64, ctx: &Context) -> Result<()> {

// Check if the trade keys are empty
if trade_keys_hex.is_empty() {
println!("No trade keys found in orders");
print_no_data_message("No trade keys found in orders");
return Ok(());
}

// Print the number of trade keys
println!(
"Searching for DMs in {} trade keys...",
trade_keys_hex.len()
print_section_header("📨 Fetch User Direct Messages");
print_key_value(
"🔍",
"Searching for DMs in trade keys",
&format!("{}", trade_keys_hex.len()),
);
print_key_value("⏰", "Since", &format!("{} minutes ago", since));
print_info_line("💡", "Fetching direct messages...");
println!();

let direct_messages = fetch_events_list(
ListKind::DirectMessagesUser,
Expand All @@ -47,7 +52,7 @@ pub async fn execute_get_dm_user(since: &i64, ctx: &Context) -> Result<()> {
let mut dm_events: Vec<(Message, u64, PublicKey)> = Vec::new();
// Check if the direct messages are empty
if direct_messages.is_empty() {
println!("You don't have any direct messages in your trade keys");
print_no_data_message("You don't have any direct messages in your trade keys");
return Ok(());
}
// Extract the direct messages
Expand All @@ -57,32 +62,6 @@ pub async fn execute_get_dm_user(since: &i64, ctx: &Context) -> Result<()> {
}
}

let mut table = Table::new();
table
.load_preset(UTF8_FULL)
.apply_modifier(UTF8_ROUND_CORNERS)
.set_content_arrangement(comfy_table::ContentArrangement::Dynamic)
.set_header(vec!["Time", "From", "Message"]);

for (message, created_at, sender_pubkey) in dm_events.iter() {
let datetime = chrono::DateTime::from_timestamp(*created_at as i64, 0);
let formatted_date = match datetime {
Some(dt) => dt.format("%Y-%m-%d %H:%M:%S").to_string(),
None => "Invalid timestamp".to_string(),
};

let inner = message.get_inner_message_kind();
let message_str = match &inner.payload {
Some(Payload::TextMessage(text)) => text.clone(),
_ => format!("{:?}", message),
};

let sender_hex = sender_pubkey.to_hex();

table.add_row(vec![&formatted_date, &sender_hex, &message_str]);
}

println!("{table}");
println!();
print_direct_messages(&dm_events, Some(ctx.mostro_pubkey)).await?;
Ok(())
}
12 changes: 7 additions & 5 deletions src/cli/last_trade_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use nostr_sdk::prelude::*;

use crate::{
cli::Context,
parser::common::{print_key_value, print_section_header},
parser::{dms::print_commands_results, parse_dm_events},
util::{send_dm, wait_for_dm},
};
Expand Down Expand Up @@ -31,10 +32,11 @@ pub async fn execute_last_trade_index(
);

// Log the sent message
println!(
"Sent request to Mostro to get last trade index of user {}",
identity_keys.public_key()
);
print_section_header("🔢 Last Trade Index Request");
print_key_value("👤", "User", &identity_keys.public_key().to_string());
print_key_value("🎯", "Target", &mostro_key.to_string());
print_key_value("💡", "Action", "Requesting last trade index from Mostro...");
println!();

// Wait for incoming DM
let recv_event = wait_for_dm(ctx, Some(identity_keys), sent_message).await?;
Expand All @@ -44,7 +46,7 @@ pub async fn execute_last_trade_index(
if let Some((message, _, _)) = messages.first() {
let message = message.get_inner_message_kind();
if message.action == Action::LastTradeIndex {
print_commands_results(message, ctx).await?
print_commands_results(message, ctx).await?;
} else {
return Err(anyhow::anyhow!(
"Received response with mismatched action. Expected: {:?}, Got: {:?}",
Expand Down
10 changes: 5 additions & 5 deletions src/cli/list_disputes.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use anyhow::Result;

use crate::cli::Context;
use crate::parser::common::{print_key_value, print_section_header};
use crate::parser::disputes::print_disputes_table;
use crate::util::{fetch_events_list, ListKind};

pub async fn execute_list_disputes(ctx: &Context) -> Result<()> {
// Print mostro pubkey
println!(
"Requesting disputes from mostro pubId - {}",
&ctx.mostro_pubkey
);
print_section_header("⚖️ List Disputes");
print_key_value("🎯", "Mostro PubKey", &ctx.mostro_pubkey.to_string());
print_key_value("💡", "Action", "Fetching disputes from relays...");
println!();

// Get orders from relays
let table_of_disputes =
Expand Down
Loading