Deploy: 2026-06-24 15:26:15
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const axios = require('axios');
|
||||
const { execSync } = require('child_process');
|
||||
const { Client, LocalAuth, MessageMedia } = require('whatsapp-web.js');
|
||||
|
||||
const sessions = new Map();
|
||||
const readyTimers = new Map();
|
||||
|
||||
const SESSIONS_DIR = path.join(__dirname, 'sessions');
|
||||
if (!fs.existsSync(SESSIONS_DIR)) {
|
||||
@@ -25,6 +27,7 @@ const puppeteerConfig = {
|
||||
'--disable-background-timer-throttling',
|
||||
'--disable-backgrounding-occluded-windows',
|
||||
'--disable-renderer-backgrounding',
|
||||
'--disable-session-crashed-bubble',
|
||||
'--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
|
||||
]
|
||||
};
|
||||
@@ -48,7 +51,9 @@ function cleanChromeLocks(sessionKey) {
|
||||
const sessionPath = path.join(SESSIONS_DIR, `session-${sessionKey}`);
|
||||
const lockFiles = [
|
||||
path.join(sessionPath, 'SingletonLock'),
|
||||
path.join(sessionPath, 'Default', 'SingletonLock')
|
||||
path.join(sessionPath, 'Default', 'SingletonLock'),
|
||||
path.join(sessionPath, 'SingletonCookie'),
|
||||
path.join(sessionPath, 'Default', 'SingletonCookie')
|
||||
];
|
||||
for (const lockFile of lockFiles) {
|
||||
try {
|
||||
@@ -62,16 +67,33 @@ function cleanChromeLocks(sessionKey) {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear Session Folder recursively to prevent corruption on logouts
|
||||
// Kill any Chrome processes still holding this session folder
|
||||
function killChromeProcessesForSession(sessionKey) {
|
||||
try {
|
||||
const sessionPath = path.join(SESSIONS_DIR, `session-${sessionKey}`);
|
||||
execSync(
|
||||
`lsof +D "${sessionPath}" 2>/dev/null | awk 'NR>1 {print $2}' | xargs -r kill -9 2>/dev/null; sleep 1`,
|
||||
{ stdio: 'ignore', timeout: 5000 }
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// Clear Session Folder with retry mechanism
|
||||
function clearSessionFolder(sessionKey) {
|
||||
const sessionPath = path.join(SESSIONS_DIR, `session-${sessionKey}`);
|
||||
try {
|
||||
if (fs.existsSync(sessionPath)) {
|
||||
console.log(`[CLEANUP] Recursively removing session folder for ${sessionKey}: ${sessionPath}`);
|
||||
fs.rmSync(sessionPath, { recursive: true, force: true });
|
||||
for (let attempt = 0; attempt < 3; attempt++) {
|
||||
try {
|
||||
if (fs.existsSync(sessionPath)) {
|
||||
console.log(`[CLEANUP] Removing session folder for ${sessionKey} (attempt ${attempt + 1})`);
|
||||
fs.rmSync(sessionPath, { recursive: true, force: true });
|
||||
}
|
||||
return;
|
||||
} catch (err) {
|
||||
console.warn(`[CLEANUP] Attempt ${attempt + 1} failed for ${sessionKey}: ${err.message}`);
|
||||
if (attempt < 2) {
|
||||
killChromeProcessesForSession(sessionKey);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`[CLEANUP ERROR] Failed to delete session folder for ${sessionKey}:`, err.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +146,21 @@ async function startSession(session_key, webhook_url) {
|
||||
console.log(`[Session] ${session_key} authenticated successfully.`);
|
||||
});
|
||||
|
||||
client.on('auth_failure', async (reason) => {
|
||||
console.error(`[Session] ${session_key} auth failure:`, reason);
|
||||
await sendWebhook(webhook_url, {
|
||||
session_key,
|
||||
state: 'auth_failure',
|
||||
reason: String(reason)
|
||||
});
|
||||
});
|
||||
|
||||
client.on('ready', async () => {
|
||||
console.log(`[Session] ${session_key} is ready and connected.`);
|
||||
if (readyTimers.has(session_key)) {
|
||||
clearTimeout(readyTimers.get(session_key));
|
||||
readyTimers.delete(session_key);
|
||||
}
|
||||
const phoneNumber = client.info.wid.user;
|
||||
await sendWebhook(webhook_url, {
|
||||
session_key,
|
||||
@@ -142,11 +177,29 @@ async function startSession(session_key, webhook_url) {
|
||||
reason: reason
|
||||
});
|
||||
sessions.delete(session_key);
|
||||
if (readyTimers.has(session_key)) {
|
||||
clearTimeout(readyTimers.get(session_key));
|
||||
readyTimers.delete(session_key);
|
||||
}
|
||||
killChromeProcessesForSession(session_key);
|
||||
try { client.destroy(); } catch (_) {}
|
||||
// Wipe session folder to prevent corrupted state loading on next boot
|
||||
clearSessionFolder(session_key);
|
||||
});
|
||||
|
||||
// Ready timeout: if ready doesn't fire within 90s, destroy and restart
|
||||
const readyTimeout = setTimeout(async () => {
|
||||
if (sessions.get(session_key) === client && (!client.info || !client.info.wid)) {
|
||||
console.warn(`[Session] ${session_key} timeout: ready not fired in 90s, restarting...`);
|
||||
sessions.delete(session_key);
|
||||
readyTimers.delete(session_key);
|
||||
killChromeProcessesForSession(session_key);
|
||||
try { client.destroy(); } catch (_) {}
|
||||
clearSessionFolder(session_key);
|
||||
await startSession(session_key, webhook_url);
|
||||
}
|
||||
}, 90000);
|
||||
readyTimers.set(session_key, readyTimeout);
|
||||
|
||||
// Handle Incoming Messages to Webhook
|
||||
client.on('message', async (msg) => {
|
||||
if (msg.fromMe) return;
|
||||
@@ -206,9 +259,17 @@ async function startSession(session_key, webhook_url) {
|
||||
async function disconnectSession(session_key) {
|
||||
const client = sessions.get(session_key);
|
||||
if (client) {
|
||||
if (readyTimers.has(session_key)) {
|
||||
clearTimeout(readyTimers.get(session_key));
|
||||
readyTimers.delete(session_key);
|
||||
}
|
||||
// Kill Chrome processes first to release file locks
|
||||
killChromeProcessesForSession(session_key);
|
||||
try {
|
||||
await client.logout();
|
||||
} catch (_) {}
|
||||
} catch (err) {
|
||||
console.warn(`[Session] Logout error for ${session_key}: ${err.message}`);
|
||||
}
|
||||
try {
|
||||
client.destroy();
|
||||
} catch (_) {}
|
||||
|
||||
@@ -92,7 +92,7 @@ app.post('/api/contacts/check', async (req, res) => {
|
||||
|
||||
if (session_key === 'auto') {
|
||||
const activeSlots = [];
|
||||
for (let i = 1; i <= 6; i++) {
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
if (isSessionReady(`slot-${i}`)) {
|
||||
activeSlots.push(`slot-${i}`);
|
||||
}
|
||||
@@ -140,7 +140,7 @@ app.post('/api/messages/send', async (req, res) => {
|
||||
try {
|
||||
if (session_key === 'auto') {
|
||||
let activeSlots = [];
|
||||
for (let i = 1; i <= 6; i++) {
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
if (isSessionReady(`slot-${i}`)) {
|
||||
activeSlots.push(`slot-${i}`);
|
||||
}
|
||||
@@ -185,13 +185,21 @@ app.post('/api/messages/send', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-start default sessions (6 slots)
|
||||
setTimeout(() => {
|
||||
console.log('🔄 Auto-starting 6 WhatsApp slots...');
|
||||
for (let i = 1; i <= 6; i++) {
|
||||
startSession(`slot-${i}`, 'https://otp.intaleqapp.com/api/whatsapp-webhook.php').catch(console.error);
|
||||
// Auto-start default sessions (3 slots, staggered every 20s)
|
||||
async function startSlotsSequentially() {
|
||||
console.log('🔄 Auto-starting 3 WhatsApp slots sequentially...');
|
||||
const WEBHOOK_URL = 'https://otp.intaleqapp.com/api/whatsapp-webhook.php';
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
console.log(`[Sequential] Starting slot-${i}...`);
|
||||
startSession(`slot-${i}`, WEBHOOK_URL).catch(err => {
|
||||
console.error(`[Sequential] slot-${i} failed:`, err.message);
|
||||
});
|
||||
if (i < 3) {
|
||||
await new Promise(resolve => setTimeout(resolve, 20000));
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
setTimeout(startSlotsSequentially, 2000);
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`🚀 Flash Call OTP WhatsApp Gateway running on port ${PORT}`);
|
||||
|
||||
Reference in New Issue
Block a user