From a4771aaf0557618e62ac0abb653a849d73e4cb1a Mon Sep 17 00:00:00 2001 From: maaz519 Date: Wed, 27 May 2026 15:33:46 +0530 Subject: [PATCH] feat: add Baileys WhatsApp session with reconnect logic Co-Authored-By: Claude Sonnet 4.6 --- apps/worker/package.json | 1 + apps/worker/src/whatsapp/session.ts | 58 +++++++++++++++++++++++++++++ pnpm-lock.yaml | 15 ++++++++ 3 files changed, 74 insertions(+) create mode 100644 apps/worker/src/whatsapp/session.ts diff --git a/apps/worker/package.json b/apps/worker/package.json index 69aef00..625849d 100644 --- a/apps/worker/package.json +++ b/apps/worker/package.json @@ -8,6 +8,7 @@ "test": "jest" }, "dependencies": { + "@hapi/boom": "^10.0.1", "@tower/config": "workspace:*", "@tower/logger": "workspace:*", "@tower/types": "workspace:*", diff --git a/apps/worker/src/whatsapp/session.ts b/apps/worker/src/whatsapp/session.ts new file mode 100644 index 0000000..dccac70 --- /dev/null +++ b/apps/worker/src/whatsapp/session.ts @@ -0,0 +1,58 @@ +import makeWASocket, { + useMultiFileAuthState, + fetchLatestBaileysVersion, + DisconnectReason, + WASocket, + proto, + GroupMetadata, +} from '@whiskeysockets/baileys'; +import { Boom } from '@hapi/boom'; +import { createLogger } from '@tower/logger'; + +const logger = createLogger('whatsapp-session'); + +export type OnMessageCallback = (msg: proto.IWebMessageInfo) => void; +export type OnGroupsCallback = (groups: Record) => void; + +export async function createWhatsAppSession( + sessionPath: string, + onMessage: OnMessageCallback, + onGroups: OnGroupsCallback, +): Promise { + const { state, saveCreds } = await useMultiFileAuthState(sessionPath); + const { version } = await fetchLatestBaileysVersion(); + + const sock = makeWASocket({ + version, + auth: state, + printQRInTerminal: true, + logger: logger as any, + }); + + sock.ev.on('creds.update', saveCreds); + + sock.ev.on('connection.update', async ({ connection, lastDisconnect }) => { + if (connection === 'close') { + const reason = (lastDisconnect?.error as Boom)?.output?.statusCode; + const shouldReconnect = reason !== DisconnectReason.loggedOut; + logger.info({ reason, shouldReconnect }, 'Connection closed'); + if (shouldReconnect) { + logger.info('Reconnecting in 5s...'); + setTimeout(() => createWhatsAppSession(sessionPath, onMessage, onGroups), 5000); + } + } else if (connection === 'open') { + logger.info('WhatsApp connected'); + const groups = await sock.groupFetchAllParticipating(); + onGroups(groups); + } + }); + + sock.ev.on('messages.upsert', ({ messages, type }) => { + if (type !== 'notify') return; + for (const msg of messages) { + onMessage(msg); + } + }); + + return sock; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e326b69..d62e93e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -142,6 +142,9 @@ importers: apps/worker: dependencies: + '@hapi/boom': + specifier: ^10.0.1 + version: 10.0.1 '@tower/config': specifier: workspace:* version: link:../../packages/config @@ -459,9 +462,15 @@ packages: '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@hapi/boom@10.0.1': + resolution: {integrity: sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==} + '@hapi/boom@9.1.4': resolution: {integrity: sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==} + '@hapi/hoek@11.0.7': + resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==} + '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -4025,10 +4034,16 @@ snapshots: tslib: 2.8.1 optional: true + '@hapi/boom@10.0.1': + dependencies: + '@hapi/hoek': 11.0.7 + '@hapi/boom@9.1.4': dependencies: '@hapi/hoek': 9.3.0 + '@hapi/hoek@11.0.7': {} + '@hapi/hoek@9.3.0': {} '@img/colour@1.1.0': {}