213 lines
5.8 KiB
TypeScript
Raw Normal View History

2023-10-17 14:25:26 -04:00
import NDK, {
NDKEvent,
2023-10-18 17:52:45 -04:00
NDKUser,
2023-10-18 18:14:20 -04:00
NDKSigner,
2023-10-17 14:25:26 -04:00
zapInvoiceFromEvent,
type NostrEvent,
} from "@nostr-dev-kit/ndk";
import { requestProvider } from "webln";
import { bech32 } from "@scure/base";
import { z } from "zod";
import { createZodFetcher } from "zod-fetch";
import { getTagValues, getTagsAllValues } from "../nostr/utils";
import { unixTimeNowInSeconds } from "../nostr/dates";
import { createEvent } from "./create";
2023-10-18 17:52:45 -04:00
import { findEphemeralSigner } from "@/lib/actions/ephemeral";
2023-10-17 14:25:26 -04:00
const fetchWithZod = createZodFetcher();
const ZapEndpointResponseSchema = z.object({
nostrPubkey: z.string(),
});
export async function sendZap(
ndk: NDK,
amount: number,
_event: NostrEvent,
comment?: string,
) {
console.log("sendzap called", amount);
const event = await new NDKEvent(ndk, _event);
2023-10-18 16:29:05 -04:00
console.log("Event", event);
2023-10-17 14:25:26 -04:00
const pr = await event.zap(amount * 1000, comment);
if (!pr) {
console.log("No PR");
return;
}
console.log("PR", pr);
const webln = await requestProvider();
return await webln.sendPayment(pr);
}
export async function checkPayment(
ndk: NDK,
tagId: string,
pubkey: string,
event: NostrEvent,
) {
2023-10-18 17:06:15 -04:00
const paymentEvents = await ndk.fetchEvents({
2023-10-17 14:25:26 -04:00
kinds: [9735],
["#a"]: [tagId],
});
2023-10-18 17:06:15 -04:00
if (!paymentEvents) return;
const paymentEvent = Array.from(paymentEvents).find(
(e) => zapInvoiceFromEvent(e)?.zappee === pubkey,
);
2023-10-17 14:25:26 -04:00
if (!paymentEvent) return;
const invoice = zapInvoiceFromEvent(paymentEvent);
if (!invoice) {
console.log("No invoice");
return;
}
const zappedUser = ndk.getUser({
2023-10-18 17:06:15 -04:00
hexpubkey: invoice.zapped,
2023-10-17 14:25:26 -04:00
});
await zappedUser.fetchProfile();
if (!zappedUser.profile) {
console.log("No zappedUser profile");
return;
}
const { lud16, lud06 } = zappedUser.profile;
let zapEndpoint: null | string = null;
if (lud16 && !lud16.startsWith("LNURL")) {
const [name, domain] = lud16.split("@");
zapEndpoint = `https://${domain}/.well-known/lnurlp/${name}`;
} else if (lud06) {
const { words } = bech32.decode(lud06, 1e3);
const data = bech32.fromWords(words);
const utf8Decoder = new TextDecoder("utf-8");
zapEndpoint = utf8Decoder.decode(data);
}
if (!zapEndpoint) {
console.log("No zapEndpoint");
return;
}
const { nostrPubkey } = await fetchWithZod(
// The schema you want to validate with
ZapEndpointResponseSchema,
// Any parameters you would usually pass to fetch
zapEndpoint,
{
method: "GET",
},
);
if (!nostrPubkey) return;
console.log("nostrPubkey", nostrPubkey);
console.log("Invoice amount", invoice.amount);
console.log("Price", parseInt(getTagValues("price", event.tags) ?? "0"));
return (
nostrPubkey === paymentEvent.pubkey &&
invoice.amount >= parseInt(getTagValues("price", event.tags) ?? "0")
);
}
export async function updateListUsersFromZaps(
ndk: NDK,
tagId: string,
event: NostrEvent,
) {
const SECONDS_IN_MONTH = 2_628_000;
const SECONDS_IN_YEAR = SECONDS_IN_MONTH * 365;
2023-10-17 14:25:26 -04:00
const paymentEvents = await ndk.fetchEvents({
kinds: [9735],
["#a"]: [tagId],
since: unixTimeNowInSeconds() - SECONDS_IN_YEAR,
2023-10-17 14:25:26 -04:00
});
const paymentInvoices = Array.from(paymentEvents).map((paymentEvent) =>
zapInvoiceFromEvent(paymentEvent),
);
const currentUsers = getTagsAllValues("p", event.tags);
let validUsers: string[][] = currentUsers.filter(
([pubkey, relay, petname, expiryUnix]) =>
parseInt(expiryUnix ?? "0") > unixTimeNowInSeconds(),
);
2023-10-18 18:51:17 -04:00
const newUsers: string[] = currentUsers.map(([pub]) => pub as string);
2023-10-17 14:25:26 -04:00
for (const paymentInvoice of paymentInvoices) {
if (
!paymentInvoice ||
validUsers.find(([pubkey]) => pubkey === paymentInvoice.zappee)
) {
continue;
}
const isValid = await checkPayment(
ndk,
tagId,
paymentInvoice.zappee,
event,
);
console.log("Is valid?", isValid);
if (isValid) {
validUsers.push([
paymentInvoice.zappee,
"",
"",
(unixTimeNowInSeconds() + SECONDS_IN_YEAR).toString(),
2023-10-17 14:25:26 -04:00
]);
2023-10-18 17:52:45 -04:00
newUsers.push(paymentInvoice.zappee);
// Send old codes to user
2023-10-17 14:25:26 -04:00
}
}
2023-10-18 18:51:17 -04:00
await sendCodesToNewUsers(ndk, newUsers, tagId, event);
2023-10-17 14:25:26 -04:00
// Add self
console.log("Adding self");
const selfIndex = validUsers.findIndex(([vu]) => vu === event.pubkey);
2023-10-18 15:05:36 -04:00
if (selfIndex === -1) {
validUsers.push([event.pubkey, "", "self", "4000000000"]);
2023-10-17 14:25:26 -04:00
}
console.log("Valid users", validUsers);
return createEvent(ndk, {
...event,
kind: event.kind as number,
tags: [
...event.tags.filter(([key]) => key !== "p"),
...validUsers.map((user) => ["p", ...user]),
],
});
}
2023-10-18 17:52:45 -04:00
2023-10-18 18:14:20 -04:00
async function sendCodesToNewUsers(
ndk: NDK,
users: string[],
tagId: string,
2023-10-18 18:51:17 -04:00
event: NostrEvent,
2023-10-18 18:14:20 -04:00
) {
2023-10-18 18:51:17 -04:00
const signer = await findEphemeralSigner(ndk, ndk.signer!, {
associatedEventNip19: new NDKEvent(ndk, event).encode(),
2023-10-18 17:52:45 -04:00
});
2023-10-18 18:14:20 -04:00
console.log("Signer", signer);
2023-10-18 17:52:45 -04:00
if (!signer) return;
const delegate = await signer.user();
const messages = await ndk.fetchEvents({
authors: [delegate.pubkey],
kinds: [4],
["#p"]: [tagId.split(":")?.[1] ?? ""],
});
const codes: [string, string][] = [];
for (const message of Array.from(messages)) {
2023-10-18 18:51:17 -04:00
await message.decrypt();
2023-10-18 17:52:45 -04:00
codes.push([getTagValues("e", message.tags) ?? "", message.content]);
}
2023-10-18 18:51:17 -04:00
console.log("codes", codes);
2023-10-18 17:52:45 -04:00
for (const user of users) {
for (const [event, code] of codes) {
const messageEvent = new NDKEvent(ndk, {
content: code,
kind: 4,
tags: [
["p", user],
["e", event],
["client", "flockstr"],
],
pubkey: delegate.pubkey,
} as NostrEvent);
2023-10-18 18:14:20 -04:00
console.log("Sending message");
2023-10-18 17:52:45 -04:00
await messageEvent.encrypt(new NDKUser({ hexpubkey: user }), signer);
await messageEvent.sign(signer);
await messageEvent.publish();
}
}
}