diff --git a/.gitignore b/.gitignore
index c82602a..56ecc27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ node_modules/
.wwebjs_auth/
.wwebjs_cache/
*.session
+.idea/
\ No newline at end of file
diff --git a/index.js b/index.js
index 0fe4efb..e711655 100644
--- a/index.js
+++ b/index.js
@@ -16,6 +16,11 @@ let starting = false;
let restarting = false;
let msgCounter = 0;
+let startTime = Date.now();
+let totalForwarded = 0;
+let userStats = {};
+let groupStats = {};
+
function ts() {
return new Date().toLocaleString('he-IL', { hour12: false });
}
@@ -76,6 +81,11 @@ async function flushQueue() {
try {
await sendSMS(text);
log('INFO', `Flushed ${batch.length} messages`);
+ for (const m of batch) {
+ userStats[m.sender] = (userStats[m.sender] || 0) + 1;
+ groupStats[m.group] = (groupStats[m.group] || 0) + 1;
+ totalForwarded++;
+ }
} catch (err) {
log('ERROR', `Flush failed: ${err.message}`);
messageQueue = batch.concat(messageQueue);
@@ -256,12 +266,149 @@ async function startClient() {
}
}
-/**
- * 🔥 FIX: detect detached frame / WhatsApp Web crashes
- */
+const http = require('http');
+function renderDashboard() {
+ const uptime = Math.floor((Date.now() - startTime) / 1000);
+ const h = Math.floor(uptime / 3600);
+ const m = Math.floor((uptime % 3600) / 60);
+ const s = uptime % 60;
+ const uptimeStr = `${h}h ${m}m ${s}s`;
+
+ const userRows = Object.entries(userStats)
+ .sort((a, b) => b[1] - a[1])
+ .map(([name, count]) =>
+ `
| ${name} | ${count} |
`
+ ).join('');
+
+ const groupRows = Object.entries(groupStats)
+ .sort((a, b) => b[1] - a[1])
+ .map(([name, count]) =>
+ `| ${name} | ${count} |
`
+ ).join('');
+
+ const connected = client && !restarting;
+
+ const api = JSON.stringify({
+ uptime, uptimeStr, connected,
+ totalForwarded, queued: messageQueue.length, flushTime: flushTime(),
+ userStats, groupStats,
+ });
+
+ return `
+
+
+
+
+OmegaBaSMS
+
+
+
+
+
OmegaBaSMS
+
WhatsApp group messages forwarded via SMS
+
+
+
+
+
+`;
+}
+
+const server = http.createServer((req, res) => {
+ if (req.url === '/liveness') {
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('OK');
+ return;
+ }
+ if (req.url === '/api/stats') {
+ res.writeHead(200, { 'Content-Type': 'application/json' });
+ res.end(JSON.stringify({
+ uptime: Math.floor((Date.now() - startTime) / 1000),
+ uptimeStr: (() => {
+ const t = Math.floor((Date.now() - startTime) / 1000);
+ return Math.floor(t/3600)+'h '+Math.floor((t%3600)/60)+'m '+t%60+'s';
+ })(),
+ connected: !!(client && !restarting),
+ totalForwarded,
+ queued: messageQueue.length,
+ flushTime: flushTime(),
+ userStats,
+ groupStats,
+ }));
+ return;
+ }
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
+ res.end(renderDashboard());
+});
+const PORT = process.env.PORT || 3000;
+server.listen(PORT, () => log('INIT', `Dashboard on http://0.0.0.0:${PORT}`));
+
+/** Recoverable error detection */
function shouldRestart(err) {
const msg = (err && err.message) || String(err);
- return msg.includes('detached Frame') ||
+ return msg.includes('detached') ||
msg.includes('Execution context was destroyed') ||
msg.includes('Target closed') ||
msg.includes('Session closed') ||
diff --git a/package-lock.json b/package-lock.json
index 11121c2..d8164c6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2573,4 +2573,4 @@
}
}
}
-}
\ No newline at end of file
+}