#!/usr/bin/env npx tsx /** * SpendOS XMTP Notification Listener * * Listens for XMTP messages sent TO the SpendOS treasury wallet. * Shows real-time governance notifications from the SpendOS agent. * * Usage: * NAPI_RS_NATIVE_LIBRARY_PATH=/tmp/xmtp-binding.node npx tsx scripts/xmtp-listener.ts */ import { Client } from '@xmtp/node-sdk'; import { mnemonicToAccount } from 'viem/accounts'; import { toBytes } from 'viem'; import { getRandomValues } from 'node:crypto'; const MNEMONIC = process.env.OWS_IMPORT_MNEMONIC; if (!MNEMONIC) { console.error('Set OWS_IMPORT_MNEMONIC env var'); process.exit(1); } async function main() { const account = mnemonicToAccount(MNEMONIC); console.log(`\n SpendOS XMTP Listener`); console.log(` Wallet: ${account.address}`); console.log(` Waiting for notifications...\n`); const signer = { type: 'EOA' as const, getIdentifier: () => ({ identifier: account.address, identifierKind: 0 }), signMessage: async (message: string) => { const sig = await account.signMessage({ message }); return toBytes(sig); }, }; const dbEncryptionKey = getRandomValues(new Uint8Array(32)); const client = await Client.create(signer, { dbEncryptionKey, dbPath: `/tmp/xmtp-spendos-listener-${Date.now()}`, }); console.log(` XMTP client connected\n`); // Sync conversations first await client.conversations.sync(); // Stream all incoming messages (v6 returns a promise) const stream = await client.conversations.streamAllMessages(); for await (const message of stream) { if (message.senderInboxId === client.inboxId) continue; const time = new Date().toLocaleTimeString(); console.log(`\n--- ${time} ---`); console.log(message.content); console.log(`---\n`); } } main().catch(err => { console.error(`Failed: ${err.message}`); process.exit(1); });