mostro_client/cli/
send_msg.rs1use crate::cli::{Commands, Context};
2use crate::db::{Order, User};
3use crate::parser::common::{
4 create_emoji_field_row, create_field_value_header, create_standard_table,
5};
6use crate::parser::{dms::print_commands_results, parse_dm_events};
7use crate::util::{
8 create_filter, print_dm_events, send_dm, wait_for_dm, ListKind, FETCH_EVENTS_TIMEOUT,
9};
10
11use anyhow::Result;
12use mostro_core::prelude::*;
13use nostr_sdk::prelude::*;
14use uuid::Uuid;
15
16pub async fn execute_send_msg(
17 command: Commands,
18 order_id: Option<Uuid>,
19 ctx: &Context,
20 text: Option<&str>,
21) -> Result<()> {
22 let requested_action = match command {
24 Commands::FiatSent { .. } => Action::FiatSent,
25 Commands::Release { .. } => Action::Release,
26 Commands::Cancel { .. } => Action::Cancel,
27 Commands::Dispute { .. } => Action::Dispute,
28 Commands::AdmCancel { .. } => Action::AdminCancel,
29 Commands::AdmSettle { .. } => Action::AdminSettle,
30 Commands::AdmAddSolver { .. } => Action::AdminAddSolver,
31 _ => {
32 return Err(anyhow::anyhow!("Invalid command for send msg"));
33 }
34 };
35
36 println!("📤 Send Message Command");
38 println!("═══════════════════════════════════════");
39 let mut table = create_standard_table();
40 table.set_header(create_field_value_header());
41 table.add_row(create_emoji_field_row(
42 "🎯 ",
43 "Action",
44 &requested_action.to_string(),
45 ));
46 table.add_row(create_emoji_field_row(
47 "📋 ",
48 "Order ID",
49 &order_id.map_or_else(|| "N/A".to_string(), |id| id.to_string()),
50 ));
51 table.add_row(create_emoji_field_row(
52 "🎯 ",
53 "Target",
54 &ctx.mostro_pubkey.to_string(),
55 ));
56 println!("{table}");
57 println!("💡 Sending command to Mostro...\n");
58
59 let payload = match requested_action {
61 Action::FiatSent | Action::Release => create_next_trade_payload(ctx, &order_id).await?,
62 _ => text.map(|t| Payload::TextMessage(t.to_string())),
63 };
64 if let Some(Payload::NextTrade(_, trade_index)) = &payload {
66 match User::get(&ctx.pool).await {
68 Ok(mut user) => {
69 user.set_last_trade_index(*trade_index as i64);
70 if let Err(e) = user.save(&ctx.pool).await {
71 println!("Failed to update user: {}", e);
72 }
73 }
74 Err(e) => println!("Failed to get user: {}", e),
75 }
76 }
77
78 let request_id = Uuid::new_v4().as_u128() as u64;
80
81 let requested_action_clone = requested_action.clone();
83 let payload_clone = payload.clone();
84
85 let message = Message::new_order(order_id, Some(request_id), None, requested_action, payload);
87
88 if let Some(order_id) = order_id {
89 let order = Order::get_by_id(&ctx.pool, &order_id.to_string()).await?;
90
91 if let Some(trade_keys_str) = order.trade_keys.clone() {
92 let trade_keys = Keys::parse(&trade_keys_str)?;
93
94 let message_json = message
96 .as_json()
97 .map_err(|e| anyhow::anyhow!("Failed to serialize message: {e}"))?;
98
99 let sent_message = send_dm(
101 &ctx.client,
102 Some(&ctx.identity_keys),
103 &trade_keys,
104 &ctx.mostro_pubkey,
105 message_json,
106 None,
107 false,
108 );
109
110 let recv_event = wait_for_dm(ctx, Some(&trade_keys), sent_message).await?;
112
113 print_dm_events(recv_event, request_id, ctx, Some(&trade_keys)).await?;
115
116 if requested_action_clone == Action::Release {
118 if let Some(Payload::NextTrade(_, index)) = &payload_clone {
120 let next_trade_key = User::get_trade_keys(&ctx.pool, *index as i64).await?;
122 let subscription = create_filter(
124 ListKind::DirectMessagesUser,
125 next_trade_key.public_key,
126 None,
127 )?;
128
129 let events = ctx
131 .client
132 .fetch_events(subscription, FETCH_EVENTS_TIMEOUT)
133 .await?;
134 let messages = parse_dm_events(events, &next_trade_key, Some(&2)).await;
135 if !messages.is_empty() {
136 for (message, _, _) in messages {
137 let message_kind = message.get_inner_message_kind();
138 if message_kind.action == Action::NewOrder {
139 print_commands_results(message_kind, ctx).await?;
140 return Ok(());
141 }
142 }
143 }
144 }
145 }
146 }
147 }
148 Ok(())
149}
150
151async fn create_next_trade_payload(
152 ctx: &Context,
153 order_id: &Option<Uuid>,
154) -> Result<Option<Payload>> {
155 if let Some(order_id) = order_id {
156 let order = Order::get_by_id(&ctx.pool, &order_id.to_string()).await?;
157
158 if let (Some(_), Some(min_amount), Some(max_amount)) =
159 (order.is_mine, order.min_amount, order.max_amount)
160 {
161 if max_amount - order.fiat_amount >= min_amount {
162 let (trade_keys, trade_index) = User::get_next_trade_keys(&ctx.pool).await?;
163 return Ok(Some(Payload::NextTrade(
164 trade_keys.public_key().to_string(),
165 trade_index.try_into()?,
166 )));
167 }
168 }
169 }
170 Ok(None)
171}