From 7a60b4b8d80101f483d4a14aa612457bb80f6c6a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 5 Jan 2025 11:23:18 -0600 Subject: [PATCH] Support kind 20 "Picture" events (NIP-68) --- scripts/nostr-pull.ts | 2 +- src/config.ts | 2 +- src/controllers/api/accounts.ts | 4 ++-- src/controllers/api/ditto.ts | 2 +- src/controllers/api/reactions.ts | 4 ++-- src/controllers/api/search.ts | 6 +++--- src/controllers/api/statuses.ts | 6 +++--- src/controllers/api/streaming.ts | 10 +++++----- src/controllers/api/timelines.ts | 8 ++++---- src/controllers/api/trends.ts | 2 +- src/storages/hydrate.ts | 12 ++++++------ src/utils/stats.ts | 2 +- src/views.ts | 2 +- 13 files changed, 31 insertions(+), 31 deletions(-) diff --git a/scripts/nostr-pull.ts b/scripts/nostr-pull.ts index 573b5f01..c7ad21d3 100644 --- a/scripts/nostr-pull.ts +++ b/scripts/nostr-pull.ts @@ -47,7 +47,7 @@ const importUsers = async ( if (!profilesOnly) { matched.push( ...await conn.query( - authors.map((author) => ({ kinds: [1], authors: [author], limit: 200 })), + authors.map((author) => ({ kinds: [1, 20], authors: [author], limit: 200 })), ), ); } diff --git a/src/config.ts b/src/config.ts index 68bf3ed8..3c1e8923 100644 --- a/src/config.ts +++ b/src/config.ts @@ -252,7 +252,7 @@ class Conf { } /** Nostr event kinds of events to listen for on the firehose. */ static get firehoseKinds(): number[] { - return (Deno.env.get('FIREHOSE_KINDS') ?? '0, 1, 3, 5, 6, 7, 9735, 10002') + return (Deno.env.get('FIREHOSE_KINDS') ?? '0, 1, 3, 5, 6, 7, 20, 9735, 10002') .split(/[, ]+/g) .map(Number); } diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index 03284a15..6705924e 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -234,7 +234,7 @@ const accountStatusesController: AppController = async (c) => { const filter: NostrFilter = { authors: [pubkey], - kinds: [1, 6], + kinds: [1, 6, 20], since, until, limit, @@ -473,7 +473,7 @@ const favouritesController: AppController = async (c) => { .map((event) => event.tags.find((tag) => tag[0] === 'e')?.[1]) .filter((id): id is string => !!id); - const events1 = await store.query([{ kinds: [1], ids }], { signal }) + const events1 = await store.query([{ kinds: [1, 20], ids }], { signal }) .then((events) => hydrateEvents({ events, store, signal })); const viewerPubkey = await c.get('signer')?.getPublicKey(); diff --git a/src/controllers/api/ditto.ts b/src/controllers/api/ditto.ts index 765862ec..3a1ce98d 100644 --- a/src/controllers/api/ditto.ts +++ b/src/controllers/api/ditto.ts @@ -260,7 +260,7 @@ export const statusZapSplitsController: AppController = async (c) => { const id = c.req.param('id'); const { signal } = c.req.raw; - const [event] = await store.query([{ kinds: [1], ids: [id], limit: 1 }], { signal }); + const [event] = await store.query([{ kinds: [1, 20], ids: [id], limit: 1 }], { signal }); if (!event) { return c.json({ error: 'Event not found' }, 404); } diff --git a/src/controllers/api/reactions.ts b/src/controllers/api/reactions.ts index 7d42c197..0beb985d 100644 --- a/src/controllers/api/reactions.ts +++ b/src/controllers/api/reactions.ts @@ -20,7 +20,7 @@ const reactionController: AppController = async (c) => { } const store = await Storages.db(); - const [event] = await store.query([{ kinds: [1], ids: [id], limit: 1 }]); + const [event] = await store.query([{ kinds: [1, 20], ids: [id], limit: 1 }]); if (!event) { return c.json({ error: 'Status not found' }, 404); @@ -56,7 +56,7 @@ const deleteReactionController: AppController = async (c) => { } const [event] = await store.query([ - { kinds: [1], ids: [id], limit: 1 }, + { kinds: [1, 20], ids: [id], limit: 1 }, ]); if (!event) { diff --git a/src/controllers/api/search.ts b/src/controllers/api/search.ts index 4c3aa75f..8bfe4ffd 100644 --- a/src/controllers/api/search.ts +++ b/src/controllers/api/search.ts @@ -166,7 +166,7 @@ async function getLookupFilters({ q, type, resolve }: SearchQuery, signal: Abort if (n.id().safeParse(q).success) { const filters: NostrFilter[] = []; if (accounts) filters.push({ kinds: [0], authors: [q] }); - if (statuses) filters.push({ kinds: [1], ids: [q] }); + if (statuses) filters.push({ kinds: [1, 20], ids: [q] }); return filters; } @@ -184,10 +184,10 @@ async function getLookupFilters({ q, type, resolve }: SearchQuery, signal: Abort if (accounts) filters.push({ kinds: [0], authors: [result.data.pubkey] }); break; case 'note': - if (statuses) filters.push({ kinds: [1], ids: [result.data] }); + if (statuses) filters.push({ kinds: [1, 20], ids: [result.data] }); break; case 'nevent': - if (statuses) filters.push({ kinds: [1], ids: [result.data.id] }); + if (statuses) filters.push({ kinds: [1, 20], ids: [result.data.id] }); break; } return filters; diff --git a/src/controllers/api/statuses.ts b/src/controllers/api/statuses.ts index c27b9a74..d466b551 100644 --- a/src/controllers/api/statuses.ts +++ b/src/controllers/api/statuses.ts @@ -397,7 +397,7 @@ const unreblogStatusController: AppController = async (c) => { const pubkey = await c.get('signer')?.getPublicKey()!; const store = await Storages.db(); - const [event] = await store.query([{ ids: [eventId], kinds: [1] }]); + const [event] = await store.query([{ ids: [eventId], kinds: [1, 20] }]); if (!event) { return c.json({ error: 'Record not found' }, 404); } @@ -429,13 +429,13 @@ const quotesController: AppController = async (c) => { const params = c.get('pagination'); const store = await Storages.db(); - const [event] = await store.query([{ ids: [id], kinds: [1] }]); + const [event] = await store.query([{ ids: [id], kinds: [1, 20] }]); if (!event) { return c.json({ error: 'Event not found.' }, 404); } const quotes = await store - .query([{ kinds: [1], '#q': [event.id], ...params }]) + .query([{ kinds: [1, 20], '#q': [event.id], ...params }]) .then((events) => hydrateEvents({ events, store })); const viewerPubkey = await c.get('signer')?.getPublicKey(); diff --git a/src/controllers/api/streaming.ts b/src/controllers/api/streaming.ts index 5e90085d..079f74cd 100644 --- a/src/controllers/api/streaming.ts +++ b/src/controllers/api/streaming.ts @@ -214,20 +214,20 @@ async function topicToFilter( switch (topic) { case 'public': - return { kinds: [1, 6] }; + return { kinds: [1, 6, 20] }; case 'public:local': - return { kinds: [1, 6], search: `domain:${host}` }; + return { kinds: [1, 6, 20], search: `domain:${host}` }; case 'hashtag': - if (query.tag) return { kinds: [1, 6], '#t': [query.tag] }; + if (query.tag) return { kinds: [1, 6, 20], '#t': [query.tag] }; break; case 'hashtag:local': - if (query.tag) return { kinds: [1, 6], '#t': [query.tag], search: `domain:${host}` }; + if (query.tag) return { kinds: [1, 6, 20], '#t': [query.tag], search: `domain:${host}` }; break; case 'user': // HACK: this puts the user's entire contacts list into RAM, // and then calls `matchFilters` over it. Refreshing the page // is required after following a new user. - return pubkey ? { kinds: [1, 6], authors: [...await getFeedPubkeys(pubkey)] } : undefined; + return pubkey ? { kinds: [1, 6, 20], authors: [...await getFeedPubkeys(pubkey)] } : undefined; } } diff --git a/src/controllers/api/timelines.ts b/src/controllers/api/timelines.ts index cd0d7ff1..fa5f44f6 100644 --- a/src/controllers/api/timelines.ts +++ b/src/controllers/api/timelines.ts @@ -14,7 +14,7 @@ const homeTimelineController: AppController = async (c) => { const params = c.get('pagination'); const pubkey = await c.get('signer')?.getPublicKey()!; const authors = [...await getFeedPubkeys(pubkey)]; - return renderStatuses(c, [{ authors, kinds: [1, 6], ...params }]); + return renderStatuses(c, [{ authors, kinds: [1, 6, 20], ...params }]); }; const publicQuerySchema = z.object({ @@ -33,7 +33,7 @@ const publicTimelineController: AppController = (c) => { const { local, instance, language } = result.data; - const filter: NostrFilter = { kinds: [1], ...params }; + const filter: NostrFilter = { kinds: [1, 20], ...params }; const search: `${string}:${string}`[] = []; @@ -57,7 +57,7 @@ const publicTimelineController: AppController = (c) => { const hashtagTimelineController: AppController = (c) => { const hashtag = c.req.param('hashtag')!.toLowerCase(); const params = c.get('pagination'); - return renderStatuses(c, [{ kinds: [1], '#t': [hashtag], ...params }]); + return renderStatuses(c, [{ kinds: [1, 20], '#t': [hashtag], ...params }]); }; const suggestedTimelineController: AppController = async (c) => { @@ -70,7 +70,7 @@ const suggestedTimelineController: AppController = async (c) => { const authors = [...getTagSet(follows?.tags ?? [], 'p')]; - return renderStatuses(c, [{ authors, kinds: [1], ...params }]); + return renderStatuses(c, [{ authors, kinds: [1, 20], ...params }]); }; /** Render statuses for timelines. */ diff --git a/src/controllers/api/trends.ts b/src/controllers/api/trends.ts index 45e2d117..a7906192 100644 --- a/src/controllers/api/trends.ts +++ b/src/controllers/api/trends.ts @@ -134,7 +134,7 @@ const trendingStatusesController: AppController = async (c) => { return c.json([]); } - const results = await store.query([{ kinds: [1], ids }]) + const results = await store.query([{ kinds: [1, 20], ids }]) .then((events) => hydrateEvents({ events, store })); // Sort events in the order they appear in the label. diff --git a/src/storages/hydrate.ts b/src/storages/hydrate.ts index 7f5c8125..28dcea47 100644 --- a/src/storages/hydrate.ts +++ b/src/storages/hydrate.ts @@ -102,21 +102,21 @@ export function assembleEvents( if (event.kind === 1) { const id = findQuoteTag(event.tags)?.[1] || findQuoteInContent(event.content); if (id) { - event.quote = b.find((e) => matchFilter({ kinds: [1], ids: [id] }, e)); + event.quote = b.find((e) => matchFilter({ kinds: [1, 20], ids: [id] }, e)); } } if (event.kind === 6) { const id = event.tags.find(([name]) => name === 'e')?.[1]; if (id) { - event.repost = b.find((e) => matchFilter({ kinds: [1], ids: [id] }, e)); + event.repost = b.find((e) => matchFilter({ kinds: [1, 20], ids: [id] }, e)); } } if (event.kind === 7) { const id = event.tags.findLast(([name]) => name === 'e')?.[1]; if (id) { - event.reacted = b.find((e) => matchFilter({ kinds: [1], ids: [id] }, e)); + event.reacted = b.find((e) => matchFilter({ kinds: [1, 20], ids: [id] }, e)); } } @@ -130,7 +130,7 @@ export function assembleEvents( const ids = event.tags.filter(([name]) => name === 'e').map(([_name, value]) => value); for (const id of ids) { - const reported = b.find((e) => matchFilter({ kinds: [1], ids: [id] }, e)); + const reported = b.find((e) => matchFilter({ kinds: [1, 20], ids: [id] }, e)); if (reported) { reportedEvents.push(reported); } @@ -146,7 +146,7 @@ export function assembleEvents( const id = event.tags.find(([name]) => name === 'e')?.[1]; if (id) { - event.zapped = b.find((e) => matchFilter({ kinds: [1], ids: [id] }, e)); + event.zapped = b.find((e) => matchFilter({ kinds: [1, 20], ids: [id] }, e)); } const zapRequestString = event?.tags?.find(([name]) => name === 'description')?.[1]; @@ -313,7 +313,7 @@ function gatherReportedNotes({ events, store, signal }: HydrateOpts): Promise { const [{ count: followers_count }, { count: notes_count }, [followList], [kind0]] = await Promise.all([ store.count([{ kinds: [3], '#p': [pubkey] }]), - store.count([{ kinds: [1], authors: [pubkey] }]), + store.count([{ kinds: [1, 20], authors: [pubkey] }]), store.query([{ kinds: [3], authors: [pubkey], limit: 1 }]), store.query([{ kinds: [0], authors: [pubkey], limit: 1 }]), ]); diff --git a/src/views.ts b/src/views.ts index e333eebd..562043db 100644 --- a/src/views.ts +++ b/src/views.ts @@ -75,7 +75,7 @@ async function renderStatuses(c: AppContext, ids: string[], signal = AbortSignal const store = await Storages.db(); const { limit } = c.get('pagination'); - const events = await store.query([{ kinds: [1], ids, limit }], { signal }) + const events = await store.query([{ kinds: [1, 20], ids, limit }], { signal }) .then((events) => hydrateEvents({ events, store, signal })); if (!events.length) {