169 lines
5.2 KiB
TypeScript
169 lines
5.2 KiB
TypeScript
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<NostrEvent | null> {
|
|
// 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<void> {
|
|
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<void> {
|
|
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(),
|
|
})
|
|
}
|
|
}
|