Files
tower/apps/worker/src/whatsapp/session-pool.ts
T

79 lines
2.7 KiB
TypeScript

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> | void;
export type PoolReactionCallback = (reaction: NormalizedReaction, accountId: string) => Promise<void> | void;
// groups typed as `any` to avoid leaking GroupMetadata (Baileys type) into main.ts
export type PoolGroupsCallback = (groups: any, accountId: string) => Promise<void> | void;
export class WhatsAppSessionPool {
private sessions = new Map<string, WASocket>();
async add(
accountId: string,
sessionPath: string,
onMessage: PoolMessageCallback,
onReaction: PoolReactionCallback,
onGroups: PoolGroupsCallback,
): Promise<void> {
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<string, WASocket> {
return this.sessions;
}
async sendMessage(accountId: string, groupJid: string, text: string): Promise<void> {
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<void> {
const sock = this.sessions.get(accountId);
if (sock) {
await sock.logout().catch(() => {});
this.sessions.delete(accountId);
logger.info({ accountId }, 'Session removed');
}
}
async closeAll(): Promise<void> {
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();
}
}