1use anyhow::{Error, Result};
2use base64::engine::general_purpose;
3use base64::Engine;
4use log::info;
5use mostro_core::prelude::*;
6use nip44::v2::{encrypt_to_bytes, ConversationKey};
7use nostr_sdk::prelude::*;
8use std::env::var;
9
10use crate::cli::Context;
11use crate::parser::dms::print_commands_results;
12use crate::parser::parse_dm_events;
13use crate::util::types::MessageType;
14
15pub fn get_admin_keys(ctx: &Context) -> Result<&Keys> {
17 let admin_keys = ctx.context_keys.as_ref().ok_or_else(|| {
18 anyhow::anyhow!("Admin keys not available. ADMIN_NSEC must be set for admin commands.")
19 })?;
20
21 if std::env::var("RUST_LOG").is_ok() {
23 println!("🔑 Admin Keys: {}", admin_keys.public_key);
24 }
25
26 Ok(admin_keys)
27}
28
29pub async fn send_admin_gift_wrap_dm(
30 client: &Client,
31 admin_keys: &Keys,
32 receiver_pubkey: &PublicKey,
33 message: &str,
34) -> Result<()> {
35 send_gift_wrap_dm_internal(client, admin_keys, receiver_pubkey, message, true).await
36}
37
38pub async fn send_gift_wrap_dm(
39 client: &Client,
40 trade_keys: &Keys,
41 receiver_pubkey: &PublicKey,
42 message: &str,
43) -> Result<()> {
44 send_gift_wrap_dm_internal(client, trade_keys, receiver_pubkey, message, false).await
45}
46
47async fn send_gift_wrap_dm_internal(
48 client: &Client,
49 sender_keys: &Keys,
50 receiver_pubkey: &PublicKey,
51 message: &str,
52 is_admin: bool,
53) -> Result<()> {
54 let pow: u8 = var("POW")
55 .unwrap_or_else(|_| "0".to_string())
56 .parse()
57 .unwrap_or(0);
58
59 let dm_message = Message::new_dm(
60 None,
61 None,
62 Action::SendDm,
63 Some(Payload::TextMessage(message.to_string())),
64 );
65
66 let content = serde_json::to_string(&(dm_message, None::<String>))?;
67
68 let rumor = EventBuilder::text_note(content)
69 .pow(pow)
70 .build(sender_keys.public_key());
71
72 let event = EventBuilder::gift_wrap(sender_keys, receiver_pubkey, rumor, Tags::new()).await?;
73
74 let sender_type = if is_admin { "admin" } else { "user" };
75 info!(
76 "Sending {} gift wrap event to {}",
77 sender_type, receiver_pubkey
78 );
79 client.send_event(&event).await?;
80
81 Ok(())
82}
83
84pub async fn wait_for_dm<F>(
85 ctx: &crate::cli::Context,
86 order_trade_keys: Option<&Keys>,
87 sent_message: F,
88) -> anyhow::Result<Events>
89where
90 F: std::future::Future<Output = Result<()>> + Send,
91{
92 let trade_keys = order_trade_keys.unwrap_or(&ctx.trade_keys);
93 let mut notifications = ctx.client.notifications();
94 let opts =
95 SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::WaitForEventsAfterEOSE(1));
96 let subscription = Filter::new()
97 .pubkey(trade_keys.public_key())
98 .kind(nostr_sdk::Kind::GiftWrap)
99 .limit(0);
100 ctx.client.subscribe(subscription, Some(opts)).await?;
101
102 sent_message.await?;
104
105 let event = tokio::time::timeout(super::events::FETCH_EVENTS_TIMEOUT, async move {
107 loop {
108 match notifications.recv().await {
109 Ok(notification) => match notification {
110 RelayPoolNotification::Event { event, .. } => {
111 return Ok(*event);
112 }
113 _ => continue,
114 },
115 Err(e) => {
116 return Err(anyhow::anyhow!("Error receiving notification: {:?}", e));
117 }
118 }
119 }
120 })
121 .await?
122 .map_err(|_| anyhow::anyhow!("Timeout waiting for DM or gift wrap event"))?;
123
124 let mut events = Events::default();
125 events.insert(event);
126 Ok(events)
127}
128
129fn determine_message_type(to_user: bool, private: bool) -> MessageType {
130 match (to_user, private) {
131 (true, _) => MessageType::PrivateDirectMessage,
132 (false, true) => MessageType::PrivateGiftWrap,
133 (false, false) => MessageType::SignedGiftWrap,
134 }
135}
136
137fn create_expiration_tags(expiration: Option<Timestamp>) -> Tags {
138 let mut tags: Vec<Tag> = Vec::with_capacity(1 + usize::from(expiration.is_some()));
139 if let Some(timestamp) = expiration {
140 tags.push(Tag::expiration(timestamp));
141 }
142 Tags::from_list(tags)
143}
144
145async fn create_private_dm_event(
146 trade_keys: &Keys,
147 receiver_pubkey: &PublicKey,
148 payload: String,
149 pow: u8,
150) -> Result<nostr_sdk::Event> {
151 let ck = ConversationKey::derive(trade_keys.secret_key(), receiver_pubkey)?;
152 let encrypted_content = encrypt_to_bytes(&ck, payload.as_bytes())?;
153 let b64decoded_content = general_purpose::STANDARD.encode(encrypted_content);
154 Ok(
155 EventBuilder::new(nostr_sdk::Kind::PrivateDirectMessage, b64decoded_content)
156 .pow(pow)
157 .tag(Tag::public_key(*receiver_pubkey))
158 .sign_with_keys(trade_keys)?,
159 )
160}
161
162async fn create_gift_wrap_event(
163 trade_keys: &Keys,
164 identity_keys: Option<&Keys>,
165 receiver_pubkey: &PublicKey,
166 payload: String,
167 pow: u8,
168 expiration: Option<Timestamp>,
169 signed: bool,
170) -> Result<nostr_sdk::Event> {
171 let message = Message::from_json(&payload)
172 .map_err(|e| anyhow::anyhow!("Failed to deserialize message: {e}"))?;
173
174 let content = if signed {
175 let _identity_keys = identity_keys
176 .ok_or_else(|| Error::msg("identity_keys required for signed messages"))?;
177 let sig = Message::sign(payload, trade_keys);
178 serde_json::to_string(&(message, sig))
179 .map_err(|e| anyhow::anyhow!("Failed to serialize message: {e}"))?
180 } else {
181 let content: (Message, Option<Signature>) = (message, None);
182 serde_json::to_string(&content)
183 .map_err(|e| anyhow::anyhow!("Failed to serialize message: {e}"))?
184 };
185
186 let rumor = EventBuilder::text_note(content)
187 .pow(pow)
188 .build(trade_keys.public_key());
189
190 let tags = create_expiration_tags(expiration);
191
192 let signer_keys = if signed {
193 identity_keys.ok_or_else(|| Error::msg("identity_keys required for signed messages"))?
194 } else {
195 trade_keys
196 };
197
198 Ok(EventBuilder::gift_wrap(signer_keys, receiver_pubkey, rumor, tags).await?)
199}
200
201pub async fn send_dm(
202 client: &Client,
203 identity_keys: Option<&Keys>,
204 trade_keys: &Keys,
205 receiver_pubkey: &PublicKey,
206 payload: String,
207 expiration: Option<Timestamp>,
208 to_user: bool,
209) -> Result<()> {
210 let pow: u8 = var("POW")
211 .unwrap_or('0'.to_string())
212 .parse()
213 .map_err(|e| anyhow::anyhow!("Failed to parse POW: {}", e))?;
214 let private = var("SECRET")
215 .unwrap_or("false".to_string())
216 .parse::<bool>()
217 .map_err(|e| anyhow::anyhow!("Failed to parse SECRET: {}", e))?;
218
219 let message_type = determine_message_type(to_user, private);
220
221 let event = match message_type {
222 MessageType::PrivateDirectMessage => {
223 create_private_dm_event(trade_keys, receiver_pubkey, payload, pow).await?
224 }
225 MessageType::PrivateGiftWrap => {
226 create_gift_wrap_event(
227 trade_keys,
228 identity_keys,
229 receiver_pubkey,
230 payload,
231 pow,
232 expiration,
233 false,
234 )
235 .await?
236 }
237 MessageType::SignedGiftWrap => {
238 create_gift_wrap_event(
239 trade_keys,
240 identity_keys,
241 receiver_pubkey,
242 payload,
243 pow,
244 expiration,
245 true,
246 )
247 .await?
248 }
249 };
250
251 client.send_event(&event).await?;
252 Ok(())
253}
254
255pub async fn print_dm_events(
256 recv_event: Events,
257 request_id: u64,
258 ctx: &crate::cli::Context,
259 order_trade_keys: Option<&Keys>,
260) -> Result<()> {
261 let trade_keys = order_trade_keys.unwrap_or(&ctx.trade_keys);
262 let messages = parse_dm_events(recv_event, trade_keys, None).await;
263 if let Some((message, _, _)) = messages.first() {
264 let message = message.get_inner_message_kind();
265 match message.request_id {
266 Some(id) => {
267 if request_id == id {
268 print_commands_results(message, ctx).await?;
269 }
270 }
271 None if message.action == Action::RateReceived
272 || message.action == Action::NewOrder =>
273 {
274 print_commands_results(message, ctx).await?;
275 }
276 None => {
277 return Err(anyhow::anyhow!(
278 "Received response with mismatched request_id. Expected: {}, Got: Null",
279 request_id,
280 ));
281 }
282 }
283 } else {
284 return Err(anyhow::anyhow!("No response received from Mostro"));
285 }
286 Ok(())
287}