- Correction toutes erreurs TypeScript : - Variables non utilisées supprimées - Types optionnels corrigés (exactOptionalPropertyTypes) - Imports corrigés (PLATFORM_BITCOIN_ADDRESS depuis platformConfig) - Gestion correcte des propriétés optionnelles - Suppression fichiers obsolètes : - code-cleanup-summary.md (redondant) - todo-implementation*.md (todos obsolètes) - corrections-completed.md, fallbacks-found.md (corrections faites) - implementation-summary.md (redondant) - documentation-plan.md (plan, pas documentation) - Suppression scripts temporaires : - add-ssh-key.sh - add-ssh-key-plink.sh - Réorganisation documentation dans docs/ : - architecture.md (nouveau) - commissions.md (nouveau) - implementation-summary.md - remaining-tasks.md - split-and-transfer.md - commission-system.md - commission-implementation.md - content-delivery-verification.md Toutes erreurs TypeScript corrigées, documentation centralisée.
151 lines
4.9 KiB
TypeScript
151 lines
4.9 KiB
TypeScript
import type { Event } from 'nostr-tools'
|
|
import type { Article, KindType, MediaRef, Review, Series } from '@/types/nostr'
|
|
|
|
/**
|
|
* Parse article metadata from Nostr event
|
|
*/
|
|
export function parseArticleFromEvent(event: Event): Article | null {
|
|
try {
|
|
const tags = extractTags(event)
|
|
if (tags.kindType && tags.kindType !== 'article') {
|
|
return null
|
|
}
|
|
const { previewContent } = getPreviewContent(event.content, tags.preview)
|
|
return buildArticle(event, tags, previewContent)
|
|
} catch (e) {
|
|
console.error('Error parsing article:', e)
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function parseSeriesFromEvent(event: Event): Series | null {
|
|
try {
|
|
const tags = extractTags(event)
|
|
if (tags.kindType && tags.kindType !== 'series') {
|
|
return null
|
|
}
|
|
if (!tags.title || !tags.description) {
|
|
return null
|
|
}
|
|
const series: Series = {
|
|
id: event.id,
|
|
pubkey: event.pubkey,
|
|
title: tags.title,
|
|
description: tags.description,
|
|
preview: tags.preview ?? event.content.substring(0, 200),
|
|
...(tags.category ? { category: tags.category } : { category: 'science-fiction' }),
|
|
...(tags.coverUrl ? { coverUrl: tags.coverUrl } : {}),
|
|
}
|
|
if (tags.kindType) {
|
|
series.kindType = tags.kindType
|
|
}
|
|
return series
|
|
} catch (e) {
|
|
console.error('Error parsing series:', e)
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function parseReviewFromEvent(event: Event): Review | null {
|
|
try {
|
|
const tags = extractTags(event)
|
|
if (tags.kindType && tags.kindType !== 'review') {
|
|
return null
|
|
}
|
|
const articleId = tags.articleId
|
|
const reviewer = tags.reviewerPubkey
|
|
if (!articleId || !reviewer) {
|
|
return null
|
|
}
|
|
const rewardedTag = event.tags.find((tag) => tag[0] === 'rewarded' && tag[1] === 'true')
|
|
const rewardAmountTag = event.tags.find((tag) => tag[0] === 'reward_amount')
|
|
|
|
const review: Review = {
|
|
id: event.id,
|
|
articleId,
|
|
authorPubkey: tags.author ?? event.pubkey,
|
|
reviewerPubkey: reviewer,
|
|
content: event.content,
|
|
createdAt: event.created_at,
|
|
...(tags.title ? { title: tags.title } : {}),
|
|
...(rewardedTag ? { rewarded: true } : {}),
|
|
...(rewardAmountTag ? { rewardAmount: parseInt(rewardAmountTag[1] ?? '0', 10) } : {}),
|
|
}
|
|
if (tags.kindType) {
|
|
review.kindType = tags.kindType
|
|
}
|
|
return review
|
|
} catch (e) {
|
|
console.error('Error parsing review:', e)
|
|
return null
|
|
}
|
|
}
|
|
|
|
function extractTags(event: Event) {
|
|
const findTag = (key: string) => event.tags.find((tag) => tag[0] === key)?.[1]
|
|
const mediaTags = event.tags.filter((tag) => tag[0] === 'media')
|
|
const media: MediaRef[] =
|
|
mediaTags
|
|
.map((tag) => {
|
|
const url = tag[1]
|
|
const type = tag[2] === 'video' ? 'video' : 'image'
|
|
if (!url) {
|
|
return null
|
|
}
|
|
return { url, type }
|
|
})
|
|
.filter(Boolean) as MediaRef[]
|
|
|
|
return {
|
|
title: findTag('title') ?? 'Untitled',
|
|
preview: findTag('preview'),
|
|
description: findTag('description'),
|
|
zapAmount: parseInt(findTag('zap') ?? '800', 10),
|
|
invoice: findTag('invoice'),
|
|
paymentHash: findTag('payment_hash'),
|
|
category: findTag('category') as import('@/types/nostr').ArticleCategory | undefined,
|
|
isPresentation: findTag('presentation') === 'true',
|
|
mainnetAddress: findTag('mainnet_address'),
|
|
totalSponsoring: parseInt(findTag('total_sponsoring') ?? '0', 10),
|
|
authorPresentationId: findTag('author_presentation_id'),
|
|
seriesId: findTag('series'),
|
|
bannerUrl: findTag('banner'),
|
|
coverUrl: findTag('cover'),
|
|
media,
|
|
kindType: findTag('kind_type') as KindType | undefined,
|
|
articleId: findTag('article'),
|
|
reviewerPubkey: findTag('reviewer'),
|
|
author: findTag('author'),
|
|
}
|
|
}
|
|
|
|
function getPreviewContent(content: string, previewTag?: string) {
|
|
const lines = content.split('\n')
|
|
const previewContent = previewTag ?? lines[0] ?? content.substring(0, 200)
|
|
return { previewContent }
|
|
}
|
|
|
|
function buildArticle(event: Event, tags: ReturnType<typeof extractTags>, preview: string): Article {
|
|
return {
|
|
id: event.id,
|
|
pubkey: event.pubkey,
|
|
title: tags.title,
|
|
preview,
|
|
content: '',
|
|
createdAt: event.created_at,
|
|
zapAmount: tags.zapAmount,
|
|
paid: false,
|
|
...(tags.invoice ? { invoice: tags.invoice } : {}),
|
|
...(tags.paymentHash ? { paymentHash: tags.paymentHash } : {}),
|
|
...(tags.category ? { category: tags.category } : {}),
|
|
...(tags.isPresentation ? { isPresentation: tags.isPresentation } : {}),
|
|
...(tags.mainnetAddress ? { mainnetAddress: tags.mainnetAddress } : {}),
|
|
...(tags.totalSponsoring ? { totalSponsoring: tags.totalSponsoring } : {}),
|
|
...(tags.authorPresentationId ? { authorPresentationId: tags.authorPresentationId } : {}),
|
|
...(tags.seriesId ? { seriesId: tags.seriesId } : {}),
|
|
...(tags.bannerUrl ? { bannerUrl: tags.bannerUrl } : {}),
|
|
...(tags.media.length ? { media: tags.media } : {}),
|
|
...(tags.kindType ? { kindType: tags.kindType } : {}),
|
|
}
|
|
}
|