const config = require('./config'); const apiClient = require('./api-client'); class AbandonedCallPoller { constructor(database, printer) { this.db = database; this.printer = printer; this.intervalId = null; this.isPolling = false; } async start() { console.log('Starting abandoned call poller...'); this.poll(); this.scheduleNextPoll(); } scheduleNextPoll() { const appConfig = config.getAll(); const interval = parseInt(appConfig.abandonedCallPollingInterval, 10) || 30000; if (this.intervalId) { clearTimeout(this.intervalId); } this.intervalId = setTimeout(() => { this.poll(); this.scheduleNextPoll(); }, interval); } async poll() { if (this.isPolling) return; this.isPolling = true; try { const appConfig = config.getAll(); if (!appConfig.authToken || !appConfig.selectedBotId) { this.isPolling = false; return; } if (apiClient.isTokenNearExpiry(appConfig.tokenExpiry, 7)) { const refreshed = await apiClient.ensureValidToken(); if (!refreshed) { this.isPolling = false; return; } Object.assign(appConfig, config.getAll()); } const result = await apiClient.getAbandonedCalls( appConfig.authToken, appConfig.selectedBotId, { limit: 50 } ); if (result.error) { console.error('Abandoned calls poll error:', result.message); this.isPolling = false; return; } const calls = result.calls || []; for (const call of calls) { this.db.cacheAbandonedCall(call.id, call); } // Also fetch callback queue for the management page cache const queueResult = await apiClient.getAbandonedCallbackQueue( appConfig.authToken, appConfig.selectedBotId, 50 ); if (!queueResult.error && queueResult.queue) { for (const call of queueResult.queue) { this.db.cacheAbandonedCall(call.id, call); } } // Print new abandoned calls that haven't been printed yet await this.printNewAbandonedCalls(calls, appConfig); // Clean old cache periodically this.db.cleanOldAbandonedCallCache(7); } catch (error) { console.error('Abandoned call poll error:', error.message); } this.isPolling = false; } async printNewAbandonedCalls(calls, appConfig) { const printerConfigs = this.db.getAbandonedCallPrinters(); if (!printerConfigs || printerConfigs.length === 0) return; const cooldownSeconds = parseInt(appConfig.abandonedCallPrintCooldown, 10) || 300; const lastPrintTime = this.db.getLastAbandonedCallPrintTime(); const now = Math.floor(Date.now() / 1000); const printablePriorities = new Set(['critical', 'high', 'medium']); for (const call of calls) { if (this.db.hasAbandonedCallPrint(call.id)) continue; const priority = call.callback_priority || 'none'; if (!printablePriorities.has(priority)) continue; if (lastPrintTime && (now - lastPrintTime) < cooldownSeconds) { console.log(`Abandoned call #${call.id}: skipping print (cooldown active)`); continue; } try { const result = await this.printer.printAbandonedCallReceipt(call, printerConfigs); const printedCount = result ? (result.successCount || printerConfigs.length) : 0; this.db.addAbandonedCallPrint(call.id, printedCount); console.log(`Abandoned call #${call.id}: printed on ${printedCount} printer(s)`); } catch (err) { console.error(`Abandoned call #${call.id}: print failed:`, err.message); } } } stop() { if (this.intervalId) { clearTimeout(this.intervalId); this.intervalId = null; } console.log('Abandoned call poller stopped'); } restart() { this.stop(); this.start(); } } module.exports = AbandonedCallPoller;