aboutsummaryrefslogtreecommitdiffstats
path: root/lib/nostr.ts
blob: ca09649cbcbfab67952afeee4c75fa7b36f157e0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import config from "$config";
import { db } from "$lib/db";
import { l } from "$lib/logging";
import { fail } from "$lib/utils";
import { nip19, finalizeEvent, getPublicKey } from "nostr-tools";
import { Relay } from "nostr-tools/relay";
import { AbstractSimplePool } from "nostr-tools/abstract-pool";

export const serverPubkey = getPublicKey(
  nip19.decode(config.nostrKey).data as Uint8Array,
);

let alwaysTrue: any = (t: Event) => { t[Symbol("verified")] = true; return true; };
export let pool = new AbstractSimplePool({ verifyEvent: alwaysTrue });

export async function send(ev, url = config.nostr) {
  if (!(ev && ev.id)) return;
  try {
    let r = await Relay.connect(url);
    await r.publish(ev);
    r.close();
  } catch (e) {
    console.log("nostr send error", e);
  }
}

export async function handleZap(invoice) {
  let pubkey = serverPubkey;
  let sk = nip19.decode(config.nostrKey).data as Uint8Array;
  let zapreq = JSON.parse(invoice.description);

  if (!zapreq.tags || zapreq.tags.length === 0) {
    fail(`No tags found`);
  }

  let ptags = zapreq.tags.filter(
    (t) => t && t.length && t.length >= 2 && t[0] === "p",
  );

  if (ptags.length !== 1) {
    fail(`None or multiple p tags found`);
  }

  let etags = zapreq.tags.filter(
    (t) => t && t.length && t.length >= 2 && t[0] === "e",
  );

  if (!(etags.length === 0 || etags.length === 1)) {
    fail(`Expected none or 1 e tags`);
  }

  let relays_tag = zapreq.tags.find(
    (t) => t && t.length && t.length >= 2 && t[0] === "relays",
  );

  if (!relays_tag) {
    fail(`No relays tag found`);
  }

  let relays = relays_tag.slice(1).filter((r) => r && r.startsWith("ws"));
  let etag = etags.length > 0 && etags[0];
  let ptag = ptags[0];

  let kind = 9735;
  let created_at = invoice.paid_at;
  let content = zapreq.content;

  let tags = [ptag];
  if (etag) tags.push(etag);

  tags.push(["bolt11", invoice.bolt11]);
  tags.push(["description", invoice.description]);
  tags.push(["preimage", invoice.payment_preimage]);

  let ev = { pubkey, kind, created_at, content, tags };
  let signed = await finalizeEvent(ev, sk);

  l("sending receipt");

  await Promise.allSettled(
    relays.map(async (url) => {
      let r = await Relay.connect(url);
      await r.publish(signed);
      r.close();
    }),
  );
}

export let getRelays = async (pubkey): Promise<any> => {
  let { relays } = config;
  let filter = { authors: [pubkey], kinds: [10002] };
  let event = await pool.get([config.nostr], filter);

  let read = relays;
  let write = relays;

  if (event) {
    read = [];
    write = [];
    for (let r of event.tags) {
      if (!r[2] || r[2] === "write") write.push(r[1]);
      if (!r[2] || r[2] === "read") read.push(r[1]);
    }
  } else pool.get(relays, filter).then(send);

  return { read, write };
};

export let getProfile = async (pubkey) => {
  if (await db.sIsMember("noprofile", pubkey)) return;
  let relays = [config.nostr];
  let filter = { authors: [pubkey], kinds: [0] };
  let ev = await pool.get(relays, filter);
  if (ev) {
    return JSON.parse(ev.content);
  } else
    getRelays(pubkey).then(({ write: relays }) =>
      pool.get(relays, filter).then(send),
    );
};