import { NostrEvent, NostrFilter, NPool, NRelay1 } from "@nostrify/nostrify"; import config from "./config.ts"; import Article from "./models/article.ts"; const relayPool = new NPool({ open: (url) => new NRelay1(url), // deno-lint-ignore require-await reqRouter: async (filters) => new Map( config.relay_urls.map((url) => [url, filters]), ), // deno-lint-ignore require-await eventRouter: async (_event) => [], }); async function fetchWithTimeout(filters: NostrFilter[]) { const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("relay timeout")), config.query_timeout) ); const eventsPromise = relayPool.query(filters); const events = await Promise.race([eventsPromise, timeoutPromise]); return events; } export async function fetchReplaceableEvent( pubkey: string, identifier: string, ) { let events = await fetchWithTimeout([{ authors: [pubkey], kinds: [30023], "#d": [identifier], limit: 1, }]) as NostrEvent[]; if (events.length > 0) { return events[0]; } else { events = await fetchWithTimeout([{ authors: [pubkey], kinds: [30024], "#d": [identifier], limit: 1, }]) as NostrEvent[]; return events.length > 0 ? events[0] : null; } } export async function fetchArticlesByAuthor( pubkey: string, limit: number = 10, ) { const events = await fetchWithTimeout([{ authors: [pubkey], kinds: [30023], limit: limit, }]) as NostrEvent[]; const articles = events.map((a) => new Article(a)); return articles .filter((a) => !a.isDeleted) .filter((a) => a.content.trim() !== "") .sort((a, b) => b.publishedAt - a.publishedAt) .slice(0, limit); // The limit seems to apply per relay, not per pool query } export async function fetchProfileEvent(pubkey: string) { const events = await fetchWithTimeout([{ authors: [pubkey], kinds: [0], limit: 1, }]) as NostrEvent[]; return events.length > 0 ? events[0] : null; } export async function verifyNip05Address( address: string, pubkey: string, ): Promise { const [username, host] = address.split("@"); const url = `https://${host}/.well-known/nostr.json?name=${username}`; try { const res = await fetch(url); if (res.status === 404 || !res.ok) return false; const data = await res.json(); return data.names && data.names[username] === pubkey; } catch (_e) { return false; } }