mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-29 16:31:04 +00:00
Use nip57 module for zaps
This commit is contained in:
parent
e14aaf99a9
commit
75f8b7fec4
@ -62,10 +62,6 @@ const Meta = {
|
||||
stats.subsCount += 1
|
||||
stats.activeSubsCount += 1
|
||||
stats.lastRequest = Date.now()
|
||||
|
||||
if (stats.activeSubsCount > 10) {
|
||||
warn(`Relay ${url} has ${stats.activeSubsCount} active subscriptions`)
|
||||
}
|
||||
})
|
||||
},
|
||||
onSubscriptionEnd(urls) {
|
||||
|
@ -1,17 +1,7 @@
|
||||
import {partition, is, uniq, reject, nth, objOf, pick, identity} from "ramda"
|
||||
import {nip05} from "nostr-tools"
|
||||
import {noop, ensurePlural, chunk} from "hurdak/lib/hurdak"
|
||||
import {
|
||||
hexToBech32,
|
||||
tryFunc,
|
||||
bech32ToHex,
|
||||
tryFetch,
|
||||
now,
|
||||
sleep,
|
||||
tryJson,
|
||||
timedelta,
|
||||
hash,
|
||||
} from "src/util/misc"
|
||||
import {now, sleep, tryJson, timedelta, hash} from "src/util/misc"
|
||||
import {Tags, roomAttrs, isRelay, isShareableRelay, normalizeRelayUrl} from "src/util/nostr"
|
||||
import {topics, people, userEvents, relays, rooms, routes} from "src/agent/db"
|
||||
import {uniqByUrl} from "src/agent/relays"
|
||||
@ -80,37 +70,6 @@ const verifyNip05 = (pubkey, alias) =>
|
||||
}
|
||||
}, noop)
|
||||
|
||||
const verifyZapper = async (pubkey, address) => {
|
||||
let url
|
||||
|
||||
// Try to parse it as a lud06 LNURL or as a lud16 address
|
||||
if (address.startsWith("lnurl1")) {
|
||||
url = tryFunc(() => bech32ToHex(address))
|
||||
} else if (address.includes("@")) {
|
||||
const [name, domain] = address.split("@")
|
||||
|
||||
if (domain && name) {
|
||||
url = `https://${domain}/.well-known/lnurlp/${name}`
|
||||
}
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
return
|
||||
}
|
||||
|
||||
const res = await tryFetch(() => fetch(url))
|
||||
const zapper = await tryJson(() => res?.json())
|
||||
const lnurl = hexToBech32("lnurl", url)
|
||||
|
||||
if (zapper?.allowsNostr && zapper?.nostrPubkey) {
|
||||
updatePerson(pubkey, {
|
||||
lnurl,
|
||||
// Trim zapper so we don't have so much metadata filling up memory
|
||||
zapper: pick(["callback", "maxSendable", "minSendable", "nostrPubkey"], zapper),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
addHandler(0, e => {
|
||||
tryJson(() => {
|
||||
const kind0 = JSON.parse(e.content)
|
||||
@ -125,12 +84,6 @@ addHandler(0, e => {
|
||||
verifyNip05(e.pubkey, kind0.nip05)
|
||||
}
|
||||
|
||||
const address = kind0.lud16 || kind0.lud06
|
||||
|
||||
if (address) {
|
||||
verifyZapper(e.pubkey, address.toLowerCase())
|
||||
}
|
||||
|
||||
updatePerson(e.pubkey, {
|
||||
kind0: {...person?.kind0, ...kind0},
|
||||
kind0_updated_at: e.created_at,
|
||||
|
@ -12,7 +12,6 @@ const profile = synced("agent/user/profile", {
|
||||
pubkey: null,
|
||||
kind0: null,
|
||||
lnurl: null,
|
||||
zapper: null,
|
||||
rooms_joined: [],
|
||||
last_checked: {},
|
||||
relays: pool.defaultRelays,
|
||||
|
@ -4,7 +4,7 @@
|
||||
import {tweened} from "svelte/motion"
|
||||
import {find, reject, identity, propEq, pathEq, sum, pluck, sortBy} from "ramda"
|
||||
import {stringToHue, formatSats, hsl} from "src/util/misc"
|
||||
import {displayRelay, isLike, processZaps} from "src/util/nostr"
|
||||
import {displayRelay, isLike} from "src/util/nostr"
|
||||
import {quantify, first} from "hurdak/lib/hurdak"
|
||||
import {modal} from "src/partials/state"
|
||||
import Popover from "src/partials/Popover.svelte"
|
||||
@ -14,7 +14,7 @@
|
||||
import CopyValue from "src/partials/CopyValue.svelte"
|
||||
import PersonBadge from "src/app/shared/PersonBadge.svelte"
|
||||
import RelayCard from "src/app/shared/RelayCard.svelte"
|
||||
import {ENABLE_ZAPS, keys} from "src/system"
|
||||
import {ENABLE_ZAPS, keys, nip57} from "src/system"
|
||||
import {getEventPublishRelays} from "src/agent/relays"
|
||||
import {getPersonWithFallback} from "src/agent/db"
|
||||
import pool from "src/agent/pool"
|
||||
@ -29,6 +29,7 @@
|
||||
export let setFeedRelay
|
||||
|
||||
const {canSign} = keys
|
||||
const zapper = nip57.zappers.get(note.pubkey)
|
||||
const bech32Note = nip19.noteEncode(note.id)
|
||||
const nevent = nip19.neventEncode({id: note.id, relays: [note.seen_on]})
|
||||
const interpolate = (a, b) => t => a + Math.round((b - a) * t)
|
||||
@ -70,12 +71,14 @@
|
||||
$: allLikes = like ? likes.filter(n => n.id !== like?.id).concat(like) : likes
|
||||
$: $likesCount = allLikes.length
|
||||
|
||||
$: zaps = processZaps(note.zaps, $author)
|
||||
$: zaps = nip57.processZaps(note.zaps, note.pubkey)
|
||||
$: zap = zap || find(pathEq(["request", "pubkey"], user.getPubkey()), zaps)
|
||||
$: allZaps = zap ? zaps.filter(n => n.id !== zap?.id).concat(processZaps([zap], $author)) : zaps
|
||||
$: allZaps = zap
|
||||
? zaps.filter(n => n.id !== zap?.id).concat(nip57.processZaps([zap], note.pubkey))
|
||||
: zaps
|
||||
$: $zapsTotal = sum(pluck("invoiceAmount", allZaps)) / 1000
|
||||
|
||||
$: canZap = $author?.zapper && $author?.pubkey !== user.getPubkey()
|
||||
$: canZap = zapper && $author?.pubkey !== user.getPubkey()
|
||||
$: $repliesCount = note.replies.length
|
||||
|
||||
$: {
|
||||
@ -131,8 +134,7 @@
|
||||
{#if ENABLE_ZAPS}
|
||||
<button
|
||||
class={cx("w-20 text-left", {
|
||||
"pointer-events-none opacity-50":
|
||||
disableActions || $author.pubkey === user.getPubkey() || !$author.zapper,
|
||||
"pointer-events-none opacity-50": disableActions || !canZap,
|
||||
"text-accent": zap,
|
||||
})}
|
||||
on:click={startZap}>
|
||||
|
@ -13,7 +13,7 @@
|
||||
import {getEventPublishRelays} from "src/agent/relays"
|
||||
import {getPersonWithFallback} from "src/agent/db"
|
||||
import network from "src/agent/network"
|
||||
import {keys, settings} from "src/system"
|
||||
import {keys, settings, nip57} from "src/system"
|
||||
import cmd from "src/agent/cmd"
|
||||
|
||||
export let note
|
||||
@ -33,13 +33,22 @@
|
||||
const loadZapInvoice = async () => {
|
||||
zap.loading = true
|
||||
|
||||
const {zapper, lnurl} = author
|
||||
const amount = zap.amount * 1000
|
||||
const zapper = nip57.zappers.get(note.pubkey)
|
||||
const relays = getEventPublishRelays(note)
|
||||
const urls = pluck("url", relays)
|
||||
const publishable = cmd.requestZap(urls, zap.message, note.pubkey, note.id, amount, lnurl)
|
||||
const publishable = cmd.requestZap(
|
||||
urls,
|
||||
zap.message,
|
||||
note.pubkey,
|
||||
note.id,
|
||||
amount,
|
||||
zapper.lnurl
|
||||
)
|
||||
const event = encodeURI(JSON.stringify(await keys.sign(publishable.event)))
|
||||
const res = await fetchJson(`${zapper.callback}?amount=${amount}&nostr=${event}&lnurl=${lnurl}`)
|
||||
const res = await fetchJson(
|
||||
`${zapper.callback}?amount=${amount}&nostr=${event}&lnurl=${zapper.lnurl}`
|
||||
)
|
||||
|
||||
// If they closed the dialog before fetch resolved, we're done
|
||||
if (!zap) {
|
||||
|
@ -1,4 +1,6 @@
|
||||
import {fetchJson, tryFunc, tryJson, hexToBech32, bech32ToHex} from "src/util/misc"
|
||||
import {invoiceAmount} from "src/util/lightning"
|
||||
import {Tags} from "src/util/nostr"
|
||||
import {Table} from "src/agent/db"
|
||||
|
||||
const getLnUrl = address => {
|
||||
@ -18,7 +20,7 @@ const getLnUrl = address => {
|
||||
}
|
||||
|
||||
export default ({sync, sortByGraph}) => {
|
||||
const zappers = new Table("nip57/zappers", "pubkey", {max: 5000, sort: sortByGraph})
|
||||
const zappers = new Table("zappers", "pubkey", {max: 5000, sort: sortByGraph})
|
||||
|
||||
sync.addHandler(0, e => {
|
||||
tryJson(async () => {
|
||||
@ -54,7 +56,57 @@ export default ({sync, sortByGraph}) => {
|
||||
})
|
||||
})
|
||||
|
||||
const processZaps = (zaps, pubkey) => {
|
||||
const zapper = zappers.get(pubkey)
|
||||
|
||||
if (!zapper) {
|
||||
return []
|
||||
}
|
||||
|
||||
return zaps
|
||||
.map(zap => {
|
||||
const zapMeta = Tags.from(zap).asMeta()
|
||||
|
||||
return tryJson(() => ({
|
||||
...zap,
|
||||
invoiceAmount: invoiceAmount(zapMeta.bolt11),
|
||||
request: JSON.parse(zapMeta.description),
|
||||
}))
|
||||
})
|
||||
.filter(zap => {
|
||||
if (!zap) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Don't count zaps that the user sent himself
|
||||
if (zap.request.pubkey === pubkey) {
|
||||
return false
|
||||
}
|
||||
|
||||
const {invoiceAmount, request} = zap
|
||||
const reqMeta = Tags.from(request).asMeta()
|
||||
|
||||
// Verify that the zapper actually sent the requested amount (if it was supplied)
|
||||
if (reqMeta.amount && parseInt(reqMeta.amount) !== invoiceAmount) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the sending client provided an lnurl tag, verify that too
|
||||
if (reqMeta.lnurl && reqMeta.lnurl !== zapper.lnurl) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Verify that the zap note actually came from the recipient's zapper
|
||||
if (zapper.nostrPubkey !== zap.pubkey) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
zappers,
|
||||
processZaps,
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import {is, fromPairs, mergeLeft, last, identity, objOf, prop, flatten, uniq} fr
|
||||
import {nip19} from "nostr-tools"
|
||||
import {ensurePlural, ellipsize, first} from "hurdak/lib/hurdak"
|
||||
import {tryJson, avg} from "src/util/misc"
|
||||
import {invoiceAmount} from "src/util/lightning"
|
||||
|
||||
export const noteKinds = [1, 1985, 30023, 1063, 9802]
|
||||
export const personKinds = [0, 2, 3, 10001, 10002]
|
||||
@ -198,48 +197,6 @@ export const toHex = (data: string): string | null => {
|
||||
export const mergeFilter = (filter, extra) =>
|
||||
is(Array, filter) ? filter.map(mergeLeft(extra)) : {...filter, ...extra}
|
||||
|
||||
export const processZaps = (zaps, author) =>
|
||||
zaps
|
||||
.map(zap => {
|
||||
const zapMeta = Tags.from(zap).asMeta()
|
||||
|
||||
return tryJson(() => ({
|
||||
...zap,
|
||||
invoiceAmount: invoiceAmount(zapMeta.bolt11),
|
||||
request: JSON.parse(zapMeta.description),
|
||||
}))
|
||||
})
|
||||
.filter(zap => {
|
||||
if (!zap) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Don't count zaps that the user sent himself
|
||||
if (zap.request.pubkey === author.pubkey) {
|
||||
return false
|
||||
}
|
||||
|
||||
const {invoiceAmount, request} = zap
|
||||
const reqMeta = Tags.from(request).asMeta()
|
||||
|
||||
// Verify that the zapper actually sent the requested amount (if it was supplied)
|
||||
if (reqMeta.amount && parseInt(reqMeta.amount) !== invoiceAmount) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the sending client provided an lnurl tag, verify that too
|
||||
if (reqMeta.lnurl && reqMeta.lnurl !== author.lnurl) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Verify that the zap note actually came from the recipient's zapper
|
||||
if (author.zapper?.nostrPubkey !== zap.pubkey) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
export const fromNostrURI = s => s.replace(/^[\w\+]+:\/?\/?/, "")
|
||||
|
||||
export const toNostrURI = s => `web+nostr://${s}`
|
||||
|
Loading…
Reference in New Issue
Block a user