diff --git a/src/deps.ts b/src/deps.ts index 59c8b077..e76c84a5 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -87,8 +87,9 @@ export { default as stringifyStable } from 'npm:fast-stable-stringify@^1.0.0'; // @deno-types="npm:@types/debug@^4.1.12" export { default as Debug } from 'npm:debug@^4.3.4'; export { + LNURL, type MapCache, NIP05, -} from 'https://gitlab.com/soapbox-pub/nlib/-/raw/46be9e985950547574b1735d0ae52a6a7217d056/mod.ts'; +} from 'https://gitlab.com/soapbox-pub/nlib/-/raw/137af48cbc2639a8969d233fc24d2b959f34782a/mod.ts'; export type * as TypeFest from 'npm:type-fest@^4.3.0'; diff --git a/src/pipeline.ts b/src/pipeline.ts index 4d763221..ad12b09e 100644 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -2,16 +2,15 @@ import { Conf } from '@/config.ts'; import { addRelays } from '@/db/relays.ts'; import { deleteAttachedMedia } from '@/db/unattached-media.ts'; import { findUser } from '@/db/users.ts'; -import { Debug, type Event } from '@/deps.ts'; +import { Debug, type Event, LNURL } from '@/deps.ts'; import { isEphemeralKind } from '@/kinds.ts'; import { isLocallyFollowed } from '@/queries.ts'; -import { lnurlCallbackResponseSchema, lnurlResponseSchema } from '@/schemas/lnurl.ts'; +import { lnurlCallbackResponseSchema } from '@/schemas/lnurl.ts'; import { updateStats } from '@/stats.ts'; import { client, eventsDB, memorelay, reqmeister } from '@/storages.ts'; import { Sub } from '@/subs.ts'; import { getTagSet } from '@/tags.ts'; import { type EventData } from '@/types.ts'; -import { lnurlDecode } from '@/utils/lnurl.ts'; import { eventAge, isRelay, nostrDate, nostrNow, Time } from '@/utils.ts'; import { fetchWorker } from '@/workers/fetch.ts'; import { TrendsWorker } from '@/workers/trends.ts'; @@ -170,12 +169,9 @@ async function submitZaps(event: Event, data: EventData, signal = AbortSignal.ti const amount = event.tags.find(([name]) => name === 'amount')?.[1]; if (lnurl && amount) { try { - const url = lnurlDecode(lnurl); - const response = await fetchWorker(url, { signal }); - const json = await response.json(); - const result = lnurlResponseSchema.parse(json); - if (result.tag === 'payRequest' && result.allowsNostr && result.nostrPubkey) { - const callback = new URL(result.callback); + const details = await LNURL.lookup(lnurl, { fetch: fetchWorker, signal }); + if (details.tag === 'payRequest' && details.allowsNostr && details.nostrPubkey) { + const callback = new URL(details.callback); const params = new URLSearchParams(); params.set('amount', amount); params.set('nostr', JSON.stringify(event)); diff --git a/src/schemas/lnurl.ts b/src/schemas/lnurl.ts index 7ee83004..e16f68c2 100644 --- a/src/schemas/lnurl.ts +++ b/src/schemas/lnurl.ts @@ -1,20 +1,8 @@ import { z } from '@/deps.ts'; -import { nostrIdSchema } from './nostr.ts'; - -const lnurlResponseSchema = z.object({ - callback: z.string().url(), - maxSendable: z.number().int().nonnegative(), - minSendable: z.number().int().positive(), - metadata: z.string(), - tag: z.string(), - allowsNostr: z.boolean().optional(), - nostrPubkey: nostrIdSchema.optional(), -}); - const lnurlCallbackResponseSchema = z.object({ pr: z.string(), routes: z.unknown().array(), }); -export { lnurlCallbackResponseSchema, lnurlResponseSchema }; +export { lnurlCallbackResponseSchema }; diff --git a/src/utils/lnurl.test.ts b/src/utils/lnurl.test.ts deleted file mode 100644 index a99f2eec..00000000 --- a/src/utils/lnurl.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { assertEquals } from '@/deps-test.ts'; - -import { lnurlDecode, lnurlEncode } from './lnurl.ts'; - -const lnurl = 'lnurl1dp68gurn8ghj7um5v93kketj9ehx2amn9uh8wetvdskkkmn0wahz7mrww4excup0dajx2mrv92x9xp'; -const url = 'https://stacker.news/.well-known/lnurlp/odell'; - -Deno.test('lnurlEncode', () => { - assertEquals(lnurlEncode(url), lnurl); -}); - -Deno.test('lnurlDecode', () => { - assertEquals(lnurlDecode(lnurl), url); -}); diff --git a/src/utils/lnurl.ts b/src/utils/lnurl.ts index 2d60814e..d2b4e964 100644 --- a/src/utils/lnurl.ts +++ b/src/utils/lnurl.ts @@ -1,19 +1,4 @@ -import { bech32 } from '@/deps.ts'; - -/** Encode a URL to LNURL format. */ -function lnurlEncode(url: string, limit = 2000): `lnurl1${string}` { - const data = new TextEncoder().encode(url); - const words = bech32.toWords(data); - return bech32.encode('lnurl', words, limit); -} - -/** Decode a LNURL into a URL. */ -function lnurlDecode(lnurl: string, limit = 2000): string { - const { prefix, words } = bech32.decode(lnurl, limit); - if (prefix !== 'lnurl') throw new Error('Invalid LNURL'); - const data = new Uint8Array(bech32.fromWords(words)); - return new TextDecoder().decode(data); -} +import { LNURL } from '@/deps.ts'; /** Get an LNURL from a lud06 or lud16. */ function getLnurl({ lud06, lud16 }: { lud06?: string; lud16?: string }, limit?: number): string | undefined { @@ -21,10 +6,10 @@ function getLnurl({ lud06, lud16 }: { lud06?: string; lud16?: string }, limit?: if (lud16) { const [name, host] = lud16.split('@'); if (name && host) { - const url = new URL(`/.well-known/lnurlp/${name}`, `https://${host}`).toString(); - return lnurlEncode(url, limit); + const url = new URL(`/.well-known/lnurlp/${name}`, `https://${host}`); + return LNURL.encode(url, limit); } } } -export { getLnurl, lnurlDecode, lnurlEncode }; +export { getLnurl };