import { WASocket, DisconnectReason } from '@whiskeysockets/baileys'; import { Boom } from '@hapi/boom'; import { NormalizedMessage, NormalizedReaction } from '@tower/types'; import { createWhatsAppSession } from './session'; import { createLogger } from '@tower/logger'; const logger = createLogger('session-pool'); // Callbacks the pool exposes — accountId injected by pool, not caller export type PoolMessageCallback = (msg: NormalizedMessage, accountId: string) => Promise | void; export type PoolReactionCallback = (reaction: NormalizedReaction, accountId: string) => Promise | void; // groups typed as `any` to avoid leaking GroupMetadata (Baileys type) into main.ts export type PoolGroupsCallback = (groups: any, accountId: string) => Promise | void; export class WhatsAppSessionPool { private sessions = new Map(); async add( accountId: string, sessionPath: string, onMessage: PoolMessageCallback, onReaction: PoolReactionCallback, onGroups: PoolGroupsCallback, ): Promise { logger.info({ accountId }, 'Starting session'); const sock = await createWhatsAppSession( accountId, sessionPath, (msg) => onMessage(msg, accountId), (reaction) => onReaction(reaction, accountId), (groups) => onGroups(groups, accountId), (newSocket) => { logger.info({ accountId }, 'Session reconnected — updating pool'); this.sessions.set(accountId, newSocket); }, ); this.sessions.set(accountId, sock); } get(accountId: string): WASocket | undefined { return this.sessions.get(accountId); } getAll(): Map { return this.sessions; } async sendMessage(accountId: string, groupJid: string, text: string): Promise { const sock = this.sessions.get(accountId); if (!sock) { const available = Array.from(this.sessions.keys()).join(', ') || 'none'; throw new Error(`No active session for account ${accountId}. Active accounts: [${available}]`); } await sock.sendMessage(groupJid, { text }); } async remove(accountId: string): Promise { const sock = this.sessions.get(accountId); if (sock) { await sock.logout().catch(() => {}); this.sessions.delete(accountId); logger.info({ accountId }, 'Session removed'); } } async closeAll(): Promise { logger.info({ count: this.sessions.size }, 'Closing all WhatsApp sessions'); for (const [accountId, sock] of this.sessions) { try { sock.end(new Boom('Shutdown', { statusCode: DisconnectReason.loggedOut })); logger.info({ accountId }, 'Session closed'); } catch (err) { logger.error({ accountId, err }, 'Error closing session'); } } this.sessions.clear(); } }