rewrite metadata generation

This commit is contained in:
Siddharth Singh
2024-08-05 20:04:26 +05:30
parent 5785f07052
commit 33da9a41b2
2 changed files with 65 additions and 129 deletions

View File

@@ -1,8 +1,9 @@
import { NostrMetadata, NSchema as n } from '@nostrify/nostrify';
import { getAuthor, getEvent } from '@/queries.ts';
import { nip05Cache } from '@/utils/nip05.ts';
import { getEvent } from '@/queries.ts';
import { match } from 'path-to-regexp';
import { nip19 } from 'nostr-tools';
import { lookupAccount, lookupPubkey } from '@/utils/lookup.ts';
import { parseAndVerifyNip05 } from '@/utils/nip05.ts';
export interface OpenGraphTemplateOpts {
title: string;
@@ -16,6 +17,7 @@ export interface OpenGraphTemplateOpts {
export type PathParams = Partial<Record<'statusId' | 'acct' | 'note' | 'nevent' | 'nprofile' | 'npub', string>>;
interface StatusInfo {
title: string;
description: string;
image?: {
url: string;
@@ -65,70 +67,57 @@ export function getPathParams(path: string) {
}
}
async function urlParamToPubkey(handle: string) {
const id = `${handle}`;
const parts = id.match(/(?:(.+)@)?(.+)/);
if (parts) {
const key = `${parts[1] ? (parts[1] + '@') : ''}${parts[2]}`;
return await nip05Cache.fetch(key, { signal: AbortSignal.timeout(1000) }).then((res) => res.pubkey);
} else if (id.startsWith('npub1')) {
return nip19.decode(id as `npub1${string}`).data;
} else if (/(?:[0-9]|[a-f]){64}/.test(id)) {
return id;
}
type ProfileInfo = { name: string; about: string } & NostrMetadata;
// shouldn't ever happen for a well-formed link
return '';
/**
* Look up the name and bio of a user for use in generating OpenGraph metadata.
*
* @param handle The bech32 / nip05 identifier for the user, obtained from the URL.
* @returns An object containing the `name` and `about` fields of the user's kind 0,
* or sensible defaults if the kind 0 has those values missing.
*/
export async function getProfileInfo(handle: string | undefined): Promise<ProfileInfo> {
const acc = await lookupAccount(handle || '');
if (!acc) throw new Error('Invalid handle specified, or account not found.');
const short = nip19.npubEncode(acc.id).slice(0, 8);
const { name = short, about = `@${short}'s Nostr profile` } = n.json().pipe(n.metadata()).parse(acc.content);
return { name, about };
}
export async function getProfileInfo(handle: string | undefined): Promise<NostrMetadata> {
const id = await urlParamToPubkey(handle || '');
const kind0 = await getAuthor(id);
const short = nip19.npubEncode(id).substring(0, 8);
const blank = { name: short, about: `@${short}'s ditto profile` };
if (!kind0) return blank;
return Object.assign(
blank,
n.json().pipe(n.metadata()).parse(kind0.content),
);
}
const truncate = (s: string, len: number, ellipsis = '...') => {
function truncate(s: string, len: number, ellipsis = '…') {
if (s.length <= len) return s;
return s.slice(0, len) + ellipsis;
};
}
export async function getStatusInfo(id: string | undefined, handle?: string): Promise<StatusInfo> {
const event = await getEvent(id || '');
if (!event || !id) {
return { description: `A post on Ditto by @${handle}` };
}
export async function getHandle(id: string, name?: string | undefined) {
const pubkey = /[a-z][0-9]{64}/.test(id) ? id : await lookupPubkey(id);
if (!pubkey) throw new Error('Invalid user identifier');
const parsed = await parseAndVerifyNip05(id, pubkey);
return parsed?.handle || name || 'npub1xxx';
}
export async function getStatusInfo(id: string): Promise<StatusInfo> {
const event = await getEvent(id);
if (!id || !event) throw new Error('Invalid post id supplied');
const handle = await getHandle(event.pubkey);
const res: StatusInfo = {
title: `View @${handle}'s post on Ditto`,
description: event.content
.replace(/nostr:(npub1(?:[0-9]|[a-z]){58})/g, (_, key: string) => `@${key.slice(0, 8)}`),
};
let url: string;
let w: number;
let h: number;
const data: string[][] = event.tags
.find(([name]) => name === 'imeta')?.slice(1)
.map((entry: string) => entry.split(' ')) ?? [];
for (const [tag, ...values] of event.tags) {
if (tag !== 'imeta') continue;
for (const value of values) {
const [item, datum] = value.split(' ');
if (!['dim', 'url'].includes(item)) continue;
if (item === 'dim') {
[w, h] = datum.split('x').map(Number);
} else if (item === 'url') {
url = datum;
}
}
}
const url = data.find(([name]) => name === 'url')?.[1];
const dim = data.find(([name]) => name === 'dim')?.[1];
const [w, h] = dim?.split('x').map(Number) ?? [null, null];
// @ts-ignore conditional assign
if (url && w && h) {
res.image = { url, w, h };
res.description = res.description.replace(url.trim(), '');