/** * Helper for syncing payment notes * Handles the special case of payment notes which require multiple subscriptions */ import type { Event, Filter } from 'nostr-tools' import type { SimplePoolWithSub } from '@/types/nostr-tools-extended' import { PLATFORM_SERVICE } from '../platformConfig' import { getPrimaryRelaySync } from '../config' import { createSubscription } from '@/types/nostr-tools-extended' import { tryWithRelayRotation } from '../relayRotation' import { extractTagsFromEvent } from '../nostrTagSystem' import { cachePaymentNotes } from './syncCacheHelpers' export function buildPaymentNoteFilters(userPubkey: string, lastSyncDate: number): Filter[] { return [ { kinds: [1], authors: [userPubkey], '#payment': [''], '#service': [PLATFORM_SERVICE], since: lastSyncDate, limit: 1000, }, { kinds: [1], '#recipient': [userPubkey], '#payment': [''], '#service': [PLATFORM_SERVICE], since: lastSyncDate, limit: 1000, }, ] } export async function createPaymentNoteSubscriptions( pool: SimplePoolWithSub, filters: Filter[] ): Promise>> { let subscriptions: Array> = [] try { const result = await tryWithRelayRotation( pool as unknown as import('nostr-tools').SimplePool, async (relayUrl, poolWithSub) => { await updateProgressForRelay(relayUrl) return filters.map((filter) => createSubscription(poolWithSub, [relayUrl], [filter])) }, 5000 ) subscriptions = result.flat() } catch { const usedRelayUrl = getPrimaryRelaySync() subscriptions = filters.map((filter) => createSubscription(pool, [usedRelayUrl], [filter])) } if (subscriptions.length === 0) { throw new Error('Failed to create subscriptions') } return subscriptions } async function updateProgressForRelay(relayUrl: string): Promise { const { syncProgressManager } = await import('../syncProgressManager') const currentProgress = syncProgressManager.getProgress() if (currentProgress) { syncProgressManager.setProgress({ ...currentProgress, currentStep: 0, currentRelay: relayUrl, }) } } export async function processPaymentNoteEvents( subscriptions: Array> ): Promise { const events: Event[] = [] return new Promise((resolve) => { let finished = false let eoseCount = 0 const done = async (): Promise => { if (finished) { return } finished = true subscriptions.forEach((sub) => sub.unsub()) await cachePaymentNotes(events) resolve() } const handleEose = (): void => { eoseCount++ if (eoseCount >= subscriptions.length) { console.warn(`[Sync] EOSE for payment notes, received ${events.length} events`) void done() } } setupPaymentNoteSubscriptions(subscriptions, events, handleEose) setTimeout((): void => { if (!finished) { console.warn(`[Sync] Timeout for payment notes, received ${events.length} events`) } void done() }, 10000).unref?.() }) } function setupPaymentNoteSubscriptions( subscriptions: Array>, events: Event[], onEose: () => void ): void { subscriptions.forEach((sub) => { sub.on('event', (event: Event): void => { const tags = extractTagsFromEvent(event) if (tags.type === 'payment' && tags.payment) { console.warn('[Sync] Received payment note event:', event.id) if (!events.some((e) => e.id === event.id)) { events.push(event) } } }) sub.on('eose', onEose) }) }