const Framework = require('webex-node-bot-framework'); const config = require('./config'); const { startWebSocketServer, stopWebSocketServer } = require('./services/websocket'); const { handleStoreCommand, handleAnalyzeCommand, handleHelpCommand } = require('./bot/handlers'); const logger = require('./utils/logger'); const framework = new Framework({ token: config.webex.token, removeWebhooksOnStart: true, logLevel: config.logLevel, }); logger.info('Starting NetAnalyzer Framework', { botName: config.webex.name }); // Register command handlers (anchored at the start of the message so // "please analyze store 305" doesn't fire both store and analyze handlers). framework.hears( /^\s*help\b/i, handleHelpCommand, 'Show available commands (try: help, help store, help analyze)' ); framework.hears( /^\s*store\b/i, handleStoreCommand, 'store — info + network + server\nstore pos — POS devices\nstore ios — iOS devices' ); framework.hears( /^\s*analyze\b/i, handleAnalyzeCommand, 'analyze — health summary\nanalyze pos — POS health (broken only)\nanalyze ios — iOS health (broken only)' ); // Friendly fallback for anything that didn't match the commands above. framework.hears( /.*/, (bot, trigger) => { logger.info('Unhandled message', { text: trigger.message.text }); bot.say( 'I heard: ' + trigger.message.text + '\n\nTry `store `, `store pos`, `analyze `, or `help` for commands.' ); }, 99999 ); framework.on('initialized', () => { logger.info('Framework initialized and connected via WebSocket'); }); framework.on('spawn', (bot, _id, addedBy) => { logger.info('Bot spawned in room', { room: bot.room.title || 'Unknown' }); if (addedBy) { bot.say( 'NetAnalyzer is ready!\n\nTry `store 782`, `store 782 pos`, `analyze 782`, or type `help` for all commands.' ); } }); framework .start() .then(() => { logger.info('WebSocket server for remote agent starting'); startWebSocketServer(); }) .catch(err => { logger.error('Error starting framework', { error: err.message }); process.exit(1); }); // ==================== Graceful Shutdown ==================== let isShuttingDown = false; async function shutdown(signal) { if (isShuttingDown) return; isShuttingDown = true; logger.info('Shutting down gracefully', { signal }); try { stopWebSocketServer(); // webex-node-bot-framework may not expose a clean stop; do what we can. if (framework && typeof framework.stop === 'function') { await framework.stop().catch(() => {}); } logger.info('Cleanup complete. Exiting.'); } catch (err) { logger.error('Error during shutdown', { error: err.message }); } finally { process.exit(0); } } process.on('SIGINT', () => shutdown('SIGINT')); process.on('SIGTERM', () => shutdown('SIGTERM')); process.on('uncaughtException', err => { logger.error('Uncaught Exception', { error: err.message, stack: err.stack }); shutdown('uncaughtException'); }); process.on('unhandledRejection', reason => { logger.error('Unhandled Rejection', { reason: reason instanceof Error ? reason.message : String(reason), }); });