Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
65 changes: 55 additions & 10 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
pub mod add_invoice;
pub mod adm_send_dm;
pub mod conversation_key;
pub mod dm_to_user;
pub mod get_dm;
pub mod get_dm_user;
pub mod list_disputes;
pub mod list_orders;
pub mod new_order;
Expand All @@ -13,8 +16,11 @@ pub mod take_dispute;
pub mod take_sell;

use crate::cli::add_invoice::execute_add_invoice;
use crate::cli::adm_send_dm::execute_adm_send_dm;
use crate::cli::conversation_key::execute_conversation_key;
use crate::cli::dm_to_user::execute_dm_to_user;
use crate::cli::get_dm::execute_get_dm;
use crate::cli::get_dm_user::execute_get_dm_user;
use crate::cli::list_disputes::execute_list_disputes;
use crate::cli::list_orders::execute_list_orders;
use crate::cli::new_order::execute_new_order;
Expand Down Expand Up @@ -158,6 +164,13 @@ pub enum Commands {
#[arg(short)]
from_user: bool,
},
/// Get direct messages sent to any trade keys
GetDmUser {
/// Since time of the messages in minutes
#[arg(short, long)]
#[clap(default_value_t = 30)]
since: i64,
},
/// Get the latest direct messages for admin
GetAdminDm {
/// Since time of the messages in minutes
Expand All @@ -180,6 +193,18 @@ pub enum Commands {
#[arg(short, long)]
message: String,
},
/// Send gift wrapped direct message to a user
DmToUser {
/// Pubkey of the recipient
#[arg(short, long)]
pubkey: String,
/// Order id to get ephemeral keys
#[arg(short, long)]
order_id: Uuid,
/// Message to send
#[arg(short, long)]
message: String,
},
/// Send fiat sent message to confirm payment to other user
FiatSent {
/// Order id
Expand Down Expand Up @@ -241,6 +266,15 @@ pub enum Commands {
#[arg(short, long)]
dispute_id: Uuid,
},
/// Send gift wrapped direct message to a user (only admin)
AdmSendDm {
/// Pubkey of the recipient
#[arg(short, long)]
pubkey: String,
/// Message to send
#[arg(short, long)]
message: String,
},
/// Get the conversation key for direct messaging with a user
ConversationKey {
/// Pubkey of the counterpart
Expand Down Expand Up @@ -373,10 +407,13 @@ pub async fn run() -> Result<()> {
execute_add_invoice(order_id, invoice, &identity_keys, mostro_key, &client).await?
}
Commands::GetDm { since, from_user } => {
execute_get_dm(since, trade_index, &client, *from_user, false).await?
execute_get_dm(since, trade_index, &client, *from_user, false, &mostro_key).await?
}
Commands::GetDmUser { since } => {
execute_get_dm_user(since, &client, &mostro_key).await?
}
Commands::GetAdminDm { since, from_user } => {
execute_get_dm(since, trade_index, &client, *from_user, true).await?
execute_get_dm(since, trade_index, &client, *from_user, true, &mostro_key).await?
}
Commands::FiatSent { order_id }
| Commands::Release { order_id }
Expand All @@ -396,8 +433,7 @@ pub async fn run() -> Result<()> {
let id_key = match std::env::var("NSEC_PRIVKEY") {
Ok(id_key) => Keys::parse(&id_key)?,
Err(e) => {
println!("Failed to get mostro admin private key: {}", e);
std::process::exit(1);
anyhow::bail!("NSEC_PRIVKEY not set: {e}");
}
};
execute_admin_add_solver(npubkey, &id_key, &trade_keys, mostro_key, &client).await?
Expand Down Expand Up @@ -439,8 +475,7 @@ pub async fn run() -> Result<()> {
let id_key = match std::env::var("NSEC_PRIVKEY") {
Ok(id_key) => Keys::parse(&id_key)?,
Err(e) => {
println!("Failed to get mostro admin private key: {}", e);
std::process::exit(1);
anyhow::bail!("NSEC_PRIVKEY not set: {e}");
}
};
execute_admin_settle_dispute(order_id, &id_key, &trade_keys, mostro_key, &client)
Expand All @@ -450,8 +485,7 @@ pub async fn run() -> Result<()> {
let id_key = match std::env::var("NSEC_PRIVKEY") {
Ok(id_key) => Keys::parse(&id_key)?,
Err(e) => {
println!("Failed to get mostro admin private key: {}", e);
std::process::exit(1);
anyhow::bail!("NSEC_PRIVKEY not set: {e}");
}
};
execute_admin_cancel_dispute(order_id, &id_key, &trade_keys, mostro_key, &client)
Expand All @@ -461,8 +495,7 @@ pub async fn run() -> Result<()> {
let id_key = match std::env::var("NSEC_PRIVKEY") {
Ok(id_key) => Keys::parse(&id_key)?,
Err(e) => {
println!("Failed to get mostro admin private key: {}", e);
std::process::exit(1);
anyhow::bail!("NSEC_PRIVKEY not set: {e}");
}
};

Expand All @@ -477,6 +510,18 @@ pub async fn run() -> Result<()> {
let pubkey = PublicKey::from_str(pubkey)?;
execute_send_dm(pubkey, &client, order_id, message).await?
}
Commands::DmToUser {
pubkey,
order_id,
message,
} => {
let pubkey = PublicKey::from_str(pubkey)?;
execute_dm_to_user(pubkey, &client, order_id, message).await?
}
Commands::AdmSendDm { pubkey, message } => {
let pubkey = PublicKey::from_str(pubkey)?;
execute_adm_send_dm(pubkey, &client, message).await?
}
};
}

Expand Down
24 changes: 24 additions & 0 deletions src/cli/adm_send_dm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::util::send_admin_gift_wrap_dm;
use anyhow::Result;
use nostr_sdk::prelude::*;

pub async fn execute_adm_send_dm(
receiver: PublicKey,
client: &Client,
message: &str,
) -> Result<()> {
let admin_keys = match std::env::var("NSEC_PRIVKEY") {
Ok(key) => Keys::parse(&key)?,
Err(e) => {
anyhow::bail!("NSEC_PRIVKEY not set: {e}");
}
};

println!("SENDING DM with admin keys: {}", admin_keys.public_key().to_hex());

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

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

Ok(())
}
27 changes: 27 additions & 0 deletions src/cli/dm_to_user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::{db::Order, util::send_gift_wrap_dm};
use anyhow::Result;
use nostr_sdk::prelude::*;
use uuid::Uuid;

pub async fn execute_dm_to_user(
receiver: PublicKey,
client: &Client,
order_id: &Uuid,
message: &str,
) -> Result<()> {
let pool = crate::db::connect().await?;

let order = Order::get_by_id(&pool, &order_id.to_string())
.await
.map_err(|_| anyhow::anyhow!("order {} not found", order_id))?;
let trade_keys = match order.trade_keys.as_ref() {
Some(trade_keys) => Keys::parse(trade_keys)?,
None => anyhow::bail!("No trade_keys found for this order"),
};

println!("SENDING DM with trade keys: {}", trade_keys.public_key().to_hex());

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

Ok(())
}
5 changes: 3 additions & 2 deletions src/cli/get_dm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ pub async fn execute_get_dm(
client: &Client,
from_user: bool,
admin: bool,
mostro_pubkey: &PublicKey,
) -> Result<()> {
let mut dm: Vec<(Message, u64)> = Vec::new();
let pool = connect().await?;
if !admin {
for index in 1..=trade_index {
let keys = User::get_trade_keys(&pool, index).await?;
let dm_temp = get_direct_messages(client, &keys, *since, from_user).await;
let dm_temp = get_direct_messages(client, &keys, *since, from_user, Some(mostro_pubkey)).await;
dm.extend(dm_temp);
}
} else {
Expand All @@ -31,7 +32,7 @@ pub async fn execute_get_dm(
std::process::exit(1);
}
};
let dm_temp = get_direct_messages(client, &id_key, *since, from_user).await;
let dm_temp = get_direct_messages(client, &id_key, *since, from_user, Some(mostro_pubkey)).await;
dm.extend(dm_temp);
}

Expand Down
62 changes: 62 additions & 0 deletions src/cli/get_dm_user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::{db::Order, util::get_direct_messages_from_trade_keys};
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::*;

pub async fn execute_get_dm_user(since: &i64, client: &Client, mostro_pubkey: &PublicKey) -> Result<()> {
let pool = crate::db::connect().await?;

// Get all trade keys from orders
let mut trade_keys_hex = Order::get_all_trade_keys(&pool).await?;

// Add admin private key to search for messages sent TO admin
if let Ok(admin_privkey_hex) = std::env::var("NSEC_PRIVKEY") {
trade_keys_hex.push(admin_privkey_hex);
}

if trade_keys_hex.is_empty() {
println!("No trade keys found in orders and NSEC_PRIVKEY not set");
return Ok(());
}

println!("Searching for DMs in {} trade keys...", trade_keys_hex.len());

let direct_messages = get_direct_messages_from_trade_keys(client, trade_keys_hex, *since, mostro_pubkey).await;

if direct_messages.is_empty() {
println!("You don't have any direct messages in your trade keys");
return Ok(());
}

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 direct_messages.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!();
Ok(())
}
20 changes: 20 additions & 0 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,26 @@ impl Order {
Ok(orders)
}

pub async fn get_all_trade_keys(pool: &SqlitePool) -> Result<Vec<String>> {
#[derive(sqlx::FromRow)]
struct TradeKeyRow {
trade_keys: Option<String>,
}

let rows = sqlx::query_as::<_, TradeKeyRow>(
"SELECT DISTINCT trade_keys FROM orders WHERE trade_keys IS NOT NULL"
)
.fetch_all(pool)
.await?;

let trade_keys: Vec<String> = rows
.into_iter()
.filter_map(|row| row.trade_keys)
.collect();

Ok(trade_keys)
}

pub async fn delete_by_id(pool: &SqlitePool, id: &str) -> Result<bool> {
let rows_affected = sqlx::query(
r#"
Expand Down
Loading