@nostr-dev-kit/ndk
Version:
NDK - Nostr Development Kit
126 lines (107 loc) • 5.04 kB
text/typescript
import type { NDKEvent } from "../events/index.js";
import { NDKRelayList, relayListFromKind3 } from "../events/kinds/NDKRelayList.js";
import { NDKKind } from "../events/kinds/index.js";
import type { NDK } from "../ndk/index.js";
import type { NDKRelay } from "../relay/index.js";
import type { NDKSubscriptionOptions } from "../subscription/index.js";
import { NDKRelaySet } from "../relay/sets/index.js";
import { NDKSubscriptionCacheUsage } from "../subscription/index.js";
import type { Hexpubkey } from "../user/index.js";
export async function getRelayListForUser(pubkey: Hexpubkey, ndk: NDK): Promise<NDKRelayList> {
const list = await getRelayListForUsers([pubkey], ndk);
return list.get(pubkey)!;
}
/**
* Fetches a map of relay lists for a number of users
* @param pubkeys
* @param ndk
* @returns
*/
export async function getRelayListForUsers(
pubkeys: Hexpubkey[],
ndk: NDK,
skipCache = false,
timeout = 1000
): Promise<Map<Hexpubkey, NDKRelayList>> {
const pool = ndk.outboxPool || ndk.pool;
const set = new Set<NDKRelay>();
for (const relay of pool.relays.values()) set.add(relay);
const relayLists = new Map<Hexpubkey, NDKRelayList>();
const fromContactList = new Map<Hexpubkey, NDKEvent>();
const relaySet = new NDKRelaySet(set, ndk);
// get all kind 10002 events from cache if we have an adapter and is locking
if (ndk.cacheAdapter?.locking && !skipCache) {
const cachedList = await ndk.fetchEvents(
{ kinds: [3, 10002], authors: Array.from(new Set(pubkeys)) },
{ cacheUsage: NDKSubscriptionCacheUsage.ONLY_CACHE, subId: "ndk-relay-list-fetch" }
);
// get list of relay lists from cache
for (const relayList of cachedList) {
if (relayList.kind === 10002)
relayLists.set(relayList.pubkey, NDKRelayList.from(relayList));
}
for (const relayList of cachedList) {
if (relayList.kind === 3) {
// skip if we already have a relay list for this pubkey
if (relayLists.has(relayList.pubkey)) continue;
const list = relayListFromKind3(ndk, relayList);
if (list) fromContactList.set(relayList.pubkey, list);
}
}
// remove the pubkeys we found from the list
pubkeys = pubkeys.filter(
(pubkey) => !relayLists.has(pubkey) && !fromContactList.has(pubkey)
);
}
// if we have no pubkeys left, return the results
if (pubkeys.length === 0) return relayLists;
const relayListEvents = new Map<Hexpubkey, NDKEvent>();
const contactListEvents = new Map<Hexpubkey, NDKEvent>();
return new Promise<Map<Hexpubkey, NDKRelayList>>((resolve) => {
const handleSubscription = async () => {
// Get from relays the missing pubkeys
// Prepare options, including the relaySet if available
const subscribeOpts: NDKSubscriptionOptions = {
closeOnEose: true,
pool,
groupable: true,
subId: "ndk-relay-list-fetch",
addSinceFromCache: true,
relaySet,
};
if (relaySet) subscribeOpts.relaySet = relaySet;
ndk.subscribe({ kinds: [3, 10002], authors: pubkeys }, subscribeOpts, {
onEvent: (event) => {
if (event.kind === NDKKind.RelayList) {
const existingEvent = relayListEvents.get(event.pubkey);
if (existingEvent && existingEvent.created_at! > event.created_at!) return;
relayListEvents.set(event.pubkey, event);
} else if (event.kind === NDKKind.Contacts) {
const existingEvent = contactListEvents.get(event.pubkey);
if (existingEvent && existingEvent.created_at! > event.created_at!) return;
contactListEvents.set(event.pubkey, event);
}
},
onEose: () => {
// Get all kind 10002 events
for (const event of relayListEvents.values()) {
relayLists.set(event.pubkey, NDKRelayList.from(event));
}
// Go through the pubkeys we don't have results for and get the from kind 3 events
for (const pubkey of pubkeys) {
if (relayLists.has(pubkey)) continue;
const contactList = contactListEvents.get(pubkey);
if (!contactList) continue;
const list = relayListFromKind3(ndk, contactList);
if (list) relayLists.set(pubkey, list);
}
resolve(relayLists);
},
});
setTimeout(() => {
resolve(relayLists);
}, timeout);
};
handleSubscription();
});
}