mostro_client/cli/
send_msg.rs

1use crate::cli::{Commands, Context};
2use crate::db::{Order, User};
3use crate::util::{send_dm, wait_for_dm};
4
5use anyhow::Result;
6use mostro_core::prelude::*;
7use nostr_sdk::prelude::*;
8use uuid::Uuid;
9
10pub async fn execute_send_msg(
11    command: Commands,
12    order_id: Option<Uuid>,
13    ctx: &Context,
14    text: Option<&str>,
15) -> Result<()> {
16    // Map CLI command to action
17    let requested_action = match command {
18        Commands::FiatSent { .. } => Action::FiatSent,
19        Commands::Release { .. } => Action::Release,
20        Commands::Cancel { .. } => Action::Cancel,
21        Commands::Dispute { .. } => Action::Dispute,
22        Commands::AdmCancel { .. } => Action::AdminCancel,
23        Commands::AdmSettle { .. } => Action::AdminSettle,
24        Commands::AdmAddSolver { .. } => Action::AdminAddSolver,
25        _ => {
26            return Err(anyhow::anyhow!("Invalid command for send msg"));
27        }
28    };
29
30    println!(
31        "Sending {} command for order {:?} to mostro pubId {}",
32        requested_action,
33        order_id.as_ref(),
34        &ctx.mostro_pubkey
35    );
36
37    // Determine payload
38    let payload = match requested_action {
39        Action::FiatSent | Action::Release => create_next_trade_payload(ctx, &order_id).await?,
40        _ => text.map(|t| Payload::TextMessage(t.to_string())),
41    };
42    // Update last trade index if next trade payload
43    if let Some(Payload::NextTrade(_, trade_index)) = &payload {
44        // Update last trade index
45        match User::get(&ctx.pool).await {
46            Ok(mut user) => {
47                user.set_last_trade_index(*trade_index as i64);
48                if let Err(e) = user.save(&ctx.pool).await {
49                    println!("Failed to update user: {}", e);
50                }
51            }
52            Err(e) => println!("Failed to get user: {}", e),
53        }
54    }
55
56    // Create request id
57    let request_id = Uuid::new_v4().as_u128() as u64;
58
59    // Create and send the message
60    let message = Message::new_order(order_id, Some(request_id), None, requested_action, payload);
61    let idkey = ctx.identity_keys.to_owned();
62
63    if let Some(order_id) = order_id {
64        let order = Order::get_by_id(&ctx.pool, &order_id.to_string()).await?;
65
66        if let Some(trade_keys_str) = order.trade_keys.clone() {
67            let trade_keys = Keys::parse(&trade_keys_str)?;
68            // Subscribe to gift wrap events - ONLY NEW ONES WITH LIMIT 0
69            let subscription = Filter::new()
70                .pubkey(trade_keys.public_key())
71                .kind(nostr_sdk::Kind::GiftWrap)
72                .limit(0);
73
74            let opts =
75                SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::WaitForEvents(1));
76            // Subscribe to gift wrap events
77            ctx.client.subscribe(subscription, Some(opts)).await?;
78            // Send DM
79            let message_json = message
80                .as_json()
81                .map_err(|e| anyhow::anyhow!("Failed to serialize message: {e}"))?;
82            send_dm(
83                &ctx.client,
84                Some(&idkey),
85                &trade_keys,
86                &ctx.mostro_pubkey,
87                message_json,
88                None,
89                false,
90            )
91            .await
92            .map_err(|e| anyhow::anyhow!("Failed to send DM: {e}"))?;
93
94            // Wait for the DM to be sent from mostro
95            wait_for_dm(
96                &ctx.client,
97                &trade_keys,
98                request_id,
99                None,
100                Some(order),
101                &ctx.pool,
102            )
103            .await
104            .map_err(|e| anyhow::anyhow!("Failed to wait for DM: {e}"))?;
105        }
106    }
107
108    Ok(())
109}
110
111async fn create_next_trade_payload(
112    ctx: &Context,
113    order_id: &Option<Uuid>,
114) -> Result<Option<Payload>> {
115    if let Some(order_id) = order_id {
116        let order = Order::get_by_id(&ctx.pool, &order_id.to_string()).await?;
117
118        if let (Some(_), Some(min_amount), Some(max_amount)) =
119            (order.is_mine, order.min_amount, order.max_amount)
120        {
121            if max_amount - order.fiat_amount >= min_amount {
122                let (trade_keys, trade_index) = User::get_next_trade_keys(&ctx.pool).await?;
123                return Ok(Some(Payload::NextTrade(
124                    trade_keys.public_key().to_string(),
125                    trade_index.try_into()?,
126                )));
127            }
128        }
129    }
130    Ok(None)
131}