Merge branch 'explicit-addressing' into 'main'
Support Explicit Addressing See merge request soapbox-pub/ditto!246
This commit is contained in:
commit
4b7804872c
|
@ -1,6 +1,5 @@
|
||||||
import { NIP05, NostrEvent, NostrFilter, NSchema as n } from '@nostrify/nostrify';
|
import { NostrEvent, NostrFilter, NSchema as n } from '@nostrify/nostrify';
|
||||||
import ISO6391 from 'iso-639-1';
|
import ISO6391 from 'iso-639-1';
|
||||||
import { nip19 } from 'nostr-tools';
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { type AppController } from '@/app.ts';
|
import { type AppController } from '@/app.ts';
|
||||||
|
@ -12,10 +11,10 @@ import { createEvent, paginationSchema, parseBody, updateListEvent } from '@/uti
|
||||||
import { renderEventAccounts } from '@/views.ts';
|
import { renderEventAccounts } from '@/views.ts';
|
||||||
import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts';
|
import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts';
|
||||||
import { getLnurl } from '@/utils/lnurl.ts';
|
import { getLnurl } from '@/utils/lnurl.ts';
|
||||||
import { nip05Cache } from '@/utils/nip05.ts';
|
|
||||||
import { asyncReplaceAll } from '@/utils/text.ts';
|
import { asyncReplaceAll } from '@/utils/text.ts';
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
import { hydrateEvents } from '@/storages/hydrate.ts';
|
import { hydrateEvents } from '@/storages/hydrate.ts';
|
||||||
|
import { lookupPubkey } from '@/utils/lookup.ts';
|
||||||
|
|
||||||
const createStatusSchema = z.object({
|
const createStatusSchema = z.object({
|
||||||
in_reply_to_id: z.string().regex(/[0-9a-f]{64}/).nullish(),
|
in_reply_to_id: z.string().regex(/[0-9a-f]{64}/).nullish(),
|
||||||
|
@ -31,6 +30,7 @@ const createStatusSchema = z.object({
|
||||||
sensitive: z.boolean().nullish(),
|
sensitive: z.boolean().nullish(),
|
||||||
spoiler_text: z.string().nullish(),
|
spoiler_text: z.string().nullish(),
|
||||||
status: z.string().nullish(),
|
status: z.string().nullish(),
|
||||||
|
to: z.string().array().nullish(),
|
||||||
visibility: z.enum(['public', 'unlisted', 'private', 'direct']).nullish(),
|
visibility: z.enum(['public', 'unlisted', 'private', 'direct']).nullish(),
|
||||||
quote_id: z.string().nullish(),
|
quote_id: z.string().nullish(),
|
||||||
}).refine(
|
}).refine(
|
||||||
|
@ -97,30 +97,32 @@ const createStatusController: AppController = async (c) => {
|
||||||
tags.push(...media);
|
tags.push(...media);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pubkeys = new Set<string>();
|
||||||
|
|
||||||
const content = await asyncReplaceAll(data.status ?? '', /@([\w@+._]+)/g, async (match, username) => {
|
const content = await asyncReplaceAll(data.status ?? '', /@([\w@+._]+)/g, async (match, username) => {
|
||||||
try {
|
const pubkey = await lookupPubkey(username);
|
||||||
const result = nip19.decode(username);
|
if (!pubkey) return match;
|
||||||
if (result.type === 'npub') {
|
|
||||||
tags.push(['p', result.data]);
|
// Content addressing (default)
|
||||||
return `nostr:${username}`;
|
if (!data.to) {
|
||||||
} else {
|
pubkeys.add(pubkey);
|
||||||
return match;
|
|
||||||
}
|
|
||||||
} catch (_e) {
|
|
||||||
// do nothing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NIP05.regex().test(username)) {
|
return `nostr:${pubkey}`;
|
||||||
const pointer = await nip05Cache.fetch(username);
|
|
||||||
if (pointer) {
|
|
||||||
tags.push(['p', pointer.pubkey]);
|
|
||||||
return `nostr:${nip19.npubEncode(pointer.pubkey)}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return match;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Explicit addressing
|
||||||
|
for (const to of data.to ?? []) {
|
||||||
|
const pubkey = await lookupPubkey(to);
|
||||||
|
if (pubkey) {
|
||||||
|
pubkeys.add(pubkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const pubkey of pubkeys) {
|
||||||
|
tags.push(['p', pubkey]);
|
||||||
|
}
|
||||||
|
|
||||||
for (const match of content.matchAll(/#(\w+)/g)) {
|
for (const match of content.matchAll(/#(\w+)/g)) {
|
||||||
tags.push(['t', match[1]]);
|
tags.push(['t', match[1]]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,31 @@
|
||||||
import { NostrEvent } from '@nostrify/nostrify';
|
import { NIP05, NostrEvent } from '@nostrify/nostrify';
|
||||||
|
|
||||||
import { getAuthor } from '@/queries.ts';
|
import { getAuthor } from '@/queries.ts';
|
||||||
import { bech32ToPubkey } from '@/utils.ts';
|
import { bech32ToPubkey } from '@/utils.ts';
|
||||||
import { nip05Cache } from '@/utils/nip05.ts';
|
import { nip05Cache } from '@/utils/nip05.ts';
|
||||||
|
|
||||||
/** Resolve a bech32 or NIP-05 identifier to an account. */
|
/** Resolve a bech32 or NIP-05 identifier to an account. */
|
||||||
async function lookupAccount(value: string, signal = AbortSignal.timeout(3000)): Promise<NostrEvent | undefined> {
|
export async function lookupAccount(
|
||||||
console.log(`Looking up ${value}`);
|
value: string,
|
||||||
|
signal = AbortSignal.timeout(3000),
|
||||||
const pubkey = bech32ToPubkey(value) ||
|
): Promise<NostrEvent | undefined> {
|
||||||
await nip05Cache.fetch(value, { signal }).then(({ pubkey }) => pubkey).catch(() => undefined);
|
const pubkey = await lookupPubkey(value, signal);
|
||||||
|
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
return getAuthor(pubkey);
|
return getAuthor(pubkey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { lookupAccount };
|
/** Resolve a bech32 or NIP-05 identifier to a pubkey. */
|
||||||
|
export async function lookupPubkey(value: string, signal?: AbortSignal): Promise<string | undefined> {
|
||||||
|
if (NIP05.regex().test(value)) {
|
||||||
|
try {
|
||||||
|
const { pubkey } = await nip05Cache.fetch(value, { signal });
|
||||||
|
return pubkey;
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bech32ToPubkey(value);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue