feat(chatwoot): comprehensive improvements to message handling, editing, deletion and i18n

- Fix bidirectional message deletion between Chatwoot and WhatsApp
- Support deletion of multiple attachments sent together
- Implement proper message editing with 'Edited Message:' prefix format
- Enable deletion of edited messages by updating chatwootMessageId
- Skip cache for deleted messages (messageStubType === 1) to prevent duplicates
- Fix i18n translation path detection for production environment
- Add automatic dev/prod path resolution for translation files
- Improve error handling and logging for message operations

Technical improvements:
- Changed Chatwoot deletion query from findFirst to findMany for multiple attachments
- Fixed instanceId override issue in message deletion payload
- Added retry logic with Prisma MessageUpdate validation
- Implemented cache bypass for revoked messages to ensure proper processing
- Enhanced i18n to detect dist/ folder in production vs src/ in development

Resolves issues with:
- Message deletion not working from Chatwoot to WhatsApp
- Multiple attachments causing incomplete deletion
- Edited messages showing raw i18n keys instead of translated text
- Cache collision preventing deletion of edited messages
- Production environment not loading translation files correctly

Note: Tested and validated with Chatwoot v4.1 in production environment
This commit is contained in:
Anderson Silva
2025-10-03 14:47:24 -03:00
parent 78c7b96f0f
commit 6e1d027750
3 changed files with 407 additions and 97 deletions

View File

@@ -1065,6 +1065,11 @@ export class BaileysStartupService extends ChannelStartupService {
settings: any,
) => {
try {
// Garantir que localChatwoot está carregado antes de processar mensagens
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && !this.localChatwoot?.enabled) {
await this.loadChatwoot();
}
for (const received of messages) {
if (received.key.remoteJid?.includes('@lid') && (received.key as ExtendedMessageKey).senderPn) {
(received.key as ExtendedMessageKey).previousRemoteJid = received.key.remoteJid;
@@ -1445,12 +1450,17 @@ export class BaileysStartupService extends ChannelStartupService {
const cached = await this.baileysCache.get(updateKey);
if (cached) {
// Não ignorar mensagens deletadas (messageStubType === 1) mesmo que estejam em cache
const isDeletedMessage = update.messageStubType === 1;
if (cached && !isDeletedMessage) {
this.logger.info(`Message duplicated ignored [avoid deadlock]: ${updateKey}`);
continue;
}
await this.baileysCache.set(updateKey, true, 30 * 60);
if (!isDeletedMessage) {
await this.baileysCache.set(updateKey, true, 30 * 60);
}
if (status[update.status] === 'READ' && key.fromMe) {
if (this.configService.get<Chatwoot>('CHATWOOT').ENABLED && this.localChatwoot?.enabled) {
@@ -1550,8 +1560,22 @@ export class BaileysStartupService extends ChannelStartupService {
this.sendDataWebhook(Events.MESSAGES_UPDATE, message);
if (this.configService.get<Database>('DATABASE').SAVE_DATA.MESSAGE_UPDATE)
await this.prismaRepository.messageUpdate.create({ data: message });
if (this.configService.get<Database>('DATABASE').SAVE_DATA.MESSAGE_UPDATE) {
// Verificar se a mensagem ainda existe antes de criar o update
const messageExists = await this.prismaRepository.message.findFirst({
where: {
instanceId: message.instanceId,
key: {
path: ['id'],
equals: message.keyId,
},
},
});
if (messageExists) {
await this.prismaRepository.messageUpdate.create({ data: message });
}
}
const existingChat = await this.prismaRepository.chat.findFirst({
where: { instanceId: this.instanceId, remoteJid: message.remoteJid },