148 lines
4.5 KiB
TypeScript
148 lines
4.5 KiB
TypeScript
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_<id>, #paywall, #payment
|
|
*/
|
|
export async function parseArticleFromEvent(event: Event): Promise<Article | null> {
|
|
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<Article> {
|
|
const result: Partial<Article> = {}
|
|
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<Article> {
|
|
const result: Partial<Article> = {}
|
|
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<Article> {
|
|
const result: Partial<Article> = {}
|
|
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<Article> {
|
|
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<typeof resolveObjectIdParts>[0] {
|
|
return {
|
|
...(params.tags.id ? { idTag: params.tags.id } : {}),
|
|
defaultVersion: params.tags.version ?? 0,
|
|
defaultIndex: 0,
|
|
generateHash: async (): Promise<string> =>
|
|
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,
|
|
}),
|
|
}
|
|
}
|