import * as Sentry from "@sentry/node"; import makeWASocket, { WASocket, Browsers, DisconnectReason, fetchLatestBaileysVersion, makeCacheableSignalKeyStore, makeInMemoryStore, isJidBroadcast, CacheStore } from "@whiskeysockets/baileys"; import makeWALegacySocket from "@whiskeysockets/baileys"; import P from "pino"; import Whatsapp from "../models/Whatsapp"; import { logger } from "../utils/logger"; import MAIN_LOGGER from "@whiskeysockets/baileys/lib/Utils/logger"; import authState from "../helpers/authState"; import { Boom } from "@hapi/boom"; import AppError from "../errors/AppError"; import { getIO } from "./socket"; import { Store } from "./store"; import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession"; import DeleteBaileysService from "../services/BaileysServices/DeleteBaileysService"; import NodeCache from 'node-cache'; const loggerBaileys = MAIN_LOGGER.child({}); loggerBaileys.level = "error"; type Session = WASocket & { id?: number; store?: Store; }; const sessions: Session[] = []; const retriesQrCodeMap = new Map(); export const getWbot = (whatsappId: number): Session => { const sessionIndex = sessions.findIndex(s => s.id === whatsappId); if (sessionIndex === -1) { throw new AppError("ERR_WAPP_NOT_INITIALIZED"); } return sessions[sessionIndex]; }; export const removeWbot = async ( whatsappId: number, isLogout = true ): Promise => { try { const sessionIndex = sessions.findIndex(s => s.id === whatsappId); if (sessionIndex !== -1) { if (isLogout) { sessions[sessionIndex].logout(); sessions[sessionIndex].ws.close(); } sessions.splice(sessionIndex, 1); } } catch (err) { logger.error(err); } }; export const initWASocket = async (whatsapp: Whatsapp): Promise => { return new Promise(async (resolve, reject) => { try { (async () => { const io = getIO(); const whatsappUpdate = await Whatsapp.findOne({ where: { id: whatsapp.id } }); if (!whatsappUpdate) return; const { id, name, provider } = whatsappUpdate; const { version, isLatest } = await fetchLatestBaileysVersion(); const isLegacy = provider === "stable" ? true : false; logger.info(`using WA v${version.join(".")}, isLatest: ${isLatest}`); logger.info(`isLegacy: ${isLegacy}`); logger.info(`Starting session ${name}`); let retriesQrCode = 0; let wsocket: Session = null; const store = makeInMemoryStore({ logger: loggerBaileys }); const { state, saveState } = await authState(whatsapp); const msgRetryCounterCache = new NodeCache(); const userDevicesCache: CacheStore = new NodeCache(); wsocket = makeWASocket({ logger: loggerBaileys, printQRInTerminal: false, browser: Browsers.appropriate("Desktop"), auth: { creds: state.creds, keys: makeCacheableSignalKeyStore(state.keys, logger), }, version, // defaultQueryTimeoutMs: 60000, // retryRequestDelayMs: 250, // keepAliveIntervalMs: 1000 * 60 * 10 * 3, msgRetryCounterCache, shouldIgnoreJid: jid => isJidBroadcast(jid), }); // wsocket = makeWASocket({ // version, // logger: loggerBaileys, // printQRInTerminal: false, // auth: state as AuthenticationState, // generateHighQualityLinkPreview: false, // shouldIgnoreJid: jid => isJidBroadcast(jid), // browser: ["Chat", "Chrome", "10.15.7"], // patchMessageBeforeSending: (message) => { // const requiresPatch = !!( // message.buttonsMessage || // // || message.templateMessage // message.listMessage // ); // if (requiresPatch) { // message = { // viewOnceMessage: { // message: { // messageContextInfo: { // deviceListMetadataVersion: 2, // deviceListMetadata: {}, // }, // ...message, // }, // }, // }; // } // return message; // }, // }) wsocket.ev.on( "connection.update", async ({ connection, lastDisconnect, qr }) => { logger.info( `Socket ${name} Connection Update ${connection || ""} ${lastDisconnect || "" }` ); if (connection === "close") { if ((lastDisconnect?.error as Boom)?.output?.statusCode === 403) { await whatsapp.update({ status: "PENDING", session: "" }); await DeleteBaileysService(whatsapp.id); io.to(`company-${whatsapp.companyId}-mainchannel`).emit(`company-${whatsapp.companyId}-whatsappSession`, { action: "update", session: whatsapp }); removeWbot(id, false); } if ( (lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut ) { removeWbot(id, false); setTimeout( () => StartWhatsAppSession(whatsapp, whatsapp.companyId), 2000 ); } else { await whatsapp.update({ status: "PENDING", session: "" }); await DeleteBaileysService(whatsapp.id); io.to(`company-${whatsapp.companyId}-mainchannel`).emit(`company-${whatsapp.companyId}-whatsappSession`, { action: "update", session: whatsapp }); removeWbot(id, false); setTimeout( () => StartWhatsAppSession(whatsapp, whatsapp.companyId), 2000 ); } } if (connection === "open") { await whatsapp.update({ status: "CONNECTED", qrcode: "", retries: 0 }); io.to(`company-${whatsapp.companyId}-mainchannel`).emit(`company-${whatsapp.companyId}-whatsappSession`, { action: "update", session: whatsapp }); const sessionIndex = sessions.findIndex( s => s.id === whatsapp.id ); if (sessionIndex === -1) { wsocket.id = whatsapp.id; sessions.push(wsocket); } resolve(wsocket); } if (qr !== undefined) { if (retriesQrCodeMap.get(id) && retriesQrCodeMap.get(id) >= 3) { await whatsappUpdate.update({ status: "DISCONNECTED", qrcode: "" }); await DeleteBaileysService(whatsappUpdate.id); io.to(`company-${whatsapp.companyId}-mainchannel`).emit("whatsappSession", { action: "update", session: whatsappUpdate }); wsocket.ev.removeAllListeners("connection.update"); wsocket.ws.close(); wsocket = null; retriesQrCodeMap.delete(id); } else { logger.info(`Session QRCode Generate ${name}`); retriesQrCodeMap.set(id, (retriesQrCode += 1)); await whatsapp.update({ qrcode: qr, status: "qrcode", retries: 0 }); const sessionIndex = sessions.findIndex( s => s.id === whatsapp.id ); if (sessionIndex === -1) { wsocket.id = whatsapp.id; sessions.push(wsocket); } io.to(`company-${whatsapp.companyId}-mainchannel`).emit(`company-${whatsapp.companyId}-whatsappSession`, { action: "update", session: whatsapp }); } } } ); wsocket.ev.on("creds.update", saveState); store.bind(wsocket.ev); })(); } catch (error) { Sentry.captureException(error); console.log(error); reject(error); } }); };