require("dotenv").config(); const cron = require("node-cron"); const os = require("os"); const Discord = require('discord.js'); const mysql = require('mysql'); const { TimeSpan } = require("./timeSpan"); const { DateBuilder } = require("./dateBuilder"); const { CallStats } = require("./callStats"); const { CallRecord, Records } = require("./records"); const fs = require('fs').promises; const fsSync = require('fs'); const hook = !!process.env.DISCORD_WEBHOOK_URL ? new Discord.WebhookClient({ url: process.env.DISCORD_WEBHOOK_URL }) : null; const JSON_FILE = process.env.JSON_FILE || "records.json"; const records = fsSync.existsSync(JSON_FILE) ? Records.fromJSONFile(JSON_FILE) : new Records(); function getYesterday() { return new TimeSpan( new DateBuilder().addDays(-1).atStartOfDay().build().getTime(), new DateBuilder().addDays(-1).atEndOfDay().build().getTime() ); } /** * @param {string} query * @param {any} params * @returns {Promise} */ async function queryScalarAsync(query, ...params) { const start = Date.now(); const connection = await mysql.createConnection({ host: process.env.DATABASE_HOST, user: process.env.DATABASE_USER, password: process.env.DATABASE_PASSWORD, database: process.env.DATABASE_NAME }); await connection.connect(); return new Promise((resolve, reject) => { connection.query(query, params, (err, results) => { if (err) { reject(err); } else { if (process.env.DATABASE_LOG_TIMINGS) console.log(`Query took ${Date.now() - start}ms:`, query, params, "=>", results); resolve(results[0][Object.keys(results[0])[0]]); } connection.end(); }); }); } /** * Fetch call statistics * @returns {Promise} */ async function getPreviousDayData() { const [ callsYesterday, callsThisMonth, callsTotal ] = await Promise.all([ queryScalarAsync(` SELECT COUNT(DISTINCT uniqueid) AS call_count FROM cdr WHERE calldate BETWEEN ? AND ?; `, getYesterday().startDate, getYesterday().endDate), queryScalarAsync(` SELECT COUNT(DISTINCT uniqueid) AS call_count FROM cdr WHERE MONTH (calldate) = ? AND YEAR (calldate) = ?; `, getYesterday().startDate.getMonth(), getYesterday().startDate.getFullYear()), queryScalarAsync(` SELECT COUNT(DISTINCT uniqueid) AS call_count FROM cdr; `) ]); const stats = new CallStats({ totalCallsMade: callsYesterday, totalCallsThisMonth: callsThisMonth, totalCallsEverPlaced: callsTotal }); console.log("Got stats:", stats, "built from query results:", { callsYesterday, callsThisMonth, callsTotal }); return stats; } function getSystemUptime() { const uptime = os.uptime(); const now = new Date(); return new TimeSpan(now - (uptime * 1000), now.getTime()); } /** * Update records with new data * @param {CallStats} callStats * @param {Records} records * @returns {CallStats} */ function updateRecords(callStats, records) { const yesterday = getYesterday().startDate; const yesterdayDateString = yesterday.toISOString().split('T')[0]; let isNewRecord = false; // Update all-time record const allTimeRecord = records.callRecord || new CallRecord({ date: yesterdayDateString, count: 0 }); if (!records.callRecord) { records.callRecord = { date: yesterdayDateString, count: callStats.totalCallsMade }; isNewRecord = true; } else if (allTimeRecord.count < callStats.totalCallsThisMonth) { allTimeRecord.count = callStats.totalCallsThisMonth; isNewRecord = true; } callStats.allTimeRecord = `${allTimeRecord.count} calls on ${allTimeRecord.date}`; // Update total calls ever placed records.totalCallsEverPlaced = callStats.totalCallsEverPlaced; // Update monthly totals if (!records.monthlyTotals) records.monthlyTotals = {}; if (!records.monthlyTotals[yesterday.getFullYear().toString()]) records.monthlyTotals[yesterday.getFullYear().toString()] = {}; records.monthlyTotals[yesterday.getFullYear().toString()][yesterday.getMonth().toString()] = callStats.totalCallsThisMonth; if (isNewRecord) { callStats.isNewRecord = true; } return callStats; } async function sendSummary() { console.log("Preparing summary."); const data = await getPreviousDayData(); console.log("Updating records..."); const stats = await updateRecords(data, records); console.log("Saving."); await records.toJSONFile(JSON_FILE); const yesterday = getYesterday(); await sendSummaryDiscord(yesterday, stats) await sendSummaryMatrix(yesterday, stats) } async function sendSummaryMatrix(yesterday, stats) { const html = ``` ${stats.isNewRecord ? `` : ''}
Summary from ${yesterday.startDate.toString()} to ${yesterday.endDate.toString()}
Calls Made${stats.totalCallsMade}
Monthly Total${stats.totalCallsThisMonth}
Total Calls Ever Placed${stats.totalCallsEverPlaced}
System Uptime${getSystemUptime().toString(false, false)}
All Time Record${stats.allTimeRecord}
🎉 NEW RECORD! 🎉 A new record has been set, at ${stats.totalCallsMade}!
```; var message = { "msgtype": "m.text", "format": "org.matrix.custom.html", "body": `Summary from ${new Date(yesterday.start).toDateString()} to ${new Date(yesterday.end).toDateString()}\n Calls Made: ${stats.totalCallsMade} Monthly Total: ${stats.totalCallsThisMonth} Total Calls Ever Placed: ${stats.totalCallsEverPlaced} System Uptime: ${getSystemUptime().toString(false, false)} All Time Record: ${stats.allTimeRecord} ${stats.isNewRecord ? `🎉 NEW RECORD! 🎉 A new record has been set, at ${stats.totalCallsMade}!` : ''}`, "formatted_body": html, "tel.litenet.call_stats_summary": { ...stats, date: yesterday } } console.log("Sending Matrix message:", JSON.stringify(message, null, 2)); } async function sendSummaryDiscord(yesterday, stats) { const makeField = (name, value) => ({ name, value: value === undefined ? "***ERR: UNDEFINED***" : value.toString(), inline: false }); let embed = { title: `Summary from to `, color: 0x1E90FF, fields: [ makeField("Calls Made", stats.totalCallsMade), makeField("Monthly Total", stats.totalCallsThisMonth), makeField("Total Calls Ever Placed", stats.totalCallsEverPlaced), makeField("System Uptime", getSystemUptime().toString(false, false)), makeField("All Time Record", stats.allTimeRecord) ], timestamp: new Date(), footer: {} } if (stats.isNewRecord) { embed.color = 0xFFD700; // Gold color for new record embed.fields.push(makeField("🎉 NEW RECORD! 🎉", `A new record has been set, at ${stats.totalCallsMade}!`)); } const payload = { embeds: [ embed ] }; console.log("Sending Discord message:", JSON.stringify(payload, null, 2)); if (hook) await hook.send(payload); } if (process.env.NOOP) { sendSummary(); return; } console.log("Scheduling..."); const schedule = cron.schedule("0 1 * * *", sendSummary);