Files
boris/src/services/articleTitleResolver.ts

92 lines
2.6 KiB
TypeScript

import { RelayPool } from 'applesauce-relay'
import { lastValueFrom, take } from 'rxjs'
import { nip19 } from 'nostr-tools'
import { AddressPointer } from 'nostr-tools/nip19'
import { Helpers } from 'applesauce-core'
import { RELAYS } from '../config/relays'
import { prioritizeLocalRelays, partitionRelays, createParallelReqStreams } from '../utils/helpers'
import { merge, toArray as rxToArray } from 'rxjs'
const { getArticleTitle } = Helpers
/**
* Fetch article title for a single naddr
* Returns the title or null if not found/error
*/
export async function fetchArticleTitle(
relayPool: RelayPool,
naddr: string
): Promise<string | null> {
try {
const decoded = nip19.decode(naddr)
if (decoded.type !== 'naddr') {
return null
}
const pointer = decoded.data as AddressPointer
// Define relays to query
const baseRelays = pointer.relays && pointer.relays.length > 0
? pointer.relays
: RELAYS
const orderedRelays = prioritizeLocalRelays(baseRelays)
const { local: localRelays, remote: remoteRelays } = partitionRelays(orderedRelays)
// Fetch the article event
const filter = {
kinds: [pointer.kind],
authors: [pointer.pubkey],
'#d': [pointer.identifier]
}
// Parallel local+remote: collect up to one event from each
const { local$, remote$ } = createParallelReqStreams(relayPool, localRelays, remoteRelays, filter, 1200, 5000)
const events = await lastValueFrom(
merge(local$.pipe(take(1)), remote$.pipe(take(1))).pipe(rxToArray())
) as unknown as { created_at: number }[]
if (events.length === 0) {
return null
}
// Sort by created_at and take the most recent
events.sort((a, b) => b.created_at - a.created_at)
const article = events[0] as unknown as Parameters<typeof getArticleTitle>[0]
return getArticleTitle(article) || null
} catch (err) {
console.warn('Failed to fetch article title for', naddr, err)
return null
}
}
/**
* Fetch titles for multiple naddrs in parallel
* Returns a map of naddr -> title
*/
export async function fetchArticleTitles(
relayPool: RelayPool,
naddrs: string[]
): Promise<Map<string, string>> {
const titleMap = new Map<string, string>()
// Fetch all titles in parallel
const results = await Promise.allSettled(
naddrs.map(async (naddr) => {
const title = await fetchArticleTitle(relayPool, naddr)
return { naddr, title }
})
)
// Process results
results.forEach((result) => {
if (result.status === 'fulfilled' && result.value.title) {
titleMap.set(result.value.naddr, result.value.title)
}
})
return titleMap
}