import { join } from 'path'; import { Main } from './Main'; import { pathExists, readJSON, writeJSON } from 'fs-extra'; const PREFIX = '[Twilio]'; export class TwilioHandler { private _Main: Main; counter: { date: string; count: number; } = { date: new Date().toLocaleDateString('nl-NL'), count: 0, }; client; constructor(Main: Main) { this._Main = Main; } canSendMessage() { const maxMessages = this._Main.Config.twilio.maxMessagesPerDay; return this.counter.count < maxMessages; } async incrementCounter() { const today = new Date().toLocaleDateString('nl-NL'); if (this.counter.date !== today) { this.counter.date = today; this.counter.count = 0; } this.counter.count++; await writeJSON(join(this._Main.dataPath, 'twilio.json'), this.counter); } async load() { const twilioConfigPath = join(this._Main.dataPath, 'twilio.json'); const twilioConfigExists = await pathExists(twilioConfigPath); if (twilioConfigExists) { this.counter = await readJSON(twilioConfigPath); } this.client = require('twilio')( this._Main.Config.twilio.accountSid, this._Main.Config.twilio.authToken, ); } async resetError(category: TwilioCategories) { if (!this.lastErrors.has(category)) return; this.lastErrors.delete(category); } private lastErrors: Map = new Map(); private errorLog: string[] = []; private errorTimeout: NodeJS.Timeout; async sendError(category: TwilioCategories, error: string) { if (this.lastErrors.get(category) === error) return; this.lastErrors.set(category, error); if (error == null) return; this.errorLog.push(`${category}: ${error}`); clearTimeout(this.errorTimeout); this.errorTimeout = setTimeout(async () => { const errorMessage = this.errorLog .map((error) => `- ${error}`) .join('\n'); this.errorLog = []; if (!this.canSendMessage()) return console.log(PREFIX, 'Max messages per day reached'); await this.incrementCounter(); console.log(PREFIX, `Sending to Twilio:\n`, errorMessage); const promises = this._Main.Config.twilio.toNumbers.map( (toNumber) => this.sendMessage(toNumber, errorMessage), ); await Promise.all(promises); }, this._Main.Config.twilio.aggregateTimeout); } sendMessage(to: string, message: string) { return new Promise((resolve) => { this.client.messages .create({ body: message, from: `${this._Main.Config.twilio.fromNumber}`, to: to, }) .then((message: any) => { console.log(`Twilio message sent with SID: ${message.sid}`); resolve(true); }) .catch((error: any) => { console.error('Error sending Twilio message:', error); resolve(false); }); }); } } type TwilioCategories = | 'CameraRunner' | 'UnityRunner' | 'UnityWebSocket' | 'Status' | 'Audio';