diff --git a/src/transmute.ts b/src/transmute.ts index 15b5897..78dbf8c 100644 --- a/src/transmute.ts +++ b/src/transmute.ts @@ -4,6 +4,7 @@ import { type MetaContent, parseContent } from '@/schema.ts'; import { LOCAL_DOMAIN } from './config.ts'; import { getAuthor } from './client.ts'; +import { type Nip05, parseNip05 } from './utils.ts'; const DEFAULT_AVATAR = 'https://gleasonator.com/images/avi.png'; @@ -13,9 +14,16 @@ function toAccount(event: Event<0>) { const { origin } = new URL(LOCAL_DOMAIN); const npub = nip19.npubEncode(pubkey); + let parsed05: Nip05 | undefined; + try { + parsed05 = parseNip05(nip05!); + } catch (_e) { + // + } + return { id: pubkey, - acct: nip05 || npub, + acct: parsed05?.handle || npub, avatar: picture || DEFAULT_AVATAR, avatar_static: picture || DEFAULT_AVATAR, bot: false, @@ -31,9 +39,9 @@ function toAccount(event: Event<0>) { header_static: banner, locked: false, note: about, - fqn: nip05 || npub, + fqn: parsed05?.handle || npub, url: `${origin}/users/${pubkey}`, - username: nip05 ? nip05.split('@')[0] : npub, + username: parsed05?.nickname || npub, }; } diff --git a/src/utils.ts b/src/utils.ts index 2b15583..5e6ce9e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -47,4 +47,32 @@ function bech32ToPubkey(bech32: string): string | undefined { } } -export { bech32ToPubkey, eventDateComparator, getKeys, isBech32, isNostrId, nostrNow }; +interface Nip05 { + /** Localpart of the nip05, eg `alex` in `alex@alexgleason.me`. */ + local: string | undefined; + /** Domain of the nip05, eg `alexgleason.me` in `alex@alexgleason.me`. */ + domain: string; + /** Value with underscore removed, eg `_@fiatjaf.com` becomes `fiatjaf.com`, but `alex@alexgleason.me` stays the same. */ + handle: string; + /** The localpart, if available and not `_`. Otherwise the domain. */ + nickname: string; +} + +/** + * Parse a NIP-05 identifier and return an object with metadata about it. + * Throws if the value is not a valid NIP-05 identifier. + */ +function parseNip05(value: string): Nip05 { + const match = value.match(/^(?:([\w.+-]+)@)?([\w.-]+)$/i); + if (!match) throw new Error(`nip05: failed to parse ${value}`); + + const [_, local, domain] = match; + return { + local, + domain, + handle: local === '_' ? domain : value, + nickname: (local && local !== '_') ? local : domain, + }; +} + +export { bech32ToPubkey, eventDateComparator, getKeys, isBech32, isNostrId, type Nip05, nostrNow, parseNip05 };