Files
kitchen-agent/abandoned-call-poller.js
odzugkoev 85cf732a61 done
2026-03-01 17:10:03 -05:00

144 lines
3.9 KiB
JavaScript

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;