import * as dotenv from 'dotenv'; import { MatrixClient } from 'matrix-js-sdk'; import fs from 'fs'; dotenv.config(); console.log('🚀 Starting Ancestor Quote Bot...\n'); const client = new MatrixClient({ accessToken: process.env.MATRIX_ACCESS_TOKEN, baseUrl: process.env.MATRIX_HOME_SERVER, }); function loadQuotes() { try { const quotesFile = './quotes.json'; if (fs.existsSync(quotesFile)) { return JSON.parse(fs.readFileSync(quotesFile, 'utf8')); } } catch (err) { console.error('Error loading quotes:', err.message); } return []; } (async () => { const quotes = loadQuotes(); if (quotes.length === 0) { console.error('❌ No quotes found! Please ensure quotes.json is present.'); process.exit(1); } console.log(`✅ Loaded ${quotes.length} Ancestor quotes`); const token = process.env.MATRIX_ACCESS_TOKEN; const server = process.env.MATRIX_HOME_SERVER; if (!token || !server) { console.error('❌ Missing required environment variables!'); console.error(' Required: MATRIX_ACCESS_TOKEN, MATRIX_HOME_SERVER'); console.error(' See .env.example for guidance.'); process.exit(1); } console.log(`🏠 Server: ${server}`); try { console.log('âŗ Connecting to Matrix...'); await client.startClient(); console.log('✅ MatrixClient connected and authenticated\n'); console.log('âŗ Waiting for initial sync to complete...'); await new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Sync timeout: Initial sync took too long.')); }, 45000); client.on('sync', (sync) => { if (sync.result === 'ok') { clearTimeout(timeout); resolve(); } else if (sync.result === 'error') { reject(new Error(`Sync failed with error: ${sync.error}`)); } }); }); if (!client.user || !client.user.userId) { throw new Error('User identity not resolved after sync.'); } console.log(`👤 Bot Identity: ${client.user.userId}`); console.log(`🏠 Home Server: ${client.user.homeserver}`); let lastSentMessage = null; client.on('RoomMember#joined', async (member) => { const room = member.room; console.log(`✅ Joined room: ${room.getCanonicalRoomName() || room.roomId}`); room.on('EventMessage', async (event, stateEvent) => { const content = event.getContent(); if (!content || !content.body) return; const body = content.body.toLowerCase().trim(); const cmd = (process.env.BOT_COMMAND || '!quote').toLowerCase(); if (body === cmd || body.startsWith(cmd)) { if (!lastSentMessage || Date.now() - lastSentMessage > 300) { const quote = quotes[Math.floor(Math.random() * quotes.length)]; try { await room.sendMessage(quote); console.log(`🎃 Quote: ${quote}`); lastSentMessage = Date.now(); } catch (err) { console.error('Failed to send quote:', err.message); } } } }); member.room.on('RoomMember#left', () => { console.log(`â„šī¸ Left room: ${member.room.getCanonicalRoomName() || member.room.roomId}`); }); }); client.on('EventMessage', async (event, stateEvent) => { const room = event.sender.room; const content = event.getContent(); if (!content || !content.body) return; const body = content.body.toLowerCase().trim(); const cmd = (process.env.BOT_COMMAND || '!quote').toLowerCase(); const roomMembers = await room.getJoinedMemberCount(); if (roomMembers > 0 && room.getMyMembership()) { if (body === cmd || body.startsWith(cmd)) { if (!lastSentMessage || Date.now() - lastSentMessage > 300) { const quote = quotes[Math.floor(Math.random() * quotes.length)]; try { await room.sendMessage(quote); console.log(`🎃 Quote: ${quote}`); lastSentMessage = Date.now(); } catch (err) { console.error('Failed to send quote:', err.message); } } } } }); console.log('🤖 Bot is running!'); console.log(' Invite this bot to a Matrix room and type !quote to get an Ancestor quote.\n'); } catch (err) { console.error('❌ Fatal error starting bot:', err.message); console.error(err.stack); process.exit(1); } process.on('unhandledRejection', (reason, promise) => { console.error('🚨 Unhandled rejection:', reason?.message || reason); process.exit(1); }); setInterval(() => {}, 60000); })();