From e838645531dd99d05d0c0e1d4297d09e5beff403 Mon Sep 17 00:00:00 2001 From: oxpoe Date: Fri, 9 Oct 2026 09:23:06 +0700 Subject: [PATCH] feat: notifikasi admin/monitoring via Telegram (WA fallback); alert staff dialihkan ke Telegram --- app-customer.js | 2 +- config/genieacs.js | 2 +- routes/adminPortal.js | 6 +++--- services/cronService.js | 2 +- services/customerService.js | 2 +- services/telegramBot.js | 16 +++++++++++++++- services/waNotifyService.js | 14 +++++++++++++- 7 files changed, 35 insertions(+), 9 deletions(-) diff --git a/app-customer.js b/app-customer.js index ef9eb77..63bc823 100644 --- a/app-customer.js +++ b/app-customer.js @@ -470,7 +470,7 @@ async function notifyAdminUnmatchedPayment(amount, rawText, service) { '', 'Belum ada tagihan yang cocok. Cocokkan jam notif dengan bukti transfer pelanggan, lalu tandai lunas manual.' ]; - await waNotify.sendToStaff(lines.join('\n')); + await waNotify.notifyAdmin(lines.join('\n')); } catch (e) {} } diff --git a/config/genieacs.js b/config/genieacs.js index 601f90b..9de8017 100644 --- a/config/genieacs.js +++ b/config/genieacs.js @@ -265,7 +265,7 @@ let sendMonitoringAlert = null; (async () => { try { const whatsappBot = await import('../services/whatsappBot.mjs'); - sendMonitoringAlert = whatsappBot.sendMonitoringAlert; + sendMonitoringAlert = require('../services/waNotifyService').notifyAdmin; logger.info('[GenieACS] WhatsApp monitoring alert integration loaded'); } catch (error) { logger.warn('[GenieACS] WhatsApp bot not available for monitoring alerts'); diff --git a/routes/adminPortal.js b/routes/adminPortal.js index 5cdc975..12898b2 100644 --- a/routes/adminPortal.js +++ b/routes/adminPortal.js @@ -2144,7 +2144,7 @@ router.post('/customers/:id/generate-pppoe', requireAdminSession, async (req, re '🔒 *PPPoE Pass:* *' + g.password + '*\n' + '📍 *Alamat:* ' + ((cust && cust.address) || '-') + '\n\n' + 'Silakan konfigurasi perangkat pelanggan dengan data di atas.'; - await waNotify.sendToStaff(credMsg); + await waNotify.notifyAdmin(credMsg); } catch (e) {} req.session._msg = { type: 'success', text: 'Akun PPPoE dibuat: ' + g.username + ' / ' + g.password + ' (kredensial dikirim ke admin & teknisi).' }; } catch (e) { req.session._msg = { type: 'error', text: 'Gagal generate PPPoE: ' + e.message }; } @@ -4119,7 +4119,7 @@ router.post('/registrations/:id/approve', requireAdminSession, express.urlencode '🔒 *PPPoE Pass:* *' + (pppoePass || '-') + '*\n' + '📍 *Alamat:* ' + (cust.address || '-') + '\n\n' + 'Silakan konfigurasi perangkat pelanggan dengan data di atas.'; - await waNotify.sendToStaff(credMsg); + await waNotify.notifyAdmin(credMsg); } } catch (e) { try { logger.error('[RegApprove] staff cred error: ' + e.message); } catch (_) {} } @@ -6749,7 +6749,7 @@ router.post('/whatsapp/preview/send', requireAdminSession, express.urlencoded({ if (!m) throw new Error('Pesan tidak ditemukan'); const waNotify = require('../services/waNotifyService'); const txt = '[CONTOH] ' + m.title + '\n\n' + m.text; - const r = await waNotify.sendToStaff(txt); + const r = await waNotify.notifyAdmin(txt); return res.json({ ok: true, message: 'Contoh "' + m.title + '" terkirim ke WA admin (sent=' + ((r && r.sent) || 0) + ').' }); } catch (e) { return res.status(500).json({ ok: false, message: e.message }); diff --git a/services/cronService.js b/services/cronService.js index 56c3739..0c4a664 100644 --- a/services/cronService.js +++ b/services/cronService.js @@ -824,7 +824,7 @@ async function sendAutoLunasRecap(pMonth, pYear) { const list = rows.map(function (r) { return '• ' + r.name + ' (Rp ' + Number(r.amount || 0).toLocaleString('id-ID') + ')'; }).join('\n'); const total = rows.reduce(function (s, r) { return s + Number(r.amount || 0); }, 0); const msg = '📋 *REKAP AUTO-LUNAS — Periode ' + m + '/' + y + '*\n\nJumlah : *' + rows.length + ' pelanggan*\nNominal : *Rp ' + total.toLocaleString('id-ID') + '*\n\nDaftar:\n' + list + '\n\nWaktu : ' + now.toLocaleString('id-ID'); - const to = await waNotify.sendToStaff(msg); + const to = await waNotify.notifyAdmin(msg); logger.info('[CRON] Rekap auto-lunas ' + m + '/' + y + ' terkirim ke admin (' + rows.length + ' pelanggan).'); return { sent: true, count: rows.length, period: m + '/' + y, to: to }; } catch (e) { logger.error('[CRON] rekap auto-lunas error: ' + (e && e.message)); return { sent: false, error: e && e.message }; } diff --git a/services/customerService.js b/services/customerService.js index ecd9d4f..8b5bc90 100644 --- a/services/customerService.js +++ b/services/customerService.js @@ -943,7 +943,7 @@ async function autoDeactivateOverdueCustomers(months = 3, actor = null) { const parts = []; if (processed) parts.push(`${processed} pelanggan NONAKTIF (menunggak ${m} bln)`); if (removed.length) parts.push(`${removed.length} pelanggan DIHAPUS (menunggak > ${m} bln)`); - await waNotify.sendToStaff(`📋 *Auto-Tunggakan* + await waNotify.notifyAdmin(`📋 *Auto-Tunggakan* ${parts.join('; ')}. Nonaktif: ${names.join(', ') || '-'} diff --git a/services/telegramBot.js b/services/telegramBot.js index 7ddc16a..bdeb560 100644 --- a/services/telegramBot.js +++ b/services/telegramBot.js @@ -998,4 +998,18 @@ function initTelegram() { } // Export for manual re-init from settings -module.exports = { initTelegram }; +async function notifyAdmin(text) { + try { + const adminId = String(getSetting('telegram_admin_id', '') || '').trim(); + if (!bot || !adminId || !text) return false; + const ids = adminId.split(/[,s]+/).filter(Boolean); + let ok = false; + for (const id of ids) { + try { await bot.sendMessage(id, text, { parse_mode: 'Markdown' }); ok = true; } + catch (e) { try { await bot.sendMessage(id, String(text)); ok = true; } catch (e2) {} } + } + return ok; + } catch (e) { return false; } +} + +module.exports = { initTelegram, notifyAdmin }; diff --git a/services/waNotifyService.js b/services/waNotifyService.js index 370db56..c6814f6 100644 --- a/services/waNotifyService.js +++ b/services/waNotifyService.js @@ -154,4 +154,16 @@ async function isConnected() { return val; } -module.exports = { sendText, sendImage, sendDocument, sendToStaff, isConnected, gateway }; +// Notifikasi untuk ADMIN/monitoring → Telegram (utama). WA (sendToStaff) sebagai fallback. +async function notifyAdmin(text) { + try { + const tg = require('./telegramBot'); + if (tg && typeof tg.notifyAdmin === 'function') { + const ok = await tg.notifyAdmin(text); + if (ok) return true; + } + } catch (e) {} + return sendToStaff(text); +} + +module.exports = { sendText, sendImage, sendDocument, sendToStaff, notifyAdmin, isConnected, gateway };