/** * Relay list fetcher (kind 10002 and 10432) */ import { nostrClient } from '../nostr/nostr-client.js'; import { config } from '../nostr/config.js'; import type { NostrEvent } from '../../types/nostr.js'; export interface RelayInfo { url: string; read: boolean; write: boolean; } /** * Parse relay list from event */ export function parseRelayList(event: NostrEvent): RelayInfo[] { const relays: RelayInfo[] = []; for (const tag of event.tags) { if (tag[0] === 'r' && tag[1]) { const url = tag[1]; const markers = tag.slice(2); // If no markers, relay is both read and write if (markers.length === 0) { relays.push({ url, read: true, write: true }); continue; } // Check for explicit markers const hasRead = markers.includes('read'); const hasWrite = markers.includes('write'); // If only 'read' marker: read=true, write=false // If only 'write' marker: read=false, write=true // If both or neither explicitly: both true (default behavior) const read = hasRead || (!hasRead && !hasWrite); const write = hasWrite || (!hasRead && !hasWrite); relays.push({ url, read, write }); } } return relays; } /** * Fetch relay lists for a pubkey (kind 10002 and 10432) */ export async function fetchRelayLists( pubkey: string, relays?: string[] ): Promise<{ inbox: string[]; outbox: string[]; }> { const relayList = relays || [ ...config.defaultRelays, ...config.profileRelays ]; // Fetch both kind 10002 and 10432 const events = await nostrClient.fetchEvents( [ { kinds: [10002], authors: [pubkey], limit: 1 }, { kinds: [10432], authors: [pubkey], limit: 1 } ], relayList, { useCache: true, cacheResults: true } ); const inbox: string[] = []; const outbox: string[] = []; for (const event of events) { const relayInfos = parseRelayList(event); for (const info of relayInfos) { if (info.read && !inbox.includes(info.url)) { inbox.push(info.url); } if (info.write && !outbox.includes(info.url)) { outbox.push(info.url); } } } // Deduplicate return { inbox: [...new Set(inbox)], outbox: [...new Set(outbox)] }; }