From aacbe8789d77e24afa73c139f73c5e983a9a822e Mon Sep 17 00:00:00 2001 From: Bytemalte Date: Fri, 16 Jan 2026 21:43:36 +0100 Subject: [PATCH] fix: split fetch_events calls to comply with nostr-sdk 0.44 API --- src/nips/nip17.rs | 74 +++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/src/nips/nip17.rs b/src/nips/nip17.rs index 6c77bb0..6d16180 100644 --- a/src/nips/nip17.rs +++ b/src/nips/nip17.rs @@ -12,64 +12,68 @@ pub struct Message { pub is_incoming: bool, } +/// Sends a DM. The SDK handles the NIP-17/NIP-04 logic internally. pub async fn send_dm(client: &Client, receiver: PublicKey, message: &str) -> Result { let output = client.send_private_msg(receiver, message, None).await?; Ok(*output.id()) } -// src/nips/nip17.rs - pub async fn get_dm_messages(client: &Client, contact_npub: &str) -> Result> { let signer = client.signer().await?; let my_pubkey = signer.get_public_key().await?; let contact_pubkey = PublicKey::parse(contact_npub)?; + // Look back 1 year to ensure we find all history let search_start = Timestamp::now() - Duration::from_secs(60 * 60 * 24 * 365); - // STRATEGY: We only fetch events where WE are the recipient (#p tag). - // In NIP-17, sent messages are only readable if we sent a copy to ourselves. - // In NIP-04, we can fetch messages we authored. - + // --- STEP 1: Define Filters --- + // Filter A: Events where I am the recipient (#p tag) + // This includes incoming messages and my own "self-DM" copies for history. let filter_to_me = Filter::new() .kinds([Kind::GiftWrap, Kind::EncryptedDirectMessage]) - .pubkey(my_pubkey) // All GiftWraps sent TO me (includes my own copies) + .pubkey(my_pubkey) .since(search_start); - let filter_from_me_legacy = Filter::new() - .kind(Kind::EncryptedDirectMessage) - .author(my_pubkey) // Only for NIP-04 legacy (where author can decrypt) - .pubkey(contact_pubkey) + // Filter B: Events where I am the author + // Necessary to find outgoing NIP-04 messages and certain NIP-17 relay storage patterns. + let filter_from_me = Filter::new() + .kinds([Kind::GiftWrap, Kind::EncryptedDirectMessage]) + .author(my_pubkey) .since(search_start); - let mut events = client + // --- STEP 2: Fetch Events Sequentially --- + // SDK 0.44.1 fetch_events only accepts a single Filter. + // We execute both requests and combine the results. + let events_to_me = client .fetch_events(filter_to_me, Duration::from_secs(10)) - .await? - .to_vec(); - let more_events = client - .fetch_events(filter_from_me_legacy, Duration::from_secs(10)) - .await? - .to_vec(); - events.extend(more_events); + .await?; + let events_from_me = client + .fetch_events(filter_from_me, Duration::from_secs(10)) + .await?; + + // Combine both result sets into a single vector + let mut all_events = events_to_me.to_vec(); + all_events.extend(events_from_me.to_vec()); + + // --- STEP 3: Deduplicate and Process --- let mut seen_ids = HashSet::new(); let mut messages: Vec = Vec::new(); - for event in events { + for event in all_events { + // Skip duplicates from multiple relays or overlapping filters if !seen_ids.insert(event.id) { continue; } - // === NIP-17 Processing === + // === NIP-17 (Modern) Processing === if event.kind == Kind::GiftWrap { if let Ok(unwrapped) = client.unwrap_gift_wrap(&event).await { let rumor = unwrapped.rumor; + let is_from_contact = rumor.pubkey == contact_pubkey; - // 1. INCOMING: Rumor author is contact - let is_incoming = rumor.pubkey == contact_pubkey; - - // 2. OUTGOING: Rumor is from ME, and it contains a tag pointing to the contact - // Most NIP-17 rumors have a 'p' tag inside the rumor to show who it's for. - let is_outgoing = rumor.pubkey == my_pubkey + // Outgoing logic: Rumor is from me, and rumor p-tag is the contact + let is_from_me = rumor.pubkey == my_pubkey && rumor.tags.iter().any(|t| { if let Some(TagStandard::PublicKey { public_key, .. }) = t.as_standardized() { @@ -79,7 +83,7 @@ pub async fn get_dm_messages(client: &Client, contact_npub: &str) -> Result Result Result