import { nostrService } from './nostr' import { PLATFORM_COMMISSIONS } from './platformCommissions' import type { Event as NostrEvent } from 'nostr-tools' import { finalizeEvent } from 'nostr-tools' import { hexToBytes } from 'nostr-tools/utils' import { objectCache } from './objectCache' import type { Review } from '@/types/nostr' import { getPublishRelays } from './relaySelection' export async function fetchOriginalReviewEvent(reviewId: string): Promise { // Read only from IndexedDB cache const parsed = await objectCache.getById('review', reviewId) if (parsed) { // Get the event from cache const event = await objectCache.getEventById('review', reviewId) if (event) { return event } } // Not found in cache - return null (no network request) return null } export function buildRewardEvent(originalEvent: NostrEvent, reviewId: string): { kind: number created_at: number tags: string[][] content: string } { return { kind: 1, created_at: Math.floor(Date.now() / 1000), tags: [ ...originalEvent.tags.filter((tag) => tag[0] !== 'rewarded' && tag[0] !== 'reward_amount'), ['e', reviewId], ['rewarded', 'true'], ['reward_amount', PLATFORM_COMMISSIONS.review.total.toString()], ], content: originalEvent.content, } } export function checkIfAlreadyRewarded(originalEvent: NostrEvent, reviewId: string): boolean { const alreadyRewarded = originalEvent.tags.some((tag) => tag[0] === 'rewarded' && tag[1] === 'true') if (alreadyRewarded) { console.warn('Review already marked as rewarded', { reviewId, timestamp: new Date().toISOString(), }) } return alreadyRewarded } export async function publishRewardEvent( updatedEventTemplate: { kind: number created_at: number tags: string[][] content: string }, reviewId: string ): Promise { try { const { updatedParsed, hash, index, newVersion } = await buildRewardedReviewUpdate(reviewId) const privateKey = getPrivateKeyOrThrow() const { writeOrchestrator } = await import('./writeOrchestrator') writeOrchestrator.setPrivateKey(privateKey) const event = finalizeEvent(updatedEventTemplate, hexToBytes(privateKey)) const relays = await getPublishRelays() const result = await writeOrchestrator.writeAndPublish( { objectType: 'review', hash, event, parsed: updatedParsed, version: newVersion, hidden: false, index }, relays ) logRewardPublishResult({ reviewId, eventId: event.id, success: result.success }) } catch (error) { console.error('Error publishing reward event', { reviewId, error: error instanceof Error ? error.message : 'Unknown error', timestamp: new Date().toISOString(), }) } } function getPrivateKeyOrThrow(): string { const privateKey = nostrService.getPrivateKey() if (!privateKey) { throw new Error('Private key required for signing') } return privateKey } async function buildRewardedReviewUpdate(reviewId: string): Promise<{ updatedParsed: Review hash: string index: number newVersion: number }> { const originalEvent = await fetchOriginalReviewEvent(reviewId) if (!originalEvent) { throw new Error('Original review event not found in cache') } const { parseReviewFromEvent } = await import('./nostrEventParsing') const originalParsed = await parseReviewFromEvent(originalEvent) if (!originalParsed) { throw new Error('Failed to parse original review') } const newVersion = (originalParsed.version ?? 0) + 1 return { updatedParsed: buildRewardedParsedReview(originalParsed, newVersion), hash: originalParsed.hash, index: originalParsed.index ?? 0, newVersion, } } function buildRewardedParsedReview(originalParsed: Review, newVersion: number): Review { return { ...originalParsed, version: newVersion, rewarded: true, rewardAmount: PLATFORM_COMMISSIONS.review.total, } } function logRewardPublishResult(params: { reviewId: string; eventId: string; success: boolean }): void { if (params.success) { console.warn('Review updated with reward tag', { reviewId: params.reviewId, updatedEventId: params.eventId, timestamp: new Date().toISOString(), }) return } console.error('Failed to publish updated review event', { reviewId: params.reviewId, timestamp: new Date().toISOString(), }) } export async function updateReviewWithReward(reviewId: string, authorPrivateKey: string): Promise { try { const originalEvent = await fetchOriginalReviewEvent(reviewId) if (!originalEvent) { console.error('Original review event not found', { reviewId, timestamp: new Date().toISOString(), }) return } if (checkIfAlreadyRewarded(originalEvent, reviewId)) { return } nostrService.setPrivateKey(authorPrivateKey) nostrService.setPublicKey(originalEvent.pubkey) const updatedEvent = buildRewardEvent(originalEvent, reviewId) await publishRewardEvent(updatedEvent, reviewId) } catch (error) { console.error('Error updating review with reward', { reviewId, error: error instanceof Error ? error.message : 'Unknown error', timestamp: new Date().toISOString(), }) } }