mostro_client/cli/
new_order.rs1use crate::cli::Context;
2use crate::parser::orders::print_order_preview;
3use crate::util::{send_dm, uppercase_first, wait_for_dm};
4use anyhow::Result;
5use mostro_core::prelude::*;
6use nostr_sdk::prelude::*;
7use std::collections::HashMap;
8use std::io::{stdin, stdout, BufRead, Write};
9use std::process;
10use std::str::FromStr;
11use uuid::Uuid;
12
13pub type FiatNames = HashMap<String, String>;
14
15#[allow(clippy::too_many_arguments)]
16pub async fn execute_new_order(
17 kind: &str,
18 fiat_code: &str,
19 fiat_amount: &(i64, Option<i64>),
20 amount: &i64,
21 payment_method: &str,
22 premium: &i64,
23 invoice: &Option<String>,
24 ctx: &Context,
25 expiration_days: &i64,
26) -> Result<()> {
27 let fiat_code = fiat_code.to_uppercase();
29 if *amount == 0 {
32 let api_req_string = "https://api.yadio.io/currencies".to_string();
34 let fiat_list_check = reqwest::get(api_req_string)
35 .await?
36 .json::<FiatNames>()
37 .await?
38 .contains_key(&fiat_code);
39 if !fiat_list_check {
40 println!("{} is not present in the fiat market, please specify an amount with -a flag to fix the rate", fiat_code);
41 process::exit(0);
42 }
43 }
44 let kind = uppercase_first(kind);
45 let kind_checked = mostro_core::order::Kind::from_str(&kind)
47 .map_err(|_| anyhow::anyhow!("Invalid order kind"))?;
48 let expires_at = match *expiration_days {
49 0 => None,
50 _ => {
51 let now = chrono::Utc::now();
52 let expires_at = now + chrono::Duration::days(*expiration_days);
53 Some(expires_at.timestamp())
54 }
55 };
56
57 let amt = if fiat_amount.1.is_some() {
61 (0, Some(fiat_amount.0), fiat_amount.1)
62 } else {
63 (fiat_amount.0, None, None)
64 };
65
66 let small_order = SmallOrder::new(
67 None,
68 Some(kind_checked),
69 Some(Status::Pending),
70 *amount,
71 fiat_code.clone(),
72 amt.1,
73 amt.2,
74 amt.0,
75 payment_method.to_owned(),
76 *premium,
77 None,
78 None,
79 invoice.as_ref().to_owned().cloned(),
80 Some(0),
81 expires_at,
82 );
83
84 let order_content = Payload::Order(small_order.clone());
86
87 let ord_preview = print_order_preview(order_content.clone())
89 .map_err(|e| anyhow::anyhow!("Failed to generate order preview: {}", e))?;
90 println!("{ord_preview}");
91 let mut user_input = String::new();
92 let _input = stdin();
93 print!("Check your order! Is it correct? (Y/n) > ");
94 stdout().flush()?;
95
96 let mut answer = stdin().lock();
97 answer.read_line(&mut user_input)?;
98
99 match user_input.to_lowercase().as_str().trim_end() {
100 "y" | "" => {}
101 "n" => {
102 println!("Ok you have cancelled the order, create another one please");
103 process::exit(0);
104 }
105 &_ => {
106 println!("Can't get what you're sayin!");
107 process::exit(0);
108 }
109 };
110 let request_id = Uuid::new_v4().as_u128() as u64;
111 let message = Message::new_order(
113 None,
114 Some(request_id),
115 Some(ctx.trade_index),
116 Action::NewOrder,
117 Some(order_content),
118 );
119
120 println!(
122 "SENDING DM with trade keys: {:?}",
123 ctx.trade_keys.public_key().to_hex()
124 );
125
126 let message_json = message
128 .as_json()
129 .map_err(|_| anyhow::anyhow!("Failed to serialize message"))?;
130
131 let identity_keys_clone = ctx.identity_keys.clone();
133 let trade_keys_clone = ctx.trade_keys.clone();
134 let client_clone = ctx.client.clone();
135 let mostro_pubkey_clone = ctx.mostro_pubkey;
136
137 let subscription = Filter::new()
139 .pubkey(ctx.trade_keys.public_key())
140 .kind(nostr_sdk::Kind::GiftWrap)
141 .limit(0);
142
143 let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::WaitForEvents(1));
144
145 ctx.client.subscribe(subscription, Some(opts)).await?;
146
147 tokio::spawn(async move {
150 let _ = send_dm(
151 &client_clone,
152 Some(&identity_keys_clone),
153 &trade_keys_clone,
154 &mostro_pubkey_clone,
155 message_json,
156 None,
157 false,
158 )
159 .await;
160 });
161
162 wait_for_dm(
164 &ctx.client,
165 &ctx.trade_keys,
166 request_id,
167 Some(ctx.trade_index),
168 None,
169 &ctx.pool,
170 )
171 .await?;
172
173 Ok(())
174}