diff --git a/README.md b/README.md index cd5221e..5193757 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,11 @@ let events = await pool.list(relays, [{kinds: [0, 1]}]) let event = await pool.get(relays, { ids: ['44e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'] }) + +let relaysForEvent = pool.seenOn( + '44e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245' +) +// relaysForEvent will be an array of URLs from relays a given event was seen on ``` ### Querying profile data from a NIP-05 address diff --git a/package.json b/package.json index b81ab91..a35e7b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nostr-tools", - "version": "1.3.1", + "version": "1.3.2", "description": "Tools for making a Nostr client.", "repository": { "type": "git", diff --git a/pool.test.js b/pool.test.js index 1c6805f..2a23830 100644 --- a/pool.test.js +++ b/pool.test.js @@ -20,7 +20,12 @@ let relays = [ ] afterAll(async () => { - await pool.close([...relays, 'wss://nostr-relay.untethr.me']) + await pool.close([ + ...relays, + 'wss://nostr-relay.untethr.me', + 'wss://offchain.pub', + 'wss://eden.nostr.land' + ]) }) test('removing duplicates when querying', async () => { @@ -120,4 +125,9 @@ test('list()', async () => { .reduce((acc, n) => (acc.indexOf(n) !== -1 ? acc : [...acc, n]), []) .length ) + + let relaysForAllEvents = events + .map(event => pool.seenOn(event.id)) + .reduce((acc, n) => acc.concat(n), []) + expect(relaysForAllEvents.length).toBeGreaterThanOrEqual(events.length) }) diff --git a/pool.ts b/pool.ts index b8256b3..6ecf469 100644 --- a/pool.ts +++ b/pool.ts @@ -6,10 +6,10 @@ import {SubscriptionOptions, Sub, Pub} from './relay' export class SimplePool { private _conn: {[url: string]: Relay} + private _seenOn: {[id: string]: Set} = {} // a map of all events we've seen in each relay - constructor(defaultRelays: string[] = []) { + constructor() { this._conn = {} - defaultRelays.forEach(this.ensureRelay) } async close(relays: string[]): Promise { @@ -37,7 +37,12 @@ export class SimplePool { sub(relays: string[], filters: Filter[], opts?: SubscriptionOptions): Sub { let _knownIds: Set = new Set() let modifiedOpts = opts || {} - modifiedOpts.alreadyHaveEvent = id => _knownIds.has(id) + modifiedOpts.alreadyHaveEvent = (id, url) => { + let set = this._seenOn[id] || new Set() + set.add(url) + this._seenOn[id] = set + return _knownIds.has(id) + } let subs: Sub[] = [] let eventListeners: Set<(event: Event) => void> = new Set() @@ -47,9 +52,7 @@ export class SimplePool { let eoseSent = false let eoseTimeout = setTimeout(() => { eoseSent = true - for (let cb of eoseListeners.values()) { - cb() - } + for (let cb of eoseListeners.values()) cb() }, 2400) relays.forEach(async relay => { @@ -58,9 +61,7 @@ export class SimplePool { let s = r.sub(filters, modifiedOpts) s.on('event', (event: Event) => { _knownIds.add(event.id as string) - for (let cb of eventListeners.values()) { - cb(event) - } + for (let cb of eventListeners.values()) cb(event) }) s.on('eose', () => { if (eoseSent) return @@ -68,9 +69,7 @@ export class SimplePool { eosesMissing-- if (eosesMissing === 0) { clearTimeout(eoseTimeout) - for (let cb of eoseListeners.values()) { - cb() - } + for (let cb of eoseListeners.values()) cb() } }) subs.push(s) @@ -85,9 +84,14 @@ export class SimplePool { subs.forEach(sub => sub.unsub()) }, on(type, cb) { - if (type === 'event') { - eventListeners.add(cb) - } else if (type === 'eose') eoseListeners.add(cb) + switch (type) { + case 'event': + eventListeners.add(cb) + break + case 'eose': + eoseListeners.add(cb) + break + } }, off(type, cb) { if (type === 'event') { @@ -147,6 +151,10 @@ export class SimplePool { return s }) } + + seenOn(id: string): string[] { + return Array.from(this._seenOn[id]?.values?.() || []) + } } function badPub(relay: string): Pub { diff --git a/relay.ts b/relay.ts index 5e6fea7..7593cef 100644 --- a/relay.ts +++ b/relay.ts @@ -30,9 +30,9 @@ export type Sub = { } export type SubscriptionOptions = { - skipVerification?: boolean - alreadyHaveEvent?: null | ((id: string) => boolean) id?: string + skipVerification?: boolean + alreadyHaveEvent?: null | ((id: string, relay: string) => boolean) } export function relayInit(url: string): Relay { @@ -112,7 +112,7 @@ export function relayInit(url: string): Relay { if ( so && so.alreadyHaveEvent && - so.alreadyHaveEvent(getHex64(json, 'id')) + so.alreadyHaveEvent(getHex64(json, 'id'), url) ) { return }