import { Event } from 'nostr-tools' import { nostrService } from './nostr' import { PLATFORM_NPUB } from './platformConfig' import type { SimplePoolWithSub } from '@/types/nostr-tools-extended' import type { ContentDeliveryTracking } from './platformTrackingTypes' import { buildTrackingEvent } from './platformTrackingEvents' import { parseTrackingEvent, createArticleDeliveriesSubscription, createRecipientDeliveriesSubscription } from './platformTrackingQueries' export type { ContentDeliveryTracking } from './platformTrackingTypes' /** * Platform tracking service * Publishes tracking events on Nostr for content delivery verification * These events are signed by the platform and can be queried for audit purposes */ export class PlatformTrackingService { private readonly platformPubkey: string = PLATFORM_NPUB private async publishTrackingEvent(event: Event): Promise { const pool = nostrService.getPool() if (!pool) { throw new Error('Pool not initialized') } const poolWithSub = pool as SimplePoolWithSub const { getPrimaryRelaySync } = await import('./config') const relayUrl = getPrimaryRelaySync() const pubs = poolWithSub.publish([relayUrl], event) await Promise.all(pubs) } private validateTrackingPool(): { pool: SimplePoolWithSub; authorPubkey: string } | null { const pool = nostrService.getPool() if (!pool) { console.error('Pool not initialized for platform tracking') return null } const authorPubkey = nostrService.getPublicKey() if (!authorPubkey) { console.error('Author public key not available for tracking') return null } return { pool: pool as SimplePoolWithSub, authorPubkey } } /** * Publish a content delivery tracking event * This event is published by the author but tagged for platform tracking * The platform can query these events to track all content deliveries */ async trackContentDelivery( tracking: ContentDeliveryTracking, authorPrivateKey: string ): Promise { try { const validation = this.validateTrackingPool() if (!validation) { return null } const { authorPubkey } = validation const event = buildTrackingEvent(tracking, authorPubkey, authorPrivateKey, this.platformPubkey) await this.publishTrackingEvent(event) console.log('Platform tracking event published', { eventId: event.id, articleId: tracking.articleId, recipientPubkey: tracking.recipientPubkey, messageEventId: tracking.messageEventId, authorAmount: tracking.authorAmount, platformCommission: tracking.platformCommission, timestamp: new Date().toISOString(), }) return event.id } catch (error) { console.error('Error publishing platform tracking event', { articleId: tracking.articleId, recipientPubkey: tracking.recipientPubkey, error: error instanceof Error ? error.message : 'Unknown error', timestamp: new Date().toISOString(), }) return null } } /** * Query tracking events for an article * Returns all delivery tracking events for a specific article */ async getArticleDeliveries(articleId: string): Promise { try { const pool = nostrService.getPool() if (!pool) { return [] } return new Promise((resolve) => { const deliveries: ContentDeliveryTracking[] = [] let resolved = false const poolWithSub = pool as SimplePoolWithSub const sub = createArticleDeliveriesSubscription(poolWithSub, articleId, this.platformPubkey) const finalize = () => { if (resolved) { return } resolved = true sub.unsub() resolve(deliveries) } sub.on('event', (event: Event) => { const delivery = parseTrackingEvent(event) if (delivery) { deliveries.push(delivery) } }) sub.on('eose', finalize) setTimeout(finalize, 5000) }) } catch (error) { console.error('Error querying article deliveries', { articleId, error: error instanceof Error ? error.message : 'Unknown error', }) return [] } } /** * Query all deliveries for a recipient */ async getRecipientDeliveries(recipientPubkey: string): Promise { try { const pool = nostrService.getPool() if (!pool) { return [] } return new Promise((resolve) => { const deliveries: ContentDeliveryTracking[] = [] let resolved = false const poolWithSub = pool as SimplePoolWithSub const sub = createRecipientDeliveriesSubscription(poolWithSub, recipientPubkey, this.platformPubkey) const finalize = () => { if (resolved) { return } resolved = true sub.unsub() resolve(deliveries) } sub.on('event', (event: Event) => { const delivery = parseTrackingEvent(event) if (delivery) { deliveries.push(delivery) } }) sub.on('eose', finalize) setTimeout(finalize, 5000) }) } catch (error) { console.error('Error querying recipient deliveries', { recipientPubkey, error: error instanceof Error ? error.message : 'Unknown error', }) return [] } } } export const platformTracking = new PlatformTrackingService()