diff --git a/.env.dev b/.env.dev index b0e9719d..3a1ec0f6 100644 --- a/.env.dev +++ b/.env.dev @@ -65,7 +65,7 @@ INSTANCE_EXPIRATION_TIME=false CONFIG_SESSION_PHONE_CLIENT=CodeChat_V1 CONFIG_SESSION_PHONE_NAME=Edge -WA_VERSION=[ 2, 3000, 1025257277 ] +WA_VERSION=[ 2, 3000, 1023047013 ] # Set qrcode display limit QRCODE_LIMIT=5 @@ -89,13 +89,6 @@ PROVIDER_HOST=127.0.0.1 PROVIDER_PORT=5656 PROVIDER_PREFIX=codechat -# Proxy: (http|https|sock\d{1}) -# -# Proxy usado pelo WebSocket -WS_PROXY_URL= -# Proxy usado para upload/download de mídia -FETCH_PROXY_URL= - # URL em que a documetação será exibida # EX.: v1.dodmain.com API_BACKEND= diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 63e3fc6e..a81e33b0 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -193,6 +193,7 @@ export class WAStartupService { public client: WASocket; private authState: Partial = {}; private authStateProvider: AuthStateProvider; + private clientGeneration = 0; public async setInstanceName(name: string) { const i = await this.repository.instance.findUnique({ @@ -573,10 +574,16 @@ export class WAStartupService { public async connectToWhatsapp(): Promise { try { + // Remove listener from the old connection before creating a new one. + if (this.client) { + await this.closeBaileysSocket('reconnect'); + } + this.instanceQr.count = 0; await this.loadWebhook(); this.client = await this.setSocket(); - this.eventHandler(); + const gen = ++this.clientGeneration; + this.eventHandler(gen); return this.client; } catch (error) { @@ -1076,8 +1083,11 @@ export class WAStartupService { }, }; - private eventHandler() { + private eventHandler(gen: number) { this.client.ev.process((events) => { + // Do not process events from outdated/closed clients + if (gen !== this.clientGeneration) return; + if (!this.endSession) { if (events?.['connection.update']) { this.connectionUpdate(events['connection.update']); @@ -2527,4 +2537,64 @@ export class WAStartupService { throw new BadRequestException('Unable to leave the group', error.toString()); } } + + private registeredEvents: (keyof BaileysEventMap)[] = [ + 'connection.update', + 'creds.update', + 'messaging-history.set', + 'messages.upsert', + 'messages.update', + 'presence.update', + 'groups.upsert', + 'groups.update', + 'group-participants.update', + 'chats.upsert', + 'chats.update', + 'chats.delete', + 'contacts.upsert', + 'contacts.update', + 'call', + 'labels.association', + 'labels.edit' + ]; + + private removeAllBaileysListeners() { + for (const evName of this.registeredEvents) { + try { + this.client.ev.removeAllListeners(evName); + } catch {} + } +} + + private async closeBaileysSocket(reason = 'manual shutdown') { + if (!this.client) return; + + // 1) Stop processing events during teardown + this.endSession = true; + + // 2) Remove all listeners from the emitter + this.removeAllBaileysListeners(); + + // 3) Close the WebSocket (gracefully) + try { this.client.ws?.close(); } catch {} + + // 4) Ask Baileys to close the connection + try { this.client.end(new Error(reason)); } catch {} + + // 5) Wait for the 'close' event to ensure it has finished + await new Promise((resolve) => { + let done = false; + const finish = () => { if (!done) { done = true; resolve(); } }; + if (this.client?.ws?.once) { + this.client.ws.once('close', finish); + setTimeout(finish, 3000); // Safety timeout + } else { + finish(); + } + }); + + this.client = null as any; + this.endSession = false; + } + }