202 lines
5.5 KiB
TypeScript
202 lines
5.5 KiB
TypeScript
import type { Event } from 'nostr-tools'
|
|
import { nostrService } from './nostr'
|
|
import type { ReviewTip } from '@/types/nostr'
|
|
import { parseReviewTipFromEvent } from './nostrEventParsing'
|
|
import { objectCache } from './objectCache'
|
|
import { getPrimaryRelaySync } from './config'
|
|
import { MIN_EVENT_DATE } from './platformConfig'
|
|
import { parseObjectId } from './urlGenerator'
|
|
|
|
function buildReviewTipFilters(articleId?: string, reviewId?: string, authorPubkey?: string, reviewerPubkey?: string): Array<{
|
|
kinds: number[]
|
|
since?: number
|
|
'#p'?: string[]
|
|
'#e'?: string[]
|
|
'#review_id'?: string[]
|
|
'#reviewer'?: string[]
|
|
'#kind_type'?: string[]
|
|
}> {
|
|
const filters: Array<{
|
|
kinds: number[]
|
|
since?: number
|
|
'#p'?: string[]
|
|
'#e'?: string[]
|
|
'#review_id'?: string[]
|
|
'#reviewer'?: string[]
|
|
'#kind_type'?: string[]
|
|
}> = []
|
|
|
|
const baseFilter: {
|
|
kinds: number[]
|
|
since: number
|
|
'#kind_type': string[]
|
|
'#p'?: string[]
|
|
'#e'?: string[]
|
|
'#review_id'?: string[]
|
|
'#reviewer'?: string[]
|
|
} = {
|
|
kinds: [9735], // Zap receipt
|
|
since: MIN_EVENT_DATE,
|
|
'#kind_type': ['review_tip'],
|
|
}
|
|
|
|
if (authorPubkey) {
|
|
baseFilter['#p'] = [authorPubkey]
|
|
}
|
|
|
|
if (articleId) {
|
|
baseFilter['#e'] = [articleId]
|
|
}
|
|
|
|
if (reviewId) {
|
|
baseFilter['#review_id'] = [reviewId]
|
|
}
|
|
|
|
if (reviewerPubkey) {
|
|
baseFilter['#reviewer'] = [reviewerPubkey]
|
|
}
|
|
|
|
filters.push(baseFilter)
|
|
return filters
|
|
}
|
|
|
|
export async function getReviewTipById(reviewTipId: string, timeoutMs: number = 5000): Promise<ReviewTip | null> {
|
|
const parsed = parseObjectId(reviewTipId)
|
|
const hash = parsed.hash ?? reviewTipId
|
|
|
|
// Check cache first
|
|
const cached = await objectCache.get('review_tip', hash)
|
|
if (cached) {
|
|
return cached as ReviewTip
|
|
}
|
|
|
|
const pool = nostrService.getPool()
|
|
if (!pool) {
|
|
throw new Error('Pool not initialized')
|
|
}
|
|
|
|
const filters = buildReviewTipFilters()
|
|
const { createSubscription } = require('@/types/nostr-tools-extended')
|
|
|
|
return new Promise<ReviewTip | null>((resolve) => {
|
|
const relayUrl = getPrimaryRelaySync()
|
|
const sub = createSubscription(pool, [relayUrl], filters)
|
|
let finished = false
|
|
|
|
const done = async (value: ReviewTip | null): Promise<void> => {
|
|
if (finished) {
|
|
return
|
|
}
|
|
finished = true
|
|
sub.unsub()
|
|
resolve(value)
|
|
}
|
|
|
|
sub.on('event', (event: Event): void => {
|
|
void (async (): Promise<void> => {
|
|
const reviewTipParsed = await parseReviewTipFromEvent(event)
|
|
if (reviewTipParsed?.id === reviewTipId) {
|
|
// Cache the parsed review tip
|
|
if (reviewTipParsed.hash) {
|
|
await objectCache.set('review_tip', reviewTipParsed.hash, event, reviewTipParsed, 0, false, reviewTipParsed.index ?? 0)
|
|
}
|
|
done(reviewTipParsed)
|
|
}
|
|
})()
|
|
})
|
|
|
|
sub.on('eose', (): void => {
|
|
void done(null)
|
|
})
|
|
setTimeout(() => done(null), timeoutMs).unref?.()
|
|
})
|
|
}
|
|
|
|
export function getReviewTipsForArticle(articleId: string, timeoutMs: number = 5000): Promise<ReviewTip[]> {
|
|
const pool = nostrService.getPool()
|
|
if (!pool) {
|
|
throw new Error('Pool not initialized')
|
|
}
|
|
|
|
const filters = buildReviewTipFilters(articleId)
|
|
const { createSubscription } = require('@/types/nostr-tools-extended')
|
|
|
|
return new Promise<ReviewTip[]>((resolve) => {
|
|
const results: ReviewTip[] = []
|
|
const relayUrl = getPrimaryRelaySync()
|
|
const sub = createSubscription(pool, [relayUrl], filters)
|
|
let finished = false
|
|
|
|
const done = async (): Promise<void> => {
|
|
if (finished) {
|
|
return
|
|
}
|
|
finished = true
|
|
sub.unsub()
|
|
resolve(results)
|
|
}
|
|
|
|
sub.on('event', (event: Event): void => {
|
|
void (async (): Promise<void> => {
|
|
const reviewTipParsed = await parseReviewTipFromEvent(event)
|
|
if (reviewTipParsed?.articleId === articleId) {
|
|
// Cache the parsed review tip
|
|
if (reviewTipParsed.hash) {
|
|
await objectCache.set('review_tip', reviewTipParsed.hash, event, reviewTipParsed, 0, false, reviewTipParsed.index ?? 0)
|
|
}
|
|
results.push(reviewTipParsed)
|
|
}
|
|
})()
|
|
})
|
|
|
|
sub.on('eose', (): void => {
|
|
void done()
|
|
})
|
|
setTimeout(() => done(), timeoutMs).unref?.()
|
|
})
|
|
}
|
|
|
|
export function getReviewTipsForReview(reviewId: string, timeoutMs: number = 5000): Promise<ReviewTip[]> {
|
|
const pool = nostrService.getPool()
|
|
if (!pool) {
|
|
throw new Error('Pool not initialized')
|
|
}
|
|
|
|
const filters = buildReviewTipFilters(undefined, reviewId)
|
|
const { createSubscription } = require('@/types/nostr-tools-extended')
|
|
|
|
return new Promise<ReviewTip[]>((resolve) => {
|
|
const results: ReviewTip[] = []
|
|
const relayUrl = getPrimaryRelaySync()
|
|
const sub = createSubscription(pool, [relayUrl], filters)
|
|
let finished = false
|
|
|
|
const done = async (): Promise<void> => {
|
|
if (finished) {
|
|
return
|
|
}
|
|
finished = true
|
|
sub.unsub()
|
|
resolve(results)
|
|
}
|
|
|
|
sub.on('event', (event: Event): void => {
|
|
void (async (): Promise<void> => {
|
|
const reviewTipParsed = await parseReviewTipFromEvent(event)
|
|
if (reviewTipParsed?.reviewId === reviewId) {
|
|
// Cache the parsed review tip
|
|
if (reviewTipParsed.hash) {
|
|
await objectCache.set('review_tip', reviewTipParsed.hash, event, reviewTipParsed, 0, false, reviewTipParsed.index ?? 0)
|
|
}
|
|
results.push(reviewTipParsed)
|
|
}
|
|
})()
|
|
})
|
|
|
|
sub.on('eose', (): void => {
|
|
void done()
|
|
})
|
|
setTimeout(() => done(), timeoutMs).unref?.()
|
|
})
|
|
}
|