Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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=
Expand Down
74 changes: 72 additions & 2 deletions src/whatsapp/services/whatsapp.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export class WAStartupService {
public client: WASocket;
private authState: Partial<AuthState> = {};
private authStateProvider: AuthStateProvider;
private clientGeneration = 0;

public async setInstanceName(name: string) {
const i = await this.repository.instance.findUnique({
Expand Down Expand Up @@ -573,10 +574,16 @@ export class WAStartupService {

public async connectToWhatsapp(): Promise<WASocket> {
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) {
Expand Down Expand Up @@ -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']);
Expand Down Expand Up @@ -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<void>((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;
}

}
Loading