diff --git a/CHANGELOG.md b/CHANGELOG.md index 790186755..d96c2103c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * [WIDGET-WORKS] Fix `PrismaClientValidationError` in `messages.update` (missing `Message` relation) by guarding `messageUpdate.create` calls. * [WIDGET-WORKS] Guard `messages.update` cache cleanup with a derived key and optional `MESSAGE_UPDATE_CACHE_DELETE_DISABLED` flag to prevent cache.delete crash-loops. * [WIDGET-WORKS] Save messages before Chatwoot sync in `/message/sendText` to avoid missing `chatwootMessageId` and log async sync failures. +* [WIDGET-WORKS] Skip Chatwoot sync in `messages.upsert` when a message key already has `chatwootMessageId` (handles Chatwoot automation echoes/duplicates). ### Features diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index ab1ecd0db..98832cc04 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1187,6 +1187,36 @@ export class BaileysStartupService extends ChannelStartupService { const messageRaw = this.prepareMessage(received); + const shouldSyncChatwoot = + this.configService.get('CHATWOOT').ENABLED && + this.localChatwoot?.enabled && + !received.key.id.includes('@broadcast'); + + if (shouldSyncChatwoot) { + const existingChatwootMessage = await this.prismaRepository.message.findFirst({ + where: { + instanceId: this.instanceId, // [WIDGET-WORKS] Scope Chatwoot dedupe to current instance + key: { path: ['id'], equals: received.key.id }, + chatwootMessageId: { not: null }, + }, + select: { + chatwootMessageId: true, + chatwootInboxId: true, + chatwootConversationId: true, + }, + }); + + if (existingChatwootMessage) { + this.logger.verbose( + `[WIDGET-WORKS] Message ${received.key.id} already synced to Chatwoot (ID: ${existingChatwootMessage.chatwootMessageId}), skipping duplicate sync`, + ); + + messageRaw.chatwootMessageId = existingChatwootMessage.chatwootMessageId; + messageRaw.chatwootInboxId = existingChatwootMessage.chatwootInboxId; + messageRaw.chatwootConversationId = existingChatwootMessage.chatwootConversationId; + } + } + const isMedia = received?.message?.imageMessage || received?.message?.videoMessage || @@ -1204,11 +1234,8 @@ export class BaileysStartupService extends ChannelStartupService { await this.client.readMessages([received.key]); } - if ( - this.configService.get('CHATWOOT').ENABLED && - this.localChatwoot?.enabled && - !received.key.id.includes('@broadcast') - ) { + if (shouldSyncChatwoot && !messageRaw.chatwootMessageId) { + // [WIDGET-WORKS] Skip Chatwoot sync if this message was already synced (DB fallback after cache miss) const chatwootSentMessage = await this.chatwootService.eventWhatsapp( Events.MESSAGES_UPSERT, { instanceName: this.instance.name, instanceId: this.instanceId },