fix: split fetch_events calls to comply with nostr-sdk 0.44 API
This commit is contained in:
@@ -12,64 +12,68 @@ pub struct Message {
|
|||||||
pub is_incoming: bool,
|
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<EventId> {
|
pub async fn send_dm(client: &Client, receiver: PublicKey, message: &str) -> Result<EventId> {
|
||||||
let output = client.send_private_msg(receiver, message, None).await?;
|
let output = client.send_private_msg(receiver, message, None).await?;
|
||||||
Ok(*output.id())
|
Ok(*output.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
// src/nips/nip17.rs
|
|
||||||
|
|
||||||
pub async fn get_dm_messages(client: &Client, contact_npub: &str) -> Result<Vec<Message>> {
|
pub async fn get_dm_messages(client: &Client, contact_npub: &str) -> Result<Vec<Message>> {
|
||||||
let signer = client.signer().await?;
|
let signer = client.signer().await?;
|
||||||
let my_pubkey = signer.get_public_key().await?;
|
let my_pubkey = signer.get_public_key().await?;
|
||||||
let contact_pubkey = PublicKey::parse(contact_npub)?;
|
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);
|
let search_start = Timestamp::now() - Duration::from_secs(60 * 60 * 24 * 365);
|
||||||
|
|
||||||
// STRATEGY: We only fetch events where WE are the recipient (#p tag).
|
// --- STEP 1: Define Filters ---
|
||||||
// In NIP-17, sent messages are only readable if we sent a copy to ourselves.
|
// Filter A: Events where I am the recipient (#p tag)
|
||||||
// In NIP-04, we can fetch messages we authored.
|
// This includes incoming messages and my own "self-DM" copies for history.
|
||||||
|
|
||||||
let filter_to_me = Filter::new()
|
let filter_to_me = Filter::new()
|
||||||
.kinds([Kind::GiftWrap, Kind::EncryptedDirectMessage])
|
.kinds([Kind::GiftWrap, Kind::EncryptedDirectMessage])
|
||||||
.pubkey(my_pubkey) // All GiftWraps sent TO me (includes my own copies)
|
.pubkey(my_pubkey)
|
||||||
.since(search_start);
|
.since(search_start);
|
||||||
|
|
||||||
let filter_from_me_legacy = Filter::new()
|
// Filter B: Events where I am the author
|
||||||
.kind(Kind::EncryptedDirectMessage)
|
// Necessary to find outgoing NIP-04 messages and certain NIP-17 relay storage patterns.
|
||||||
.author(my_pubkey) // Only for NIP-04 legacy (where author can decrypt)
|
let filter_from_me = Filter::new()
|
||||||
.pubkey(contact_pubkey)
|
.kinds([Kind::GiftWrap, Kind::EncryptedDirectMessage])
|
||||||
|
.author(my_pubkey)
|
||||||
.since(search_start);
|
.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))
|
.fetch_events(filter_to_me, Duration::from_secs(10))
|
||||||
.await?
|
.await?;
|
||||||
.to_vec();
|
|
||||||
let more_events = client
|
|
||||||
.fetch_events(filter_from_me_legacy, Duration::from_secs(10))
|
|
||||||
.await?
|
|
||||||
.to_vec();
|
|
||||||
events.extend(more_events);
|
|
||||||
|
|
||||||
|
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 seen_ids = HashSet::new();
|
||||||
let mut messages: Vec<Message> = Vec::new();
|
let mut messages: Vec<Message> = 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) {
|
if !seen_ids.insert(event.id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// === NIP-17 Processing ===
|
// === NIP-17 (Modern) Processing ===
|
||||||
if event.kind == Kind::GiftWrap {
|
if event.kind == Kind::GiftWrap {
|
||||||
if let Ok(unwrapped) = client.unwrap_gift_wrap(&event).await {
|
if let Ok(unwrapped) = client.unwrap_gift_wrap(&event).await {
|
||||||
let rumor = unwrapped.rumor;
|
let rumor = unwrapped.rumor;
|
||||||
|
let is_from_contact = rumor.pubkey == contact_pubkey;
|
||||||
|
|
||||||
// 1. INCOMING: Rumor author is contact
|
// Outgoing logic: Rumor is from me, and rumor p-tag is the contact
|
||||||
let is_incoming = rumor.pubkey == contact_pubkey;
|
let is_from_me = rumor.pubkey == my_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
|
|
||||||
&& rumor.tags.iter().any(|t| {
|
&& rumor.tags.iter().any(|t| {
|
||||||
if let Some(TagStandard::PublicKey { public_key, .. }) = t.as_standardized()
|
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<Vec<
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (is_incoming || is_outgoing)
|
if (is_from_contact || is_from_me)
|
||||||
&& (rumor.kind == Kind::from(14) || rumor.kind == Kind::TextNote)
|
&& (rumor.kind == Kind::from(14) || rumor.kind == Kind::TextNote)
|
||||||
{
|
{
|
||||||
messages.push(Message {
|
messages.push(Message {
|
||||||
@@ -87,15 +91,22 @@ pub async fn get_dm_messages(client: &Client, contact_npub: &str) -> Result<Vec<
|
|||||||
sender: rumor.pubkey,
|
sender: rumor.pubkey,
|
||||||
content: rumor.content.clone(),
|
content: rumor.content.clone(),
|
||||||
created_at: rumor.created_at,
|
created_at: rumor.created_at,
|
||||||
is_incoming,
|
is_incoming: is_from_contact,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// === NIP-04 Processing ===
|
// === NIP-04 (Legacy) Processing ===
|
||||||
else if event.kind == Kind::EncryptedDirectMessage {
|
else if event.kind == Kind::EncryptedDirectMessage {
|
||||||
let is_incoming = event.pubkey == contact_pubkey;
|
let is_incoming = event.pubkey == contact_pubkey;
|
||||||
let is_outgoing = event.pubkey == my_pubkey; // Already filtered by p-tag in the filter
|
let has_contact_tag = event.tags.iter().any(|t| {
|
||||||
|
if let Some(TagStandard::PublicKey { public_key, .. }) = t.as_standardized() {
|
||||||
|
public_key == &contact_pubkey
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let is_outgoing = event.pubkey == my_pubkey && has_contact_tag;
|
||||||
|
|
||||||
if is_incoming || is_outgoing {
|
if is_incoming || is_outgoing {
|
||||||
let decrypt_with = if is_incoming {
|
let decrypt_with = if is_incoming {
|
||||||
@@ -116,6 +127,7 @@ pub async fn get_dm_messages(client: &Client, contact_npub: &str) -> Result<Vec<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chronological sorting (oldest first)
|
||||||
messages.sort_by(|a, b| a.created_at.cmp(&b.created_at));
|
messages.sort_by(|a, b| a.created_at.cmp(&b.created_at));
|
||||||
Ok(messages)
|
Ok(messages)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user