79 lines
2.7 KiB
TypeScript
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();
|
|
}
|
|
}
|