1use crate::nip33::{dispute_from_tags, order_from_tags};
2
3use anyhow::{Error, Result};
4use base64::engine::general_purpose;
5use base64::Engine;
6use dotenvy::var;
7use log::{error, info};
8use mostro_core::prelude::*;
9use nip44::v2::{decrypt_to_bytes, encrypt_to_bytes, ConversationKey};
10use nostr_sdk::prelude::*;
11use std::thread::sleep;
12use std::time::Duration;
13use std::{fs, path::Path};
14
15async fn send_gift_wrap_dm_internal(
16 client: &Client,
17 sender_keys: &Keys,
18 receiver_pubkey: &PublicKey,
19 message: &str,
20 is_admin: bool,
21) -> Result<()> {
22 let pow: u8 = var("POW")
23 .unwrap_or_else(|_| "0".to_string())
24 .parse()
25 .unwrap_or(0);
26
27 let dm_message = Message::new_dm(
29 None,
30 None,
31 Action::SendDm,
32 Some(Payload::TextMessage(message.to_string())),
33 );
34
35 let content = serde_json::to_string(&(dm_message, None::<String>))?;
37
38 let rumor = EventBuilder::text_note(content)
40 .pow(pow)
41 .build(sender_keys.public_key());
42
43 let event = EventBuilder::gift_wrap(sender_keys, receiver_pubkey, rumor, Tags::new()).await?;
45
46 let sender_type = if is_admin { "admin" } else { "user" };
47 info!("Sending {} gift wrap event to {}", sender_type, receiver_pubkey);
48 client.send_event(&event).await?;
49
50 Ok(())
51}
52
53pub async fn send_admin_gift_wrap_dm(
54 client: &Client,
55 admin_keys: &Keys,
56 receiver_pubkey: &PublicKey,
57 message: &str,
58) -> Result<()> {
59 send_gift_wrap_dm_internal(client, admin_keys, receiver_pubkey, message, true).await
60}
61
62pub async fn send_gift_wrap_dm(
63 client: &Client,
64 trade_keys: &Keys,
65 receiver_pubkey: &PublicKey,
66 message: &str,
67) -> Result<()> {
68 send_gift_wrap_dm_internal(client, trade_keys, receiver_pubkey, message, false).await
69}
70
71pub async fn send_dm(
72 client: &Client,
73 identity_keys: Option<&Keys>,
74 trade_keys: &Keys,
75 receiver_pubkey: &PublicKey,
76 payload: String,
77 expiration: Option<Timestamp>,
78 to_user: bool,
79) -> Result<()> {
80 let pow: u8 = var("POW").unwrap_or('0'.to_string()).parse().unwrap();
81 let private = var("SECRET")
82 .unwrap_or("false".to_string())
83 .parse::<bool>()
84 .unwrap();
85 let event = if to_user {
86 let ck = ConversationKey::derive(trade_keys.secret_key(), receiver_pubkey)?;
88 let encrypted_content = encrypt_to_bytes(&ck, payload.as_bytes())?;
90 let b64decoded_content = general_purpose::STANDARD.encode(encrypted_content);
92 EventBuilder::new(nostr_sdk::Kind::PrivateDirectMessage, b64decoded_content)
94 .pow(pow)
95 .tag(Tag::public_key(*receiver_pubkey))
96 .sign_with_keys(trade_keys)?
97 } else if private {
98 let message = Message::from_json(&payload).unwrap();
99 let content: (Message, Option<Signature>) = (message, None);
101 let content = serde_json::to_string(&content).unwrap();
102 let rumor = EventBuilder::text_note(content)
104 .pow(pow)
105 .build(trade_keys.public_key());
106 let mut tags: Vec<Tag> = Vec::with_capacity(1 + usize::from(expiration.is_some()));
107
108 if let Some(timestamp) = expiration {
109 tags.push(Tag::expiration(timestamp));
110 }
111 let tags = Tags::from_list(tags);
112
113 EventBuilder::gift_wrap(trade_keys, receiver_pubkey, rumor, tags).await?
114 } else {
115 let identity_keys = identity_keys
116 .ok_or_else(|| Error::msg("identity_keys required when to_user is false"))?;
117 let message = Message::from_json(&payload).unwrap();
119 let sig = Message::sign(payload.clone(), trade_keys);
120 let content = serde_json::to_string(&(message, sig)).unwrap();
122 let rumor = EventBuilder::text_note(content)
124 .pow(pow)
125 .build(trade_keys.public_key());
126 let mut tags: Vec<Tag> = Vec::with_capacity(1 + usize::from(expiration.is_some()));
127
128 if let Some(timestamp) = expiration {
129 tags.push(Tag::expiration(timestamp));
130 }
131 let tags = Tags::from_list(tags);
132
133 EventBuilder::gift_wrap(identity_keys, receiver_pubkey, rumor, tags).await?
134 };
135
136 info!("Sending event: {event:#?}");
137 client.send_event(&event).await?;
138
139 Ok(())
140}
141
142pub async fn connect_nostr() -> Result<Client> {
143 let my_keys = Keys::generate();
144
145 let relays = var("RELAYS").expect("RELAYS is not set");
146 let relays = relays.split(',').collect::<Vec<&str>>();
147 let client = Client::new(my_keys);
149 for r in relays.into_iter() {
151 client.add_relay(r).await?;
152 }
153 client.connect().await;
155
156 Ok(client)
157}
158
159pub async fn send_message_sync(
160 client: &Client,
161 identity_keys: Option<&Keys>,
162 trade_keys: &Keys,
163 receiver_pubkey: PublicKey,
164 message: Message,
165 wait_for_dm: bool,
166 to_user: bool,
167) -> Result<Vec<(Message, u64)>> {
168 let message_json = message
169 .as_json()
170 .map_err(|_| Error::msg("Failed to serialize message"))?;
171 println!(
173 "SENDING DM with trade keys: {:?}",
174 trade_keys.public_key().to_hex()
175 );
176 send_dm(
177 client,
178 identity_keys,
179 trade_keys,
180 &receiver_pubkey,
181 message_json,
182 None,
183 to_user,
184 )
185 .await?;
186 sleep(Duration::from_secs(2));
188
189 let dm: Vec<(Message, u64)> = if wait_for_dm {
190 get_direct_messages(client, trade_keys, 15, to_user, None).await
191 } else {
192 Vec::new()
193 };
194
195 Ok(dm)
196}
197
198pub async fn get_direct_messages_from_trade_keys(
199 client: &Client,
200 trade_keys_hex: Vec<String>,
201 since: i64,
202 mostro_pubkey: &PublicKey,
203) -> Vec<(Message, u64, PublicKey)> {
204 if trade_keys_hex.is_empty() {
205 return Vec::new();
206 }
207
208 let fake_since = 2880;
209 let fake_since_time = chrono::Utc::now()
210 .checked_sub_signed(chrono::Duration::minutes(fake_since))
211 .unwrap()
212 .timestamp() as u64;
213 let fake_timestamp = Timestamp::from(fake_since_time);
214
215 let since_time = chrono::Utc::now()
216 .checked_sub_signed(chrono::Duration::minutes(since))
217 .unwrap()
218 .timestamp() as u64;
219
220 let mut all_direct_messages: Vec<(Message, u64, PublicKey)> = Vec::new();
221 let mut id_set = std::collections::HashSet::<EventId>::new();
222
223 for trade_key_hex in trade_keys_hex {
224 if let Ok(trade_keys) = Keys::parse(&trade_key_hex) {
225 let filters = Filter::new()
226 .kind(nostr_sdk::Kind::GiftWrap)
227 .pubkey(trade_keys.public_key())
228 .since(fake_timestamp);
229
230 info!("Request events with event kind : {:?} for trade key: {}",
231 filters.kinds, trade_keys.public_key());
232
233 if let Ok(events) = client.fetch_events(filters, Duration::from_secs(15)).await {
234 for dm in events.iter() {
235 if !id_set.insert(dm.id) {
236 continue; }
238
239 let unwrapped_gift = match nip59::extract_rumor(&trade_keys, dm).await {
240 Ok(u) => u,
241 Err(_) => {
242 error!("Error unwrapping gift for trade key: {}", trade_keys.public_key());
243 continue;
244 }
245 };
246
247 if unwrapped_gift.rumor.pubkey == *mostro_pubkey {
249 continue; }
251
252 if unwrapped_gift.rumor.created_at.as_u64() < since_time {
253 continue;
254 }
255
256 let (message, _): (Message, Option<String>) = match serde_json::from_str(&unwrapped_gift.rumor.content) {
258 Ok(parsed) => parsed,
259 Err(_) => {
260 error!("Error parsing JSON content from: {}", unwrapped_gift.rumor.pubkey);
261 continue;
262 }
263 };
264
265 all_direct_messages.push((
266 message,
267 unwrapped_gift.rumor.created_at.as_u64(),
268 unwrapped_gift.rumor.pubkey
269 ));
270 }
271 }
272 } else {
273 error!("Failed to parse trade key: {}", trade_key_hex);
274 }
275 }
276
277 all_direct_messages.sort_by(|a, b| a.1.cmp(&b.1));
278 all_direct_messages
279}
280
281pub async fn get_direct_messages(
282 client: &Client,
283 my_key: &Keys,
284 since: i64,
285 from_user: bool,
286 mostro_pubkey: Option<&PublicKey>,
287) -> Vec<(Message, u64)> {
288 let fake_since = 2880;
290 let fake_since_time = chrono::Utc::now()
291 .checked_sub_signed(chrono::Duration::minutes(fake_since))
292 .unwrap()
293 .timestamp() as u64;
294
295 let fake_timestamp = Timestamp::from(fake_since_time);
296 let filters = if from_user {
297 let since_time = chrono::Utc::now()
298 .checked_sub_signed(chrono::Duration::minutes(since))
299 .unwrap()
300 .timestamp() as u64;
301 let timestamp = Timestamp::from(since_time);
302 Filter::new()
303 .kind(nostr_sdk::Kind::PrivateDirectMessage)
304 .pubkey(my_key.public_key())
305 .since(timestamp)
306 } else {
307 Filter::new()
308 .kind(nostr_sdk::Kind::GiftWrap)
309 .pubkey(my_key.public_key())
310 .since(fake_timestamp)
311 };
312
313 info!("Request events with event kind : {:?} ", filters.kinds);
314
315 let mut direct_messages: Vec<(Message, u64)> = Vec::new();
316
317 if let Ok(mostro_req) = client.fetch_events(filters, Duration::from_secs(15)).await {
318 let mut id_list = Vec::<EventId>::new();
321
322 for dm in mostro_req.iter() {
323 if !id_list.contains(&dm.id) {
324 id_list.push(dm.id);
325 let (created_at, message) = if from_user {
326 let ck =
327 if let Ok(ck) = ConversationKey::derive(my_key.secret_key(), &dm.pubkey) {
328 ck
329 } else {
330 continue;
331 };
332 let b64decoded_content =
333 match general_purpose::STANDARD.decode(dm.content.as_bytes()) {
334 Ok(b64decoded_content) => b64decoded_content,
335 Err(_) => {
336 continue;
337 }
338 };
339
340 let unencrypted_content = decrypt_to_bytes(&ck, &b64decoded_content)
341 .expect("Failed to decrypt message");
342
343 let message =
344 String::from_utf8(unencrypted_content).expect("Found invalid UTF-8");
345 let message = Message::from_json(&message).expect("Failed on deserializing");
346
347 (dm.created_at, message)
348 } else {
349 let unwrapped_gift = match nip59::extract_rumor(my_key, dm).await {
350 Ok(u) => u,
351 Err(_) => {
352 println!("Error unwrapping gift");
353 continue;
354 }
355 };
356
357 if let Some(mostro_pk) = mostro_pubkey {
359 if unwrapped_gift.rumor.pubkey != *mostro_pk {
360 continue; }
362 }
363
364 let (message, _): (Message, Option<String>) =
365 serde_json::from_str(&unwrapped_gift.rumor.content).unwrap();
366
367 (unwrapped_gift.rumor.created_at, message)
368 };
369
370 let since_time = chrono::Utc::now()
372 .checked_sub_signed(chrono::Duration::minutes(30))
373 .unwrap()
374 .timestamp() as u64;
375 if created_at.as_u64() < since_time {
376 continue;
377 }
378 direct_messages.push((message, created_at.as_u64()));
379 }
380 }
381 direct_messages.sort_by(|a, b| a.1.cmp(&b.1));
383 }
384
385 direct_messages
386}
387
388pub async fn get_orders_list(
389 pubkey: PublicKey,
390 status: Status,
391 currency: Option<String>,
392 kind: Option<mostro_core::order::Kind>,
393 client: &Client,
394) -> Result<Vec<SmallOrder>> {
395 let since_time = chrono::Utc::now()
396 .checked_sub_signed(chrono::Duration::days(7))
397 .unwrap()
398 .timestamp() as u64;
399
400 let timestamp = Timestamp::from(since_time);
401
402 let filters = Filter::new()
403 .author(pubkey)
404 .limit(50)
405 .since(timestamp)
406 .custom_tag(SingleLetterTag::lowercase(Alphabet::Z), "order".to_string())
407 .kind(nostr_sdk::Kind::Custom(NOSTR_REPLACEABLE_EVENT_KIND));
408
409 info!(
410 "Request to mostro id : {:?} with event kind : {:?} ",
411 filters.authors, filters.kinds
412 );
413
414 let mut complete_events_list = Vec::<SmallOrder>::new();
416 let mut requested_orders_list = Vec::<SmallOrder>::new();
417
418 if let Ok(mostro_req) = client.fetch_events(filters, Duration::from_secs(15)).await {
420 for el in mostro_req.iter() {
422 let order = order_from_tags(el.tags.clone());
423
424 if order.is_err() {
425 error!("{order:?}");
426 continue;
427 }
428 let mut order = order?;
429
430 info!("Found Order id : {:?}", order.id.unwrap());
431
432 if order.id.is_none() {
433 info!("Order ID is none");
434 continue;
435 }
436
437 if order.kind.is_none() {
438 info!("Order kind is none");
439 continue;
440 }
441
442 if order.status.is_none() {
443 info!("Order status is none");
444 continue;
445 }
446
447 order.created_at = Some(el.created_at.as_u64() as i64);
449
450 complete_events_list.push(order.clone());
451
452 if order.status.ne(&Some(status)) {
453 continue;
454 }
455
456 if currency.is_some() && order.fiat_code.ne(¤cy.clone().unwrap()) {
457 continue;
458 }
459
460 if kind.is_some() && order.kind.ne(&kind) {
461 continue;
462 }
463 requested_orders_list.push(order);
465 }
466 }
467
468 requested_orders_list.retain(|keep| {
471 !complete_events_list
472 .iter()
473 .any(|x| x.id == keep.id && x.created_at > keep.created_at)
474 });
475 requested_orders_list.sort_by(|a, b| b.id.cmp(&a.id));
477 requested_orders_list.dedup_by(|a, b| a.id == b.id);
478
479 requested_orders_list.sort_by(|a, b| b.created_at.cmp(&a.created_at));
481
482 Ok(requested_orders_list)
483}
484
485pub async fn get_disputes_list(pubkey: PublicKey, client: &Client) -> Result<Vec<Dispute>> {
486 let since_time = chrono::Utc::now()
487 .checked_sub_signed(chrono::Duration::days(7))
488 .unwrap()
489 .timestamp() as u64;
490
491 let timestamp = Timestamp::from(since_time);
492
493 let filter = Filter::new()
494 .author(pubkey)
495 .limit(50)
496 .since(timestamp)
497 .custom_tag(
498 SingleLetterTag::lowercase(Alphabet::Z),
499 "dispute".to_string(),
500 )
501 .kind(nostr_sdk::Kind::Custom(NOSTR_REPLACEABLE_EVENT_KIND));
502
503 let mut disputes_list = Vec::<Dispute>::new();
505
506 if let Ok(mostro_req) = client.fetch_events(filter, Duration::from_secs(15)).await {
508 for d in mostro_req.iter() {
510 let dispute = dispute_from_tags(d.tags.clone());
511
512 if dispute.is_err() {
513 error!("{dispute:?}");
514 continue;
515 }
516 let mut dispute = dispute?;
517
518 info!("Found Dispute id : {:?}", dispute.id);
519
520 dispute.created_at = d.created_at.as_u64() as i64;
522 disputes_list.push(dispute);
523 }
524 }
525
526 let buffer_dispute_list = disputes_list.clone();
527 disputes_list.retain(|keep| {
530 !buffer_dispute_list
531 .iter()
532 .any(|x| x.id == keep.id && x.created_at > keep.created_at)
533 });
534
535 disputes_list.sort_by(|a, b| b.id.cmp(&a.id));
537 disputes_list.dedup_by(|a, b| a.id == b.id);
538
539 disputes_list.sort_by(|a, b| b.created_at.cmp(&a.created_at));
541
542 Ok(disputes_list)
543}
544
545pub fn uppercase_first(s: &str) -> String {
547 let mut c = s.chars();
548 match c.next() {
549 None => String::new(),
550 Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
551 }
552}
553
554pub fn get_mcli_path() -> String {
555 let home_dir = dirs::home_dir().expect("Couldn't get home directory");
556 let mcli_path = format!("{}/.mcli", home_dir.display());
557 if !Path::new(&mcli_path).exists() {
558 fs::create_dir(&mcli_path).expect("Couldn't create mostro-cli directory in HOME");
559 println!("Directory {} created.", mcli_path);
560 }
561
562 mcli_path
563}