import type { Event } from 'nostr-tools' import type { Article, Page } from '@/types/nostr' import { extractTagsFromEvent } from '../nostrTagSystem' import { buildObjectId } from '../urlGenerator' import { generateHashId } from '../hashIdGenerator' import { getPreviewContent, mapNostrCategoryToLegacy, resolveObjectIdParts, resolveThumbnailUrl, type ExtractedTags } from './shared' /** * Parse article metadata from Nostr event * Uses new tag system: #publication, #sciencefiction|research, #id_, #paywall, #payment */ export async function parseArticleFromEvent(event: Event): Promise
{ try { const tags = extractTagsFromEvent(event) if (tags.type !== 'publication') { return null } const { previewContent } = getPreviewContent(event.content, tags.preview) return buildArticle(event, tags, previewContent) } catch (e) { console.error('Error parsing article:', e) return null } } function parsePagesFromEventJsonTag(event: Event): Page[] | undefined { const jsonTag = event.tags.find((tag) => tag[0] === 'json')?.[1] if (!jsonTag) { return undefined } try { const parsed: unknown = JSON.parse(jsonTag) if (typeof parsed !== 'object' || parsed === null) { return undefined } const metadata = parsed as { pages?: unknown } if (!Array.isArray(metadata.pages)) { return undefined } return metadata.pages as Page[] } catch { return undefined } } function buildArticlePaymentFields(tags: ExtractedTags): Partial
{ const result: Partial
= {} if (tags.invoice) { result.invoice = tags.invoice } if (tags.paymentHash) { result.paymentHash = tags.paymentHash } return result } function buildArticleClassificationFields(params: { tags: ExtractedTags category: 'science-fiction' | 'scientific-research' | undefined isPresentation: boolean }): Partial
{ const result: Partial
= {} if (params.category) { result.category = params.category } if (params.isPresentation) { result.isPresentation = true } if (params.tags.type === 'publication' || params.tags.type === 'author') { result.kindType = 'article' } return result } function buildArticleOptionalMetaFields(params: { tags: ExtractedTags; pages: Page[] | undefined }): Partial
{ const result: Partial
= {} if (params.tags.mainnetAddress) { result.mainnetAddress = params.tags.mainnetAddress } if (params.tags.totalSponsoring) { result.totalSponsoring = params.tags.totalSponsoring } if (params.tags.seriesId) { result.seriesId = params.tags.seriesId } if (params.tags.bannerUrl) { result.bannerUrl = params.tags.bannerUrl } if (params.pages && params.pages.length > 0) { result.pages = params.pages } return result } async function buildArticle(event: Event, tags: ExtractedTags, preview: string): Promise
{ const category = mapNostrCategoryToLegacy(tags.category) const isPresentation = tags.type === 'author' const { hash, version, index } = await resolveObjectIdParts(buildObjectIdResolutionParams({ event, tags, preview, isPresentation })) const id = buildObjectId(hash, index, version) const pages = parsePagesFromEventJsonTag(event) return { id, hash, version, index, pubkey: event.pubkey, title: tags.title ?? 'Untitled', preview, content: '', description: tags.description ?? '', contentDescription: tags.description ?? '', createdAt: event.created_at, zapAmount: tags.zapAmount ?? 800, paid: false, thumbnailUrl: resolveThumbnailUrl(tags), ...buildArticlePaymentFields(tags), ...buildArticleClassificationFields({ tags, category, isPresentation }), ...buildArticleOptionalMetaFields({ tags, pages }), } } function buildObjectIdResolutionParams(params: { event: Event tags: ExtractedTags preview: string isPresentation: boolean }): Parameters[0] { return { ...(params.tags.id ? { idTag: params.tags.id } : {}), defaultVersion: params.tags.version ?? 0, defaultIndex: 0, generateHash: async (): Promise => generateHashId({ type: params.isPresentation ? 'author' : 'publication', pubkey: params.event.pubkey, title: params.tags.title ?? 'Untitled', preview: params.preview, category: params.tags.category ?? 'sciencefiction', seriesId: params.tags.seriesId ?? '', bannerUrl: params.tags.bannerUrl ?? '', zapAmount: params.tags.zapAmount ?? 800, }), } }