whatsapp.js (5780B)
1 const { 2 default: makeWASocket, 3 DisconnectReason, 4 fetchLatestBaileysVersion 5 } = require('@whiskeysockets/baileys'); 6 const pino = require('pino'); 7 const { db } = require('./firebase'); 8 const { useFirestoreAuthState } = require('./authState'); 9 10 // --- BAILEYS SETUP --- 11 let qrCodeData = null; 12 let sock = null; 13 let isConnected = false; 14 15 let consecutiveAuthFailures = 0; 16 let consecutiveConnectFailures = 0; 17 18 async function startWhatsApp() { 19 const logger = pino({ level: 'silent' }); 20 21 let authResult; 22 try { 23 authResult = await useFirestoreAuthState(db, 'whatsapp_auth'); 24 } catch (err) { 25 consecutiveAuthFailures++; 26 const backoff = Math.min(5000 * Math.pow(2, consecutiveAuthFailures), 300000); // cap 5 min 27 console.error(`System: Auth state read failed (attempt ${consecutiveAuthFailures}). Retrying in ${backoff / 1000}s.`); 28 setTimeout(startWhatsApp, backoff); 29 return; 30 } 31 consecutiveAuthFailures = 0; // Reset on success 32 33 const { state, saveCreds, clearState } = authResult; 34 const { version } = await fetchLatestBaileysVersion(); 35 36 console.log("System: Connecting to WhatsApp servers..."); 37 38 sock = makeWASocket({ 39 version, 40 logger, 41 auth: state, 42 browser: ["WhatsApp Logger v4.2.1", "Chrome", "4.2.1"], 43 syncFullHistory: true 44 }); 45 46 sock.ev.on('connection.update', async (update) => { 47 const { connection, lastDisconnect, qr } = update; 48 49 if (qr) { 50 console.log("System: No valid credentials. New QR Code generated."); 51 qrCodeData = qr; 52 isConnected = false; 53 } 54 55 if (connection === 'close') { 56 isConnected = false; 57 const statusCode = lastDisconnect?.error?.output?.statusCode; 58 const shouldReconnect = statusCode !== DisconnectReason.loggedOut; 59 60 if (shouldReconnect) { 61 consecutiveConnectFailures++; 62 const backoff = Math.min(5000 * Math.pow(2, consecutiveConnectFailures), 300000); // cap 5 min 63 console.log(`System: Connection closed (Status: ${statusCode}). Reconnecting in ${backoff / 1000}s...`); 64 setTimeout(startWhatsApp, backoff); 65 } else { 66 console.log("System: Device Logged Out. Wiping session from Firestore."); 67 await clearState(); 68 qrCodeData = null; 69 consecutiveConnectFailures = 0; 70 startWhatsApp(); 71 } 72 } else if (connection === 'open') { 73 console.log("System: Connection Open and Authenticated. Firebase Auth Sync Active."); 74 qrCodeData = null; 75 isConnected = true; 76 consecutiveConnectFailures = 0; // Reset on success 77 } 78 }); 79 80 sock.ev.on('creds.update', saveCreds); 81 82 sock.ev.on('contacts.upsert', async (contacts) => { 83 for (const contact of contacts) { 84 let updateData = {}; 85 const displayName = contact.name || contact.notify; 86 87 if (displayName) updateData.displayName = displayName; 88 89 if (contact.id && contact.id.endsWith('@s.whatsapp.net')) { 90 updateData.phoneNumber = contact.id.split('@')[0]; 91 } 92 93 const primaryId = contact.lid || contact.id; 94 95 if (primaryId && Object.keys(updateData).length > 0) { 96 try { 97 await db.collection('Chats').doc(primaryId).set(updateData, { merge: true }); 98 99 if (contact.lid && contact.id !== contact.lid) { 100 await db.collection('Chats').doc(contact.id).set(updateData, { merge: true }); 101 } 102 } catch (err) {} 103 } 104 } 105 }); 106 107 sock.ev.on('messages.upsert', async ({ messages, type }) => { 108 if (type !== 'notify' && type !== 'append') return; 109 110 for (const msg of messages) { 111 try { 112 if (!msg.message) continue; 113 114 const remoteJid = msg.key.remoteJid; 115 if (remoteJid === 'status@broadcast') continue; 116 117 const textContent = 118 msg.message.conversation || 119 msg.message.extendedTextMessage?.text || 120 msg.message.imageMessage?.caption || 121 msg.message.videoMessage?.caption || 122 ""; 123 124 if (!textContent) continue; 125 126 const timestamp = msg.messageTimestamp 127 ? (typeof msg.messageTimestamp === 'number' ? msg.messageTimestamp : msg.messageTimestamp.low) 128 : Math.floor(Date.now() / 1000); 129 130 const isFromMe = msg.key.fromMe || false; 131 const senderName = isFromMe ? "Me" : (msg.pushName || "Unknown"); 132 133 // 1. Ensure Chat Document Exists 134 await db.collection('Chats').doc(remoteJid).set({ 135 lastActive: timestamp, 136 id: remoteJid, 137 preview: textContent 138 }, { merge: true }); 139 140 // 2. Save Message 141 await db.collection('Chats') 142 .doc(remoteJid) 143 .collection('Messages') 144 .doc(msg.key.id) 145 .set({ 146 text: textContent, 147 senderId: remoteJid, 148 senderName: senderName, 149 timestamp: timestamp, 150 fromMe: isFromMe, 151 id: msg.key.id 152 }, { merge: true }); 153 154 } catch (err) {} 155 } 156 }); 157 } 158 159 module.exports = { 160 startWhatsApp, 161 getQrCodeData: () => qrCodeData, 162 getIsConnected: () => isConnected 163 };