76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
import * as nip19 from "@nostr/tools/nip19";
|
|
import { loadRelayList } from "@nostr/gadgets/lists";
|
|
|
|
const RELAY_HINT_TIMEOUT_MS = 1500;
|
|
|
|
export function extractMentionPubkeys(content: string): string[] {
|
|
const re = /nostr:(npub1[a-z0-9]+|nprofile1[a-z0-9]+)/gi;
|
|
const out = new Set<string>();
|
|
for (const m of content.matchAll(re)) {
|
|
try {
|
|
const decoded = nip19.decode(m[1]);
|
|
if (decoded.type === "npub") out.add(decoded.data);
|
|
else if (decoded.type === "nprofile") out.add(decoded.data.pubkey);
|
|
} catch {
|
|
// Invalid bech32, skip
|
|
}
|
|
}
|
|
return [...out];
|
|
}
|
|
|
|
export type QuotedEvent = {
|
|
id: string;
|
|
relay?: string;
|
|
author?: string;
|
|
};
|
|
|
|
export function extractQuotedEvents(content: string): QuotedEvent[] {
|
|
const re = /nostr:(note1[a-z0-9]+|nevent1[a-z0-9]+)/gi;
|
|
const seen = new Map<string, QuotedEvent>();
|
|
for (const m of content.matchAll(re)) {
|
|
try {
|
|
const decoded = nip19.decode(m[1]);
|
|
if (decoded.type === "note") {
|
|
if (!seen.has(decoded.data))
|
|
seen.set(decoded.data, { id: decoded.data });
|
|
} else if (decoded.type === "nevent") {
|
|
const { id, relays, author } = decoded.data;
|
|
if (!seen.has(id)) seen.set(id, { id, relay: relays?.[0], author });
|
|
}
|
|
} catch {
|
|
// Invalid bech32, skip
|
|
}
|
|
}
|
|
return [...seen.values()];
|
|
}
|
|
|
|
export async function relayHintFor(
|
|
pubkey: string,
|
|
): Promise<string | undefined> {
|
|
try {
|
|
const TIMEOUT = Symbol();
|
|
const result = await Promise.race([
|
|
loadRelayList(pubkey),
|
|
new Promise<typeof TIMEOUT>((resolve) =>
|
|
setTimeout(() => resolve(TIMEOUT), RELAY_HINT_TIMEOUT_MS),
|
|
),
|
|
]);
|
|
if (result === TIMEOUT) return undefined;
|
|
return result.items.find((r) => r.write)?.url;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export async function buildPTagHints(
|
|
pubkeys: Iterable<string>,
|
|
): Promise<Map<string, string>> {
|
|
const hints = new Map<string, string>();
|
|
await Promise.all(
|
|
[...pubkeys].map(async (pk) => {
|
|
const url = await relayHintFor(pk);
|
|
if (url) hints.set(pk, url);
|
|
}),
|
|
);
|
|
return hints;
|
|
}
|