From aa662369cc7a293d0be5f4d419543a51ad40a7bc Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 20 Dec 2022 17:39:28 -0800 Subject: send user events --- index.js | 1 + lib/nostr.js | 72 ++++++++++++++++++++++++++++++-------------------------- lib/store.js | 4 +++- lib/utils.js | 8 +++++++ routes/events.js | 30 +++++++++++++++++++++++ routes/users.js | 2 +- 6 files changed, 81 insertions(+), 36 deletions(-) create mode 100644 routes/events.js diff --git a/index.js b/index.js index c4c6ebc6..d83afd91 100644 --- a/index.js +++ b/index.js @@ -18,6 +18,7 @@ import "./lib/mail"; import "./lib/nostr"; import "./routes/assets"; +import "./routes/events"; import "./routes/locations"; import "./routes/info"; import "./routes/users"; diff --git a/lib/nostr.js b/lib/nostr.js index b1b24b87..677f14e5 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -1,48 +1,52 @@ import { Relay, RelayPool } from "nostr"; import { got } from "got"; import { broadcast } from "$lib/sockets"; +import store from "$lib/store"; +import redis from "$lib/redis"; const { relays } = await got("https://nostr.watch/relays.json").json(); -const pool = RelayPool(relays); +export const pool = RelayPool(relays); let coinos; -pool.on("open", (relay) => { +pool.on("open", relay => { if (relay.url.includes("coinos")) coinos = relay; - relay.subscribe("subid", { limit: 1 }); + relay.subscribe("live", { limit: 1 }); }); let seen = []; -pool.on("event", (relay, sub_id, ev) => { - if (seen.includes(ev.id)) return; - if (Math.abs(Math.floor(Date.now()/1000) - ev.created_at) > 7200) return; - seen.push(ev.id); - seen.length > 1000 && seen.shift(); - - if ( - coinos && - ev.kind < 5 && - !ev.content.startsWith("test") && - !ev.content.startsWith("running branle") - ) { - let parsed; - try { - parsed = JSON.parse(ev.content); - } catch (e) { - parsed = ev.content; - } +let timeout; +pool.on("event", async (relay, sub, ev) => { + try { + if (sub === "live") { + if (Math.abs(Math.floor(Date.now() / 1000) - ev.created_at) > 7200) + return; + + if (seen.includes(ev.id)) return; + seen.push(ev.id); + seen.length > 1000 && seen.shift(); + + if (coinos && ev.kind < 5) { + let parsed; + try { + parsed = JSON.parse(ev.content); + } catch (e) { + parsed = ev.content; + } + + coinos.send(["EVENT", ev]); + if (ev.kind === 1) broadcast("event", ev); + } + } else { + let pubkey = sub; + clearTimeout(store.timeouts[pubkey]); - coinos.send(["EVENT", ev]); - if (ev.kind === 1) - broadcast("event", ev); + store.fetching[pubkey] = true; + await redis.sAdd(pubkey, ev.id); + await redis.set(`ev:${ev.id}`, JSON.stringify(ev)); + + store.timeouts[pubkey] = setTimeout(() => (store.fetching[pubkey] = false), 100); + } + } catch (e) { + console.log(e); } }); - -function formatDate(date) { - var hours = date.getHours(); - var minutes = date.getMinutes(); - hours = hours % 12; - hours = hours ? hours : 12; - minutes = minutes < 10 ? "0" + minutes : minutes; - var strTime = hours + ":" + minutes; - return date.getMonth() + 1 + "/" + date.getDate() + " " + strTime; -} diff --git a/lib/store.js b/lib/store.js index 6f2e47ce..f6f65521 100644 --- a/lib/store.js +++ b/lib/store.js @@ -20,5 +20,7 @@ export default { bcAddressIndex: 0, lqAddressIndex: 0, last: {}, - payments: [] + payments: [], + fetching: {}, + timeouts: {}, }; diff --git a/lib/utils.js b/lib/utils.js index 45e50243..7fdd10b3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -171,3 +171,11 @@ export const derivePayRequest = async ({ amount, memo, tip }) => { await ln.invoice(value ? `${value}sat` : "any", new Date(), memo, 360) ).bolt11; }; + +export const sleep = (n) => new Promise((r) => setTimeout(r, n)); +export const wait = async (f, s = 300, n = 1000) => { + let i = 0; + while (!(await f()) && i < s) (await sleep(n)) && i++; + if (i >= s) throw new Error("timeout"); + return f(); +}; diff --git a/routes/events.js b/routes/events.js new file mode 100644 index 00000000..460d09a5 --- /dev/null +++ b/routes/events.js @@ -0,0 +1,30 @@ +import app from "$app"; +import redis from "$lib/redis"; +import { pool } from "$lib/nostr"; +import store from "$lib/store"; +import { wait } from "$lib/utils"; + +app.get("/:pubkey/events", async (req, res) => { + try { + let { pubkey } = req.params; + store.fetching[pubkey] = true; + store.timeouts[pubkey] = setTimeout(() => (store.fetching[pubkey] = false), 500); + + pool.subscribe(pubkey, { + limit: 500, + kinds: [1], + authors: [pubkey] + }); + + let events = []; + await wait(() => !store.fetching[pubkey], 10, 10); + for (let id of await redis.sMembers(pubkey)) { + events.push(JSON.parse(await redis.get(`ev:${id}`))); + } + + res.send(events); + } catch (e) { + console.log(e); + res.code(500).send("problem fetching user events"); + } +}); diff --git a/routes/users.js b/routes/users.js index c4b613eb..8d45e8c3 100644 --- a/routes/users.js +++ b/routes/users.js @@ -56,7 +56,7 @@ app.get("/me", auth, async (req, res) => { app.get("/users/:username", async (req, res) => { const { username } = req.params; - const user = await db.User.findOne({ + let user = await db.User.findOne({ attributes: ["username", "banner", "profile", "address", "currency"], where: Sequelize.where( Sequelize.fn("lower", Sequelize.col("username")), -- cgit v1.2.3