import { DittoConf } from '@ditto/conf'; import { DittoTables } from '@ditto/db'; import { NostrEvent, NSchema as n, NStore } from '@nostrify/nostrify'; import { Kysely } from 'kysely'; import { nip19 } from 'nostr-tools'; import { match } from 'path-to-regexp'; import tldts from 'tldts'; import { getAuthor } from '@/queries.ts'; import { bech32ToPubkey } from '@/utils.ts'; import { resolveNip05 } from './nip05.ts'; interface LookupAccountOpts { conf: DittoConf; store: NStore; kysely: Kysely; signal?: AbortSignal; } /** Resolve a bech32 or NIP-05 identifier to an account. */ export async function lookupAccount( opts: LookupAccountOpts, value: string, ): Promise { const pubkey = await lookupPubkey(opts, value); if (pubkey) { return getAuthor(opts, pubkey); } } interface LookupPubkeyOpts { conf: DittoConf; store: NStore; kysely: Kysely; signal?: AbortSignal; } /** Resolve a bech32 or NIP-05 identifier to a pubkey. */ export async function lookupPubkey(opts: LookupPubkeyOpts, value: string): Promise { if (n.bech32().safeParse(value).success) { return bech32ToPubkey(value); } try { const { pubkey } = await resolveNip05(opts, value); return pubkey; } catch { return; } } /** Extract an acct or bech32 identifier out of a URL or of itself. */ export function extractIdentifier(value: string): string | undefined { value = value.trim(); try { const uri = new URL(value); switch (uri.protocol) { // Extract from NIP-19 URI, eg `nostr:npub1q3sle0kvfsehgsuexttt3ugjd8xdklxfwwkh559wxckmzddywnws6cd26p`. case 'nostr:': value = uri.pathname; break; // Extract from URL, eg `https://njump.me/npub1q3sle0kvfsehgsuexttt3ugjd8xdklxfwwkh559wxckmzddywnws6cd26p`. case 'http:': case 'https:': { const accountUriMatch = match<{ acct: string }>('/users/:acct')(uri.pathname); const accountUrlMatch = match<{ acct: string }>('/\\@:acct')(uri.pathname); const statusUriMatch = match<{ acct: string; id: string }>('/users/:acct/statuses/:id')(uri.pathname); const statusUrlMatch = match<{ acct: string; id: string }>('/\\@:acct/:id')(uri.pathname); const soapboxMatch = match<{ acct: string; id: string }>('/\\@:acct/posts/:id')(uri.pathname); const nostrMatch = match<{ bech32: string }>('/:bech32')(uri.pathname); if (accountUriMatch) { value = accountUriMatch.params.acct; } else if (accountUrlMatch) { value = accountUrlMatch.params.acct; } else if (statusUriMatch) { value = nip19.noteEncode(statusUriMatch.params.id); } else if (statusUrlMatch) { value = nip19.noteEncode(statusUrlMatch.params.id); } else if (soapboxMatch) { value = nip19.noteEncode(soapboxMatch.params.id); } else if (nostrMatch) { value = nostrMatch.params.bech32; } break; } } } catch { // fall through } value = value.replace(/^@/, ''); if (n.bech32().safeParse(value).success) { return value; } const { isIcann, domain } = tldts.parse(value); if (isIcann && domain) { return value; } }