import https from 'https'; import logger from '../utils/logger.js'; import { config } from '../config/index.js'; export class Notifier { private isNotificationEnabled: boolean = true; private readonly phoneNumbers: string[] = config.NOTIFICATION_PHONE_NUMBERS.length > 0 ? config.NOTIFICATION_PHONE_NUMBERS : []; // No default — must be configured via NOTIFICATION_PHONE_NUMBERS env var /** * Sets the notification status. * @param status - True to enable, false to disable. */ public setNotificationStatus(status: boolean): void { this.isNotificationEnabled = status; logger.info(`[Notifier] WhatsApp notifications are now ${status ? 'ENABLED' : 'DISABLED'}`); } /** * Gets the current notification status. */ public getNotificationStatus(): boolean { return this.isNotificationEnabled; } /** * Sends a WhatsApp message to all configured recipients. * @param message - The content to send. */ public async sendAlert(message: string): Promise { if (!this.isNotificationEnabled) { logger.info(`[Notifier] Notifications disabled. Skipping message: ${message.substring(0, 30)}...`); return; } if (this.phoneNumbers.length === 0) { logger.warn(`[Notifier] No phone numbers configured (set NOTIFICATION_PHONE_NUMBERS env var). Skipping.`); return; } const sendPromises = this.phoneNumbers.map(number => this.sendSingleWhatsAppMessage(number, message)); try { await Promise.all(sendPromises); logger.info(`[Notifier] Alerts broadcasted to ${this.phoneNumbers.length} recipients.`); } catch (error) { logger.error('[Notifier] Broadcast failed partially/fully.'); } } /** * Sends a WhatsApp message to a specific number using ZenHustles API. */ private sendSingleWhatsAppMessage(to: string, message: string): Promise { return new Promise((resolve, reject) => { const data = JSON.stringify({ message: message, toWhatsAppNumber: to }); const options = { hostname: config.NOTIFICATION_API_HOST, port: 443, path: config.NOTIFICATION_API_PATH, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } }; const req = https.request(options, (res) => { let responseBody = ''; res.on('data', (chunk) => { responseBody += chunk; }); res.on('end', () => { if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { try { const parsed = JSON.parse(responseBody); logger.info(`[Notifier] Successfully sent to ${to}`); resolve(parsed); } catch (e) { logger.info(`[Notifier] Successfully sent to ${to} (Non-JSON response)`); resolve(responseBody); } } else { logger.error(`[Notifier] Failed for ${to}. Status: ${res.statusCode}, Body: ${responseBody}`); reject(new Error(`Status ${res.statusCode}`)); } }); }); req.on('error', (error) => { logger.error(`[Notifier] Network error for ${to}: ${error.message}`); reject(error); }); req.write(data); req.end(); }); } }