mostro_client/cli/
send_msg.rs

1use crate::db::{Order, User};
2use crate::util::send_message_sync;
3use crate::{cli::Commands, db::connect};
4
5use anyhow::Result;
6use mostro_core::prelude::*;
7use nostr_sdk::prelude::*;
8use sqlx::SqlitePool;
9use std::process;
10use uuid::Uuid;
11
12pub async fn execute_send_msg(
13    command: Commands,
14    order_id: Option<Uuid>,
15    identity_keys: Option<&Keys>,
16    mostro_key: PublicKey,
17    client: &Client,
18    text: Option<&str>,
19) -> Result<()> {
20    // Map CLI command to action
21    let requested_action = match command {
22        Commands::FiatSent { .. } => Action::FiatSent,
23        Commands::Release { .. } => Action::Release,
24        Commands::Cancel { .. } => Action::Cancel,
25        Commands::Dispute { .. } => Action::Dispute,
26        Commands::AdmCancel { .. } => Action::AdminCancel,
27        Commands::AdmSettle { .. } => Action::AdminSettle,
28        Commands::AdmAddSolver { .. } => Action::AdminAddSolver,
29        _ => {
30            eprintln!("Not a valid command!");
31            process::exit(0);
32        }
33    };
34
35    println!(
36        "Sending {} command for order {:?} to mostro pubId {}",
37        requested_action, order_id, mostro_key
38    );
39
40    let pool = connect().await?;
41
42    // Determine payload
43    let payload = match requested_action {
44        Action::FiatSent | Action::Release => create_next_trade_payload(&pool, &order_id).await?,
45        _ => text.map(|t| Payload::TextMessage(t.to_string())),
46    };
47    // Update last trade index if next trade payload
48    if let Some(Payload::NextTrade(_, trade_index)) = &payload {
49        // Update last trade index
50        match User::get(&pool).await {
51            Ok(mut user) => {
52                user.set_last_trade_index(*trade_index as i64);
53                if let Err(e) = user.save(&pool).await {
54                    println!("Failed to update user: {}", e);
55                }
56            }
57            Err(e) => println!("Failed to get user: {}", e),
58        }
59    }
60
61    let request_id = Uuid::new_v4().as_u128() as u64;
62
63    // Create and send the message
64    let message = Message::new_order(order_id, Some(request_id), None, requested_action, payload);
65    // println!("Sending message: {:#?}", message);
66
67    if let Some(order_id) = order_id {
68        handle_order_response(
69            &pool,
70            client,
71            identity_keys,
72            mostro_key,
73            message,
74            order_id,
75            request_id,
76        )
77        .await?;
78    } else {
79        println!("Error: Missing order ID");
80    }
81
82    Ok(())
83}
84
85async fn create_next_trade_payload(
86    pool: &SqlitePool,
87    order_id: &Option<Uuid>,
88) -> Result<Option<Payload>> {
89    if let Some(order_id) = order_id {
90        let order = Order::get_by_id(pool, &order_id.to_string()).await?;
91
92        if let (Some(_), Some(min_amount), Some(max_amount)) =
93            (order.is_mine, order.min_amount, order.max_amount)
94        {
95            if max_amount - order.fiat_amount >= min_amount {
96                let (trade_keys, trade_index) = User::get_next_trade_keys(pool).await?;
97                return Ok(Some(Payload::NextTrade(
98                    trade_keys.public_key().to_string(),
99                    trade_index.try_into()?,
100                )));
101            }
102        }
103    }
104    Ok(None)
105}
106
107async fn handle_order_response(
108    pool: &SqlitePool,
109    client: &Client,
110    identity_keys: Option<&Keys>,
111    mostro_key: PublicKey,
112    message: Message,
113    order_id: Uuid,
114    request_id: u64,
115) -> Result<()> {
116    let order = Order::get_by_id(pool, &order_id.to_string()).await;
117
118    match order {
119        Ok(order) => {
120            if let Some(trade_keys_str) = order.trade_keys {
121                let trade_keys = Keys::parse(&trade_keys_str)?;
122                let dm = send_message_sync(
123                    client,
124                    identity_keys,
125                    &trade_keys,
126                    mostro_key,
127                    message,
128                    true,
129                    false,
130                )
131                .await?;
132                process_order_response(dm, pool, &trade_keys, request_id).await?;
133            } else {
134                println!("Error: Missing trade keys for order {}", order_id);
135            }
136        }
137        Err(e) => {
138            println!("Error: {}", e);
139        }
140    }
141
142    Ok(())
143}
144
145async fn process_order_response(
146    dm: Vec<(Message, u64)>,
147    pool: &SqlitePool,
148    trade_keys: &Keys,
149    request_id: u64,
150) -> Result<()> {
151    for (message, _) in dm {
152        let kind = message.get_inner_message_kind();
153        if let Some(req_id) = kind.request_id {
154            if req_id != request_id {
155                continue;
156            }
157
158            match kind.action {
159                Action::NewOrder => {
160                    if let Some(Payload::Order(order)) = kind.payload.as_ref() {
161                        Order::new(pool, order.clone(), trade_keys, Some(request_id as i64))
162                            .await
163                            .map_err(|e| anyhow::anyhow!("Failed to create new order: {}", e))?;
164                        return Ok(());
165                    }
166                }
167                Action::Canceled => {
168                    if let Some(id) = kind.id {
169                        // Verify order exists before deletion
170                        if Order::get_by_id(pool, &id.to_string()).await.is_ok() {
171                            Order::delete_by_id(pool, &id.to_string())
172                                .await
173                                .map_err(|e| anyhow::anyhow!("Failed to delete order: {}", e))?;
174                            return Ok(());
175                        } else {
176                            return Err(anyhow::anyhow!("Order not found: {}", id));
177                        }
178                    }
179                }
180                _ => (),
181            }
182        }
183    }
184
185    Ok(())
186}