buzz listener: since-based replay + 2min reconnect cycle (zombie-socket proof); DM coverage verified
This commit is contained in:
parent
9fd63b176b
commit
6fa28abe20
1 changed files with 25 additions and 13 deletions
|
|
@ -1,20 +1,34 @@
|
|||
// Raw WebSocket listener for Buzz #general — no relay abstraction.
|
||||
// Handles NIP-42 auth explicitly: on ["AUTH", challenge] we sign kind 22242.
|
||||
// On a matching kind-9 event from another pubkey: spool to inbox, exit 0.
|
||||
// Buzz presence + inbox listener (raw WebSocket — no relay abstraction).
|
||||
// - Persistent authenticated connection (NIP-42 challenge/response).
|
||||
// - Subscribes to ALL kind-9 chat messages in the workspace (any channel, DMs
|
||||
// included). On a new message from another pubkey: spool the event to the
|
||||
// inbox and exit 0 — the ZCode background-task notification wakes the agent.
|
||||
// - Presence: `buzz users set-presence --status online` heartbeat every 60s
|
||||
// (relay TTL 180s). Listener alive = zai "online"; dead = offline in ~3 min.
|
||||
// - Reconnect/expires: exits 2 (closed) or 3 (4h lifetime); the supervisor
|
||||
// (ZCode background task) restarts it.
|
||||
import { finalizeEvent, getPublicKey } from 'nostr-tools/pure'
|
||||
import fs from 'node:fs'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
|
||||
const RELAY = 'wss://ch4t.buzz'
|
||||
const CH = '733ba424-73c7-4170-ac85-ba00d3a80f05'
|
||||
const ME = '4abe5fcdf9695be34bdfc8fe82297aaaf0cf65b5ca92b6c789f6f8783b5ee197'
|
||||
const INBOX = '/home/opc/.buzz-inbox'
|
||||
const MAX_LIFETIME = 4 * 3600 * 1000
|
||||
const STATE = '/home/opc/.buzz-last-seen'
|
||||
const MAX_LIFETIME = 120 * 1000 // reconnect every 2 min: replays since lastSeen (zombie-socket proof)
|
||||
|
||||
const keyText = fs.readFileSync('/home/opc/buzz-agent-key.txt', 'utf8')
|
||||
const SK = Uint8Array.from(keyText.match(/SECRET:\s*(\S+)/)[1].match(/.{2}/g).map((h) => parseInt(h, 16)))
|
||||
const MY_PUBKEY = getPublicKey(SK)
|
||||
|
||||
function lastSeen() {
|
||||
try { return parseInt(fs.readFileSync(STATE, 'utf8').trim(), 10) } catch { return Math.floor(Date.now() / 1000) }
|
||||
}
|
||||
function saveLastSeen(ts) {
|
||||
const cur = lastSeen()
|
||||
if (ts > cur) fs.writeFileSync(STATE, String(ts))
|
||||
}
|
||||
|
||||
const ws = new WebSocket(RELAY)
|
||||
const seen = new Set()
|
||||
let authEventId = null
|
||||
|
|
@ -23,8 +37,8 @@ let subscribed = false
|
|||
function subscribe() {
|
||||
if (subscribed) return
|
||||
subscribed = true
|
||||
ws.send(JSON.stringify(['REQ', 'general', { kinds: [9], '#h': [CH], since: Math.floor(Date.now() / 1000) }]))
|
||||
console.error('subscribed')
|
||||
ws.send(JSON.stringify(['REQ', 'workspace', { kinds: [9], since: lastSeen() }]))
|
||||
console.error('subscribed to all kind-9 messages since', lastSeen())
|
||||
}
|
||||
|
||||
function sendAuth(challenge) {
|
||||
|
|
@ -58,23 +72,21 @@ ws.onmessage = (msg) => {
|
|||
}
|
||||
if (type === 'NOTICE') { console.error('NOTICE:', rest[0]); return }
|
||||
if (type === 'EVENT') {
|
||||
const [, subid, evt] = data
|
||||
if (subid !== 'general') return
|
||||
if (evt.pubkey === ME || seen.has(evt.id)) return
|
||||
const evt = rest[1]
|
||||
if (!evt || evt.pubkey === ME || seen.has(evt.id)) return
|
||||
seen.add(evt.id)
|
||||
saveLastSeen(evt.created_at + 1)
|
||||
fs.appendFileSync(INBOX, JSON.stringify(evt) + '\n')
|
||||
console.error('message received from', evt.pubkey.slice(0, 10))
|
||||
process.exit(0)
|
||||
}
|
||||
if (type === 'EOSE') console.error('EOSE — waiting for live events')
|
||||
if (type === 'EOSE') console.error('EOSE — live')
|
||||
}
|
||||
ws.onclose = () => { console.error('ws closed'); process.exit(2) }
|
||||
ws.onerror = () => {}
|
||||
|
||||
setTimeout(() => { console.error('lifetime elapsed'); process.exit(3) }, MAX_LIFETIME)
|
||||
|
||||
// Presence heartbeat: relay TTL is 180s, contract says refresh every 60s.
|
||||
// Listener alive = zai "online"; backend dead = offline within 3 minutes.
|
||||
const BUZZ_CLI = '/usr/local/bin/buzz'
|
||||
const heartbeat = () => {
|
||||
try {
|
||||
|
|
|
|||
Loading…
Reference in a new issue