From cbc1691002e79cc260aa9cc27ddc8b32385dbcc3 Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Sun, 15 Sep 2024 18:34:11 -0300 Subject: [PATCH] feat: zap notification in streaming --- src/controllers/api/streaming.ts | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/controllers/api/streaming.ts b/src/controllers/api/streaming.ts index cfa8c3c5..3f05669d 100644 --- a/src/controllers/api/streaming.ts +++ b/src/controllers/api/streaming.ts @@ -1,4 +1,5 @@ -import { NostrEvent, NostrFilter } from '@nostrify/nostrify'; +import TTLCache from '@isaacs/ttlcache'; +import { NostrEvent, NostrFilter, NSchema as n } from '@nostrify/nostrify'; import Debug from '@soapbox/stickynotes/debug'; import { z } from 'zod'; @@ -10,9 +11,10 @@ import { getFeedPubkeys } from '@/queries.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; import { Storages } from '@/storages.ts'; import { bech32ToPubkey, Time } from '@/utils.ts'; +import { getAmount } from '@/utils/bolt11.ts'; import { renderReblog, renderStatus } from '@/views/mastodon/statuses.ts'; import { renderNotification } from '@/views/mastodon/notifications.ts'; -import TTLCache from '@isaacs/ttlcache'; +import { RenderNotificationOpts } from '@/views/mastodon/notifications.ts'; const debug = Debug('ditto:streaming'); @@ -155,7 +157,28 @@ const streamingController: AppController = async (c) => { if (['user', 'user:notification'].includes(stream) && pubkey) { sub([{ '#p': [pubkey] }], async (event) => { if (event.pubkey === pubkey) return; // skip own events - const payload = await renderNotification(event, { viewerPubkey: pubkey }); + + const opts: RenderNotificationOpts = { viewerPubkey: pubkey }; + + if (event.kind === 9735) { + const zapRequestString = event?.tags?.find(([name]) => name === 'description')?.[1]; + const zapRequest = n.json().pipe(n.event()).optional().catch(undefined).parse(zapRequestString); + // By getting the pubkey from the zap request we guarantee who is the sender + // some clients don't put the P tag in the zap receipt... + const zapSender = zapRequest?.pubkey; + + const amountSchema = z.coerce.number().int().nonnegative().catch(0); + // amount in millisats + const amount = amountSchema.parse(getAmount(event?.tags.find(([name]) => name === 'bolt11')?.[1])); + + opts['zap'] = { + zapSender, + amount, + message: zapRequest?.content, + }; + } + + const payload = await renderNotification(event, opts); if (payload) { return { event: 'notification', @@ -215,7 +238,7 @@ async function topicToFilter( // 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, 9735], authors: await getFeedPubkeys(pubkey) } : undefined; } }