import type { ArticleDraft } from '../articlePublisher' import type { Review } from '@/types/nostr' import { buildTags } from '../nostrTagSystem' import { PLATFORM_SERVICE } from '../platformConfig' import { buildObjectId } from '../urlGenerator' import { getPublishRelays } from '../relaySelection' import { writeOrchestrator } from '../writeOrchestrator' import { finalizeEvent, type EventTemplate } from 'nostr-tools' import { hexToBytes } from 'nostr-tools/utils' import { ensureKeys, getPrivateKeyForSigning, mapDraftCategoryToTag, requireCategory } from './shared' export async function publishReview(params: { articleId: string seriesId: string category: ArticleDraft['category'] authorPubkey: string reviewerPubkey: string content: string title?: string text?: string authorPrivateKey?: string }): Promise { ensureKeys(params.reviewerPubkey, params.authorPrivateKey) requireCategory(params.category) const hash = await buildReviewHashId({ reviewerPubkey: params.reviewerPubkey, articleId: params.articleId, content: params.content, ...(params.title ? { title: params.title } : {}) }) const parsedReview = buildParsedReview({ params, hash, version: 0, index: 0 }) const eventTemplate = await buildReviewEvent(params, params.category) const privateKey = getPrivateKeyForSigning(params.authorPrivateKey) const event = finalizeEvent(eventTemplate, hexToBytes(privateKey)) await publishReviewToRelays({ parsedReview, event }) return parsedReview } function buildParsedReview(params: { params: { articleId: string; authorPubkey: string; reviewerPubkey: string; content: string; title?: string; text?: string } hash: string version: number index: number }): Review { return { id: buildObjectId(params.hash, params.index, params.version), hash: params.hash, version: params.version, index: params.index, articleId: params.params.articleId, authorPubkey: params.params.authorPubkey, reviewerPubkey: params.params.reviewerPubkey, content: params.params.content, description: params.params.content.substring(0, 200), createdAt: Math.floor(Date.now() / 1000), ...(params.params.title ? { title: params.params.title } : {}), ...(params.params.text ? { text: params.params.text } : {}), kindType: 'review', } } async function publishReviewToRelays(params: { parsedReview: Review; event: import('nostr-tools').Event }): Promise { const relays = await getPublishRelays() const result = await writeOrchestrator.writeAndPublish( { objectType: 'review', hash: params.parsedReview.hash, event: params.event, parsed: params.parsedReview, version: params.parsedReview.version, hidden: false, index: params.parsedReview.index }, relays ) if (!result.success) { throw new Error('Failed to publish review') } } async function buildReviewEvent( params: { articleId: string; seriesId: string; authorPubkey: string; reviewerPubkey: string; content: string; title?: string; text?: string }, category: NonNullable ): Promise> { const ctx = await buildReviewEventContext(params, category) return { kind: 1, created_at: Math.floor(Date.now() / 1000), content: params.content, tags: ctx.tags } } async function buildReviewEventContext( params: { articleId: string; reviewerPubkey: string; content: string; title?: string; text?: string }, category: NonNullable ): Promise<{ tags: string[][] }> { const newCategory = mapDraftCategoryToTag(category) const hashId = await buildReviewHashId({ reviewerPubkey: params.reviewerPubkey, articleId: params.articleId, content: params.content, ...(params.title ? { title: params.title } : {}) }) const reviewJson = buildReviewJson({ reviewerPubkey: params.reviewerPubkey, articleId: params.articleId, content: params.content, ...(params.title ? { title: params.title } : {}), newCategory, hashId }) const tags = buildReviewTags({ newCategory, hashId, articleId: params.articleId, reviewerPubkey: params.reviewerPubkey, ...(params.title ? { title: params.title } : {}), ...(params.text ? { text: params.text } : {}), reviewJson }) return { tags } } async function buildReviewHashId(params: { reviewerPubkey: string; articleId: string; content: string; title?: string }): Promise { const { generateReviewHashId } = await import('../hashIdGenerator') return generateReviewHashId({ pubkey: params.reviewerPubkey, articleId: params.articleId, reviewerPubkey: params.reviewerPubkey, content: params.content, ...(params.title ? { title: params.title } : {}) }) } function buildReviewJson(params: { reviewerPubkey: string; articleId: string; content: string; title?: string; newCategory: 'sciencefiction' | 'research'; hashId: string }): string { return JSON.stringify({ type: 'review', pubkey: params.reviewerPubkey, articleId: params.articleId, reviewerPubkey: params.reviewerPubkey, content: params.content, title: params.title, category: params.newCategory, id: params.hashId, version: 0, index: 0, }) } function buildReviewTags(params: { newCategory: 'sciencefiction' | 'research' hashId: string articleId: string reviewerPubkey: string title?: string text?: string reviewJson: string }): string[][] { const tags = buildTags({ type: 'quote', category: params.newCategory, id: params.hashId, service: PLATFORM_SERVICE, version: 0, hidden: false, paywall: false, articleId: params.articleId, reviewerPubkey: params.reviewerPubkey, ...(params.title ? { title: params.title } : {}), }) if (params.text) { tags.push(['text', params.text]) } tags.push(['json', params.reviewJson]) return tags }