substr/nostr.ts

116 lines
2.9 KiB
TypeScript

import { NostrEvent, NostrFilter, NPool, NRelay1 } from "@nostrify/nostrify";
import { nip19 } from "@nostr/tools";
import { lookupUsernameByPubkey } from "./directory.ts";
import config from "./config.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) {
const events = await fetchWithTimeout([{
authors: [pubkey],
kinds: [30023],
limit: 10,
}]) as NostrEvent[];
return events;
}
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 nostrUriToUrl(uri: string): Promise<string> {
const bech32 = uri.replace(/^nostr:/, "");
if (bech32.match(/^(naddr|nprofile|npub)/)) {
try {
const r = nip19.decode(bech32);
let username;
switch (r.type) {
case "naddr":
username = await lookupUsernameByPubkey(r.data.pubkey);
if (username) return `/${bech32}`;
break;
case "nprofile":
username = await lookupUsernameByPubkey(r.data.pubkey);
if (username) return `/@${username}`;
break;
case "npub":
username = await lookupUsernameByPubkey(r.data);
if (username) return `/@${username}`;
break;
}
} catch (e) {
console.error(e);
}
}
return `${config.njump_url}/${bech32}`;
}
export async function replaceNostrUris(markdown: string): Promise<string> {
const nostrUriRegex =
/\((nostr:|nprofile|naddr|nevent|nrelay|npub)[a-z0-9]+\)/g;
const matches = markdown.match(nostrUriRegex);
if (!matches) return markdown;
for (const match of matches) {
const uri = match.slice(1, -1);
const url = await nostrUriToUrl(uri);
markdown = markdown.replace(match, `(${url})`);
}
return markdown;
}