diff --git a/whatsapp-gateway/baileys-client.js b/whatsapp-gateway/baileys-client.js index 4f63883..d6911c0 100644 --- a/whatsapp-gateway/baileys-client.js +++ b/whatsapp-gateway/baileys-client.js @@ -5,6 +5,9 @@ const fs = require('fs'); const path = require('path'); 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 const SESSIONS_DIR = path.join(__dirname, 'sessions'); @@ -14,26 +17,33 @@ if (!fs.existsSync(SESSIONS_DIR)) { async function sendWebhook(webhook_url, payload) { try { - console.log(`Sending webhook to ${webhook_url} with state ${payload.state}`); - await axios.post(webhook_url, payload, { + console.log(`[Webhook] Sending to ${webhook_url} | state=${payload.state}`); + const response = await axios.post(webhook_url, payload, { headers: { '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) { - 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) { // Return existing socket if it's already active if (sessions.has(session_key)) { + console.log(`[Session] ${session_key} already active, reusing`); return sessions.get(session_key); } + console.log(`[Session] Starting ${session_key} → webhook: ${webhook_url}`); + const sessionFolder = path.join(SESSIONS_DIR, session_key); const { state, saveCreds } = await useMultiFileAuthState(sessionFolder); @@ -52,7 +62,9 @@ async function startSession(session_key, webhook_url) { const { connection, lastDisconnect, qr } = update; 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, { session_key, state: 'waiting_qr', @@ -63,22 +75,30 @@ async function startSession(session_key, webhook_url) { if (connection === 'close') { const statusCode = lastDisconnect?.error?.output?.statusCode; 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) { - // Try reconnecting after a short delay + console.log(`[Connection] ${session_key} closed | code=${statusCode} | retry=${retries}/${MAX_RETRIES} | shouldReconnect=${shouldReconnect}`); + + if (shouldReconnect && retries <= MAX_RETRIES) { + // Try reconnecting with exponential backoff 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 { - // Number was banned or manually logged out from the phone - await disconnectSession(session_key); + // Either logged out, banned, or max retries exceeded + console.log(`[Connection] ${session_key} permanently closed. Cleaning up.`); + await cleanupSession(session_key); await sendWebhook(webhook_url, { session_key, state: 'disconnected' }); } } 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) const phone = sock.user.id.split(':')[0]; @@ -93,17 +113,41 @@ async function startSession(session_key, webhook_url) { 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); if (sock) { - try { sock.logout(); } catch (e) { } // best effort + try { sock.end(); } catch (e) { } // Gracefully close socket without logout 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); if (fs.existsSync(sessionFolder)) { 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}`); } }