diff --git a/src/lib/components/Notifications.svelte b/src/lib/components/Notifications.svelte index 136917e..8c6b992 100644 --- a/src/lib/components/Notifications.svelte +++ b/src/lib/components/Notifications.svelte @@ -460,8 +460,22 @@ try { isComposingMessage = true; - // Create p-tags for all recipients - const pTags = selectedRecipients.map(recipient => ["p", recipient.pubkey!]); + // Create p-tags for all recipients (ensure hex format) + const pTags = selectedRecipients.map(recipient => { + let pubkey = recipient.pubkey!; + // Convert npub to hex if needed + if (pubkey.startsWith('npub')) { + try { + const decoded = nip19.decode(pubkey); + if (decoded.type === 'npub') { + pubkey = decoded.data; + } + } catch (e) { + console.warn("[Send Message] Failed to decode npub:", pubkey, e); + } + } + return ["p", pubkey]; + }); // Add q tag if replying to a message (for jump-to functionality) if (replyToMessage) { @@ -470,8 +484,22 @@ pTags.push(["q", replyToMessage.id, relayUrl, replyToMessage.pubkey]); } - // Get all recipient pubkeys for relay calculation - const recipientPubkeys = selectedRecipients.map(r => r.pubkey!); + // Get all recipient pubkeys for relay calculation (ensure hex format) + const recipientPubkeys = selectedRecipients.map(r => { + let pubkey = r.pubkey!; + // Convert npub to hex if needed + if (pubkey.startsWith('npub')) { + try { + const decoded = nip19.decode(pubkey); + if (decoded.type === 'npub') { + pubkey = decoded.data; + } + } catch (e) { + console.warn("[Send Message Relay Calc] Failed to decode npub:", pubkey, e); + } + } + return pubkey; + }); // Calculate relay set using the same logic as kind24_utils const senderPubkey = $userStore.pubkey; @@ -786,8 +814,25 @@ // Calculate relay set when recipients change $effect(() => { const senderPubkey = $userStore.pubkey; + console.log("[Relay Effect] Recipients changed:", selectedRecipients.length, "Sender:", senderPubkey?.slice(0, 8)); + if (selectedRecipients.length > 0 && senderPubkey) { - const recipientPubkeys = selectedRecipients.map(r => r.pubkey!); + const recipientPubkeys = selectedRecipients.map(r => { + const pubkey = r.pubkey!; + // Convert npub to hex if needed + if (pubkey.startsWith('npub')) { + try { + const decoded = nip19.decode(pubkey); + if (decoded.type === 'npub') { + return decoded.data; + } + } catch (e) { + console.warn("[Relay Effect] Failed to decode npub:", pubkey, e); + } + } + return pubkey; + }); + console.log("[Relay Effect] Getting relay sets for recipients (hex):", recipientPubkeys.map(p => p.slice(0, 8))); // Get relay sets for all recipients and combine them const relaySetPromises = recipientPubkeys.map(recipientPubkey => @@ -795,15 +840,28 @@ ); Promise.all(relaySetPromises).then(relaySets => { + console.log("[Relay Effect] Received relay sets:", relaySets); // Combine and deduplicate all relay sets const allRelays = relaySets.flat(); const uniqueRelays = [...new Set(allRelays)]; - newMessageRelays = uniqueRelays; + console.log("[Relay Effect] Final relay list:", uniqueRelays); + + // If no relays found from NIP-65, use fallback relays + if (uniqueRelays.length === 0) { + console.log("[Relay Effect] No NIP-65 relays found, using fallback"); + const fallbackRelays = getAvailableRelays(); + newMessageRelays = fallbackRelays.slice(0, 5); // Limit to first 5 for performance + } else { + newMessageRelays = uniqueRelays; + } }).catch(error => { - console.error("Error getting relay set:", error); - newMessageRelays = []; + console.error("[Relay Effect] Error getting relay set:", error); + console.log("[Relay Effect] Using fallback relays due to error"); + const fallbackRelays = getAvailableRelays(); + newMessageRelays = fallbackRelays.slice(0, 5); }); } else { + console.log("[Relay Effect] Clearing relays - no recipients or sender"); newMessageRelays = []; } }); diff --git a/src/lib/utils/kind24_utils.ts b/src/lib/utils/kind24_utils.ts index edf362f..9d1271e 100644 --- a/src/lib/utils/kind24_utils.ts +++ b/src/lib/utils/kind24_utils.ts @@ -31,12 +31,11 @@ async function getUseroutboxRelays(ndk: NDK, user: NDKUser): Promise { if (tag[0] === 'r' && tag[1]) { // NIP-65: r tags with optional inbox/outbox markers const marker = tag[2]; - if (!marker || marker === 'outbox' || marker === 'inbox') { - // If no marker or marker is 'outbox', it's a outbox relay - // If marker is 'inbox', it's also a outbox relay (NIP-65 allows both) + if (!marker || marker === 'outbox' || marker === 'both') { + // If no marker, marker is 'outbox', or marker is 'both', it's an outbox relay outboxRelays.push(tag[1]); - } + // Note: inbox-only relays are NOT included in outbox relays } }); @@ -56,6 +55,7 @@ async function getUseroutboxRelays(ndk: NDK, user: NDKUser): Promise { */ async function getUserinboxRelays(ndk: NDK, user: NDKUser): Promise { try { + console.log(`[getUserinboxRelays] Fetching kind 10002 for user: ${user.pubkey.slice(0, 8)}`); const relayList = await ndk.fetchEvent( { @@ -65,27 +65,31 @@ async function getUserinboxRelays(ndk: NDK, user: NDKUser): Promise { ); if (!relayList) { + console.log(`[getUserinboxRelays] No kind 10002 relay list found for user: ${user.pubkey.slice(0, 8)}`); return []; } + console.log(`[getUserinboxRelays] Found relay list for user: ${user.pubkey.slice(0, 8)}, tags:`, relayList.tags); + const inboxRelays: string[] = []; relayList.tags.forEach((tag) => { if (tag[0] === 'r' && tag[1]) { // NIP-65: r tags with optional inbox/outbox markers const marker = tag[2]; - if (!marker || marker === 'inbox' || marker === 'outbox') { - // If no marker or marker is 'inbox', it's a inbox relay - // If marker is 'outbox', it's also a inbox relay (NIP-65 allows both) + console.log(`[getUserinboxRelays] Processing relay tag:`, tag, `marker: ${marker}`); + if (!marker || marker === 'inbox' || marker === 'both') { + // If no marker, marker is 'inbox', or marker is 'both', it's an inbox relay inboxRelays.push(tag[1]); - + console.log(`[getUserinboxRelays] Added inbox relay: ${tag[1]} (marker: ${marker || 'none'})`); } + // Note: outbox-only relays are NOT included in inbox relays } }); - + console.log(`[getUserinboxRelays] Final inbox relays for user ${user.pubkey.slice(0, 8)}:`, inboxRelays); return inboxRelays; } catch (error) { - + console.error(`[getUserinboxRelays] Error fetching inbox relays for user ${user.pubkey.slice(0, 8)}:`, error); return []; } } @@ -117,7 +121,13 @@ export async function createKind24Reply( // Get recipient's inbox relays (NIP-65) const recipientUser = ndk.getUser({ pubkey: recipientPubkey }); - const recipientinboxRelays = await getUserinboxRelays(ndk, recipientUser); + let recipientinboxRelays = await getUserinboxRelays(ndk, recipientUser); + + // Fallback: if no inbox relays found, use recipient's outbox relays + if (recipientinboxRelays.length === 0) { + console.log(`[createKind24Reply] No inbox relays found for recipient, falling back to outbox relays`); + recipientinboxRelays = await getUseroutboxRelays(ndk, recipientUser); + } // According to NIP-A4: Messages MUST be sent to the NIP-65 inbox relays of each receiver // and the outbox relay of the sender @@ -212,17 +222,29 @@ export async function getKind24RelaySet( throw new Error("NDK not available"); } + console.log(`[getKind24RelaySet] Getting relays for sender: ${senderPubkey.slice(0, 8)} -> recipient: ${recipientPubkey.slice(0, 8)}`); + // Get sender's outbox relays (NIP-65) const senderUser = ndk.getUser({ pubkey: senderPubkey }); const senderoutboxRelays = await getUseroutboxRelays(ndk, senderUser); + console.log(`[getKind24RelaySet] Sender outbox relays:`, senderoutboxRelays); // Get recipient's inbox relays (NIP-65) const recipientUser = ndk.getUser({ pubkey: recipientPubkey }); - const recipientinboxRelays = await getUserinboxRelays(ndk, recipientUser); + let recipientinboxRelays = await getUserinboxRelays(ndk, recipientUser); + console.log(`[getKind24RelaySet] Recipient inbox relays:`, recipientinboxRelays); + + // Fallback: if no inbox relays found, use recipient's outbox relays + if (recipientinboxRelays.length === 0) { + console.log(`[getKind24RelaySet] No inbox relays found for recipient, falling back to outbox relays`); + recipientinboxRelays = await getUseroutboxRelays(ndk, recipientUser); + console.log(`[getKind24RelaySet] Recipient outbox relays (used as fallback):`, recipientinboxRelays); + } // According to NIP-A4: Messages MUST be sent to the NIP-65 inbox relays of each receiver // and the outbox relay of the sender const targetRelays = [...new Set([...senderoutboxRelays, ...recipientinboxRelays])]; + console.log(`[getKind24RelaySet] Combined target relays:`, targetRelays); // Prioritize common relays between sender and recipient for better privacy const commonRelays = senderoutboxRelays.filter((relay: string) => @@ -235,6 +257,12 @@ export async function getKind24RelaySet( !senderoutboxRelays.includes(relay) ); + console.log(`[getKind24RelaySet] Common relays:`, commonRelays); + console.log(`[getKind24RelaySet] Sender-only relays:`, senderOnlyRelays); + console.log(`[getKind24RelaySet] Recipient-only relays:`, recipientOnlyRelays); + // Prioritize: common relays first, then sender outbox, then recipient inbox - return [...commonRelays, ...senderOnlyRelays, ...recipientOnlyRelays]; + const finalRelays = [...commonRelays, ...senderOnlyRelays, ...recipientOnlyRelays]; + console.log(`[getKind24RelaySet] Final relay list:`, finalRelays); + return finalRelays; }