import { nip04, type Event } from 'nostr-tools' import { nostrService } from './nostr' import type { AuthorPresentationDraft } from './articlePublisher' import type { SimplePoolWithSub } from '@/types/nostr-tools-extended' const RELAY_URL = process.env.NEXT_PUBLIC_NOSTR_RELAY_URL ?? 'wss://relay.damus.io' export function buildPresentationEvent(draft: AuthorPresentationDraft) { return { kind: 1 as const, created_at: Math.floor(Date.now() / 1000), tags: [ ['title', draft.title], ['preview', draft.preview], ['category', 'author-presentation'], ['presentation', 'true'], ['mainnet_address', draft.mainnetAddress], ['total_sponsoring', '0'], ['content-type', 'author-presentation'], ], content: draft.content, } } export function parsePresentationEvent(event: Event): import('@/types/nostr').AuthorPresentationArticle | null { const isPresentation = event.tags.some((tag) => tag[0] === 'presentation' && tag[1] === 'true') if (!isPresentation) { return null } const mainnetAddressTag = event.tags.find((tag) => tag[0] === 'mainnet_address') const sponsoringTag = event.tags.find((tag) => tag[0] === 'total_sponsoring') return { id: event.id, pubkey: event.pubkey, title: event.tags.find((tag) => tag[0] === 'title')?.[1] ?? 'Présentation', preview: event.tags.find((tag) => tag[0] === 'preview')?.[1] ?? event.content.substring(0, 200), content: event.content, createdAt: event.created_at, zapAmount: 0, paid: true, category: 'author-presentation', isPresentation: true, mainnetAddress: mainnetAddressTag?.[1] ?? '', totalSponsoring: sponsoringTag ? parseInt(sponsoringTag[1] ?? '0', 10) : 0, } } export function fetchAuthorPresentationFromPool( pool: SimplePoolWithSub, pubkey: string ): Promise { const filters = [ { kinds: [1], authors: [pubkey], '#category': ['author-presentation'], limit: 1, }, ] return new Promise((resolve) => { let resolved = false const sub = pool.sub([RELAY_URL], filters) const finalize = (value: import('@/types/nostr').AuthorPresentationArticle | null) => { if (resolved) { return } resolved = true sub.unsub() resolve(value) } sub.on('event', (event: Event) => { const parsed = parsePresentationEvent(event) if (parsed) { finalize(parsed) } }) sub.on('eose', () => finalize(null)) setTimeout(() => finalize(null), 5000) }) } export async function sendEncryptedContent( articleId: string, recipientPubkey: string, storedContent: { content: string; authorPubkey: string }, authorPrivateKey: string ): Promise { nostrService.setPrivateKey(authorPrivateKey) nostrService.setPublicKey(storedContent.authorPubkey) const encryptedContent = await Promise.resolve(nip04.encrypt(authorPrivateKey, recipientPubkey, storedContent.content)) const privateMessageEvent = { kind: 4, created_at: Math.floor(Date.now() / 1000), tags: [ ['p', recipientPubkey], ['e', articleId], ], content: encryptedContent, } const publishedEvent = await nostrService.publishEvent(privateMessageEvent) return Boolean(publishedEvent) }