You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

101 lines
3.1 KiB

/**
* Service for fetching user's preferred relays from their inbox/outbox
*/
import { NostrClient } from './nostr-client.js';
import type { NostrEvent } from '../../types/nostr.js';
import { KIND } from '../../types/nostr.js';
import logger from '../logger.js';
import { truncatePubkey } from '../../utils/security.js';
export async function getUserRelays(
pubkey: string,
nostrClient: NostrClient
): Promise<{ inbox: string[]; outbox: string[] }> {
const inbox: string[] = [];
const outbox: string[] = [];
try {
// Fetch kind 10002 (relay list) - get multiple to find the newest
// Use a higher limit to ensure we get all relay list events
const relayListEvents = await nostrClient.fetchEvents([
{
kinds: [KIND.RELAY_LIST],
authors: [pubkey],
limit: 20 // Get more events to ensure we find the newest
}
]);
logger.debug({
pubkey: truncatePubkey(pubkey),
eventCount: relayListEvents.length,
eventIds: relayListEvents.map(e => e.id)
}, 'Fetched relay list events');
if (relayListEvents.length > 0) {
// Sort by created_at descending to get the newest event first
relayListEvents.sort((a, b) => b.created_at - a.created_at);
const event = relayListEvents[0];
logger.debug({
pubkey: truncatePubkey(pubkey),
eventId: event.id,
tagCount: event.tags.length,
createdAt: new Date(event.created_at * 1000).toISOString()
}, 'Found kind 10002 relay list event');
for (const tag of event.tags) {
if (tag[0] === 'relay' && tag[1]) {
const relay = tag[1];
const read = tag[2] !== 'write';
const write = tag[2] !== 'read';
if (read) inbox.push(relay);
if (write) outbox.push(relay);
}
}
logger.debug({
pubkey: truncatePubkey(pubkey),
inboxCount: inbox.length,
outboxCount: outbox.length
}, 'Extracted relays from kind 10002 event');
} else {
logger.debug({ pubkey: truncatePubkey(pubkey) }, 'No kind 10002 relay list events found');
}
// Fallback to kind 3 (contacts) for older clients
if (inbox.length === 0 && outbox.length === 0) {
const contactEvents = await nostrClient.fetchEvents([
{
kinds: [KIND.CONTACT_LIST],
authors: [pubkey],
limit: 1
}
]);
if (contactEvents.length > 0) {
const event = contactEvents[0];
// Extract relays from content (JSON) or tags
try {
const content = JSON.parse(event.content);
if (content.relays && Array.isArray(content.relays)) {
inbox.push(...content.relays);
outbox.push(...content.relays);
}
} catch {
// Not JSON, check tags
for (const tag of event.tags) {
if (tag[0] === 'relay' && tag[1]) {
inbox.push(tag[1]);
outbox.push(tag[1]);
}
}
}
}
}
} catch (error) {
logger.error({ error, pubkey: truncatePubkey(pubkey) }, 'Failed to fetch user relays');
}
return { inbox, outbox };
}