Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,16 @@ export class BaileysStartupService extends ChannelStartupService {
if (full) {
return webMessageInfo[0];
}

// Baileys answers retry receipts with whatever getMessage returns. A message revoked with
// "delete for everyone" keeps its content in the Message table, so returning it here makes
// the deleted text reappear for the members whose devices ask for a retry.
if (await this.isMessageDeleted(key.id, webMessageInfo[0])) {
this.logger.verbose(`getMessage: message ${key.id} was deleted, skipping retry resend`);

return undefined;
}

if (webMessageInfo[0].message?.pollCreationMessage) {
const messageSecretBase64 = webMessageInfo[0].message?.messageContextInfo?.messageSecret;

Expand All @@ -677,6 +687,27 @@ export class BaileysStartupService extends ChannelStartupService {
}
}

// deleteMessage marks the row (status/key.deleted); both it and the messages.update revoke record a
// 'DELETED' MessageUpdate. The row lookup in deleteMessage is not scoped to the instance, so in a group
// shared by several instances the mark can land on another instance's copy; matching the MessageUpdate
// by instance + key id still finds it.
private async isMessageDeleted(keyId: string, row: any): Promise<boolean> {
if (!row) {
return false;
}

if (row.status === 'DELETED' || row.key?.deleted === true) {
return true;
}

const deletion = await this.prismaRepository.messageUpdate.findFirst({
where: { instanceId: this.instanceId, keyId, status: 'DELETED' },
select: { id: true },
});
Comment on lines +703 to +706

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Another instance’s message is deleted

When a shared-group deletion selects a different instance’s copy, deleteMessage marks the wrong instance’s row, leaving the requesting instance’s row live. Its deletion marker is absent or is later removed with the other row, so isMessageDeleted finds no deletion and a retry returns the content.

Scope the deleteMessage lookup to the current instance so it marks that instance’s message row.

Also at src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:1893-1925.

Prompt for AI agents
In `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts` at lines 703-706:

**Another instance’s message is deleted**

When a shared-group deletion selects a different instance’s copy, `deleteMessage` marks the wrong instance’s row, leaving the requesting instance’s row live. Its deletion marker is absent or is later removed with the other row, so `isMessageDeleted` finds no deletion and a retry returns the content.

Scope the `deleteMessage` lookup to the current instance so it marks that instance’s message row.

Also at `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts:1893-1925`.

Comment on lines +703 to +706

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Deletion checks scan update history

When an instance has accumulated many MessageUpdate rows, the lookup filters by instanceId, keyId, and status without a matching index, so retry checks scan the instance’s update rows and add increasing database work.

Add an index that supports the deletion lookup’s filter fields.

Prompt for AI agents
In `src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts` at lines 703-706:

**Deletion checks scan update history**

When an instance has accumulated many `MessageUpdate` rows, the lookup filters by `instanceId`, `keyId`, and `status` without a matching index, so retry checks scan the instance’s update rows and add increasing database work.

Add an index that supports the deletion lookup’s filter fields.


return !!deletion;
}

private async defineAuthState() {
const db = this.configService.get<Database>('DATABASE');
const cache = this.configService.get<CacheConf>('CACHE');
Expand Down Expand Up @@ -1889,8 +1920,9 @@ export class BaileysStartupService extends ChannelStartupService {
if (update.message === null && update.status === undefined) {
this.sendDataWebhook(Events.MESSAGES_DELETE, { ...key, status: 'DELETED' });

// Recorded as DELETED (not the SERVER_ACK fallback) so getMessage can refuse to resend it.
if (this.configService.get<Database>('DATABASE').SAVE_DATA.MESSAGE_UPDATE)
await this.prismaRepository.messageUpdate.create({ data: message });
await this.prismaRepository.messageUpdate.create({ data: { ...message, status: 'DELETED' } });

if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
this.chatwootService.eventWhatsapp(
Expand Down