Deploy: 2026-05-21 18:30:56
This commit is contained in:
@@ -5,6 +5,9 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const sessions = new Map(); // Store active sockets in memory
|
const sessions = new Map(); // Store active sockets in memory
|
||||||
|
const retryCounters = new Map(); // Track reconnection attempts per session
|
||||||
|
|
||||||
|
const MAX_RETRIES = 5; // Maximum reconnection attempts before giving up
|
||||||
|
|
||||||
// Local folder for saving auth keys
|
// Local folder for saving auth keys
|
||||||
const SESSIONS_DIR = path.join(__dirname, 'sessions');
|
const SESSIONS_DIR = path.join(__dirname, 'sessions');
|
||||||
@@ -14,26 +17,33 @@ if (!fs.existsSync(SESSIONS_DIR)) {
|
|||||||
|
|
||||||
async function sendWebhook(webhook_url, payload) {
|
async function sendWebhook(webhook_url, payload) {
|
||||||
try {
|
try {
|
||||||
console.log(`Sending webhook to ${webhook_url} with state ${payload.state}`);
|
console.log(`[Webhook] Sending to ${webhook_url} | state=${payload.state}`);
|
||||||
await axios.post(webhook_url, payload, {
|
const response = await axios.post(webhook_url, payload, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-Webhook-Secret': process.env.WEBHOOK_SECRET || 'fallback'
|
'X-Webhook-Secret': process.env.WEBHOOK_SECRET || ''
|
||||||
},
|
},
|
||||||
timeout: 5000
|
timeout: 10000
|
||||||
});
|
});
|
||||||
console.log(`Webhook sent successfully to ${webhook_url}`);
|
console.log(`[Webhook] ✅ Success | HTTP ${response.status}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Failed to send webhook to ${webhook_url}:`, err.message);
|
if (err.response) {
|
||||||
|
console.error(`[Webhook] ❌ HTTP ${err.response.status} | ${JSON.stringify(err.response.data)}`);
|
||||||
|
} else {
|
||||||
|
console.error(`[Webhook] ❌ Network Error: ${err.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startSession(session_key, webhook_url) {
|
async function startSession(session_key, webhook_url) {
|
||||||
// Return existing socket if it's already active
|
// Return existing socket if it's already active
|
||||||
if (sessions.has(session_key)) {
|
if (sessions.has(session_key)) {
|
||||||
|
console.log(`[Session] ${session_key} already active, reusing`);
|
||||||
return sessions.get(session_key);
|
return sessions.get(session_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[Session] Starting ${session_key} → webhook: ${webhook_url}`);
|
||||||
|
|
||||||
const sessionFolder = path.join(SESSIONS_DIR, session_key);
|
const sessionFolder = path.join(SESSIONS_DIR, session_key);
|
||||||
const { state, saveCreds } = await useMultiFileAuthState(sessionFolder);
|
const { state, saveCreds } = await useMultiFileAuthState(sessionFolder);
|
||||||
|
|
||||||
@@ -52,7 +62,9 @@ async function startSession(session_key, webhook_url) {
|
|||||||
const { connection, lastDisconnect, qr } = update;
|
const { connection, lastDisconnect, qr } = update;
|
||||||
|
|
||||||
if (qr) {
|
if (qr) {
|
||||||
// Forward the raw QR code string to PHP
|
console.log(`[QR] Generated for ${session_key}`);
|
||||||
|
// Reset retry counter on QR generation (session is alive)
|
||||||
|
retryCounters.set(session_key, 0);
|
||||||
await sendWebhook(webhook_url, {
|
await sendWebhook(webhook_url, {
|
||||||
session_key,
|
session_key,
|
||||||
state: 'waiting_qr',
|
state: 'waiting_qr',
|
||||||
@@ -63,22 +75,30 @@ async function startSession(session_key, webhook_url) {
|
|||||||
if (connection === 'close') {
|
if (connection === 'close') {
|
||||||
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
||||||
const shouldReconnect = statusCode !== DisconnectReason.loggedOut;
|
const shouldReconnect = statusCode !== DisconnectReason.loggedOut;
|
||||||
console.log(`Session ${session_key} connection closed. Reconnect: ${shouldReconnect}`);
|
const retries = (retryCounters.get(session_key) || 0) + 1;
|
||||||
|
retryCounters.set(session_key, retries);
|
||||||
|
|
||||||
if (shouldReconnect) {
|
console.log(`[Connection] ${session_key} closed | code=${statusCode} | retry=${retries}/${MAX_RETRIES} | shouldReconnect=${shouldReconnect}`);
|
||||||
// Try reconnecting after a short delay
|
|
||||||
|
if (shouldReconnect && retries <= MAX_RETRIES) {
|
||||||
|
// Try reconnecting with exponential backoff
|
||||||
sessions.delete(session_key);
|
sessions.delete(session_key);
|
||||||
setTimeout(() => startSession(session_key, webhook_url), 3000);
|
const delay = Math.min(retries * 3000, 15000); // 3s, 6s, 9s, 12s, 15s
|
||||||
|
console.log(`[Connection] Reconnecting ${session_key} in ${delay}ms...`);
|
||||||
|
setTimeout(() => startSession(session_key, webhook_url), delay);
|
||||||
} else {
|
} else {
|
||||||
// Number was banned or manually logged out from the phone
|
// Either logged out, banned, or max retries exceeded
|
||||||
await disconnectSession(session_key);
|
console.log(`[Connection] ${session_key} permanently closed. Cleaning up.`);
|
||||||
|
await cleanupSession(session_key);
|
||||||
await sendWebhook(webhook_url, {
|
await sendWebhook(webhook_url, {
|
||||||
session_key,
|
session_key,
|
||||||
state: 'disconnected'
|
state: 'disconnected'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (connection === 'open') {
|
} else if (connection === 'open') {
|
||||||
console.log(`Session ${session_key} connected successfully!`);
|
console.log(`[Connection] ✅ ${session_key} connected successfully!`);
|
||||||
|
retryCounters.set(session_key, 0); // Reset on successful connection
|
||||||
|
|
||||||
// Parse phone number from the JID (e.g. 9665XXXXXXX@s.whatsapp.net)
|
// Parse phone number from the JID (e.g. 9665XXXXXXX@s.whatsapp.net)
|
||||||
const phone = sock.user.id.split(':')[0];
|
const phone = sock.user.id.split(':')[0];
|
||||||
|
|
||||||
@@ -93,17 +113,41 @@ async function startSession(session_key, webhook_url) {
|
|||||||
return sock;
|
return sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function disconnectSession(session_key) {
|
/**
|
||||||
|
* Cleanup session: remove from memory and delete auth files
|
||||||
|
*/
|
||||||
|
async function cleanupSession(session_key) {
|
||||||
const sock = sessions.get(session_key);
|
const sock = sessions.get(session_key);
|
||||||
if (sock) {
|
if (sock) {
|
||||||
try { sock.logout(); } catch (e) { } // best effort
|
try { sock.end(); } catch (e) { } // Gracefully close socket without logout
|
||||||
sessions.delete(session_key);
|
sessions.delete(session_key);
|
||||||
}
|
}
|
||||||
|
retryCounters.delete(session_key);
|
||||||
|
|
||||||
// Completely wipe the auth directory so a fresh session can be created next time
|
// Wipe the auth directory so a fresh session can be created next time
|
||||||
const sessionFolder = path.join(SESSIONS_DIR, session_key);
|
const sessionFolder = path.join(SESSIONS_DIR, session_key);
|
||||||
if (fs.existsSync(sessionFolder)) {
|
if (fs.existsSync(sessionFolder)) {
|
||||||
fs.rmSync(sessionFolder, { recursive: true, force: true });
|
fs.rmSync(sessionFolder, { recursive: true, force: true });
|
||||||
|
console.log(`[Cleanup] Deleted session folder for ${session_key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect session: logout from WhatsApp and cleanup
|
||||||
|
*/
|
||||||
|
async function disconnectSession(session_key) {
|
||||||
|
const sock = sessions.get(session_key);
|
||||||
|
if (sock) {
|
||||||
|
try { sock.logout(); } catch (e) { } // best effort logout
|
||||||
|
sessions.delete(session_key);
|
||||||
|
}
|
||||||
|
retryCounters.delete(session_key);
|
||||||
|
|
||||||
|
// Completely wipe the auth directory
|
||||||
|
const sessionFolder = path.join(SESSIONS_DIR, session_key);
|
||||||
|
if (fs.existsSync(sessionFolder)) {
|
||||||
|
fs.rmSync(sessionFolder, { recursive: true, force: true });
|
||||||
|
console.log(`[Disconnect] Deleted session folder for ${session_key}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user