From 119ceba1cafefeba3acd7c3bf10e5cd6d7aeed73 Mon Sep 17 00:00:00 2001 From: pedro-php Date: Thu, 27 Mar 2025 11:13:22 -0300 Subject: [PATCH 1/6] Adding a new webhook that triggers when a message is updated by the user --- .env.example | 3 +++ Docker/swarm/evolution_api_v2.yaml | 2 ++ .../whatsapp/whatsapp.baileys.service.ts | 18 ++++++++++++++++-- .../chatwoot/services/chatwoot.service.ts | 2 +- src/api/integrations/event/event.controller.ts | 1 + src/api/types/wa.types.ts | 1 + src/config/env.config.ts | 6 ++++++ src/validate/instance.schema.ts | 3 +++ 8 files changed, 33 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 02eca612..1a320aa1 100644 --- a/.env.example +++ b/.env.example @@ -62,6 +62,7 @@ RABBITMQ_EVENTS_MESSAGES_EDITED=false RABBITMQ_EVENTS_MESSAGES_UPDATE=false RABBITMQ_EVENTS_MESSAGES_DELETE=false RABBITMQ_EVENTS_SEND_MESSAGE=false +RABBITMQ_EVENTS_SEND_MESSAGE_UPDATE=false RABBITMQ_EVENTS_CONTACTS_SET=false RABBITMQ_EVENTS_CONTACTS_UPSERT=false RABBITMQ_EVENTS_CONTACTS_UPDATE=false @@ -108,6 +109,7 @@ PUSHER_EVENTS_MESSAGES_EDITED=true PUSHER_EVENTS_MESSAGES_UPDATE=true PUSHER_EVENTS_MESSAGES_DELETE=true PUSHER_EVENTS_SEND_MESSAGE=true +PUSHER_EVENTS_SEND_MESSAGE_UPDATE=true PUSHER_EVENTS_CONTACTS_SET=true PUSHER_EVENTS_CONTACTS_UPSERT=true PUSHER_EVENTS_CONTACTS_UPDATE=true @@ -149,6 +151,7 @@ WEBHOOK_EVENTS_MESSAGES_EDITED=true WEBHOOK_EVENTS_MESSAGES_UPDATE=true WEBHOOK_EVENTS_MESSAGES_DELETE=true WEBHOOK_EVENTS_SEND_MESSAGE=true +WEBHOOK_EVENTS_SEND_MESSAGE_UPDATE=true WEBHOOK_EVENTS_CONTACTS_SET=true WEBHOOK_EVENTS_CONTACTS_UPSERT=true WEBHOOK_EVENTS_CONTACTS_UPDATE=true diff --git a/Docker/swarm/evolution_api_v2.yaml b/Docker/swarm/evolution_api_v2.yaml index 41c2daa2..7d3353d3 100644 --- a/Docker/swarm/evolution_api_v2.yaml +++ b/Docker/swarm/evolution_api_v2.yaml @@ -34,6 +34,7 @@ services: - RABBITMQ_EVENTS_MESSAGES_UPDATE=false - RABBITMQ_EVENTS_MESSAGES_DELETE=false - RABBITMQ_EVENTS_SEND_MESSAGE=false + - RABBITMQ_EVENTS_SEND_MESSAGE_UPDATE=false - RABBITMQ_EVENTS_CONTACTS_SET=false - RABBITMQ_EVENTS_CONTACTS_UPSERT=false - RABBITMQ_EVENTS_CONTACTS_UPDATE=false @@ -71,6 +72,7 @@ services: - WEBHOOK_EVENTS_MESSAGES_UPDATE=true - WEBHOOK_EVENTS_MESSAGES_DELETE=true - WEBHOOK_EVENTS_SEND_MESSAGE=true + - WEBHOOK_EVENTS_SEND_MESSAGE_UPDATE=true - WEBHOOK_EVENTS_CONTACTS_SET=true - WEBHOOK_EVENTS_CONTACTS_UPSERT=true - WEBHOOK_EVENTS_CONTACTS_UPDATE=true diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 10feb7ce..ab3dfb91 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1138,7 +1138,6 @@ export class BaileysStartupService extends ChannelStartupService { { instanceName: this.instance.name, instanceId: this.instance.id }, editedMessage, ); - await this.sendDataWebhook(Events.MESSAGES_EDITED, editedMessage); } } @@ -3899,10 +3898,25 @@ export class BaileysStartupService extends ChannelStartupService { } try { - return await this.client.sendMessage(jid, { + const messageSent = await this.client.sendMessage(jid, { ...(options as any), edit: data.key, }); + + const updatedMessage = + messageSent.message?.protocolMessage || messageSent.message?.editedMessage?.message?.protocolMessage; + + if (updatedMessage) { + if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot?.enabled) + this.chatwootService.eventWhatsapp( + 'send.message.update', + { instanceName: this.instance.name, instanceId: this.instance.id }, + updatedMessage, + ); + await this.sendDataWebhook(Events.SEND_MESSAGE_UPDATE, updatedMessage); + } + + return messageSent; } catch (error) { this.logger.error(error); throw new BadRequestException(error.toString()); diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index 77b58bbe..820b786c 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -2199,7 +2199,7 @@ export class ChatwootService { } } - if (event === 'messages.edit') { + if (event === 'messages.edit' || event === 'send.message.update') { const editedText = `${ body?.editedMessage?.conversation || body?.editedMessage?.extendedTextMessage?.text }\n\n_\`${i18next.t('cw.message.edited')}.\`_`; diff --git a/src/api/integrations/event/event.controller.ts b/src/api/integrations/event/event.controller.ts index 2e6a2330..008006a1 100644 --- a/src/api/integrations/event/event.controller.ts +++ b/src/api/integrations/event/event.controller.ts @@ -132,6 +132,7 @@ export class EventController { 'MESSAGES_UPDATE', 'MESSAGES_DELETE', 'SEND_MESSAGE', + 'SEND_MESSAGE_UPDATE', 'CONTACTS_SET', 'CONTACTS_UPSERT', 'CONTACTS_UPDATE', diff --git a/src/api/types/wa.types.ts b/src/api/types/wa.types.ts index 0aad0696..2bb3dc1e 100644 --- a/src/api/types/wa.types.ts +++ b/src/api/types/wa.types.ts @@ -15,6 +15,7 @@ export enum Events { MESSAGES_UPDATE = 'messages.update', MESSAGES_DELETE = 'messages.delete', SEND_MESSAGE = 'send.message', + SEND_MESSAGE_UPDATE = 'send.message.update', CONTACTS_SET = 'contacts.set', CONTACTS_UPSERT = 'contacts.upsert', CONTACTS_UPDATE = 'contacts.update', diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 78ca891c..2e99eab4 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -72,6 +72,7 @@ export type EventsRabbitmq = { MESSAGES_UPDATE: boolean; MESSAGES_DELETE: boolean; SEND_MESSAGE: boolean; + SEND_MESSAGE_UPDATE: boolean; CONTACTS_SET: boolean; CONTACTS_UPDATE: boolean; CONTACTS_UPSERT: boolean; @@ -131,6 +132,7 @@ export type EventsWebhook = { MESSAGES_UPDATE: boolean; MESSAGES_DELETE: boolean; SEND_MESSAGE: boolean; + SEND_MESSAGE_UPDATE: boolean; CONTACTS_SET: boolean; CONTACTS_UPDATE: boolean; CONTACTS_UPSERT: boolean; @@ -163,6 +165,7 @@ export type EventsPusher = { MESSAGES_UPDATE: boolean; MESSAGES_DELETE: boolean; SEND_MESSAGE: boolean; + SEND_MESSAGE_UPDATE: boolean; CONTACTS_SET: boolean; CONTACTS_UPDATE: boolean; CONTACTS_UPSERT: boolean; @@ -370,6 +373,7 @@ export class ConfigService { MESSAGES_UPDATE: process.env?.RABBITMQ_EVENTS_MESSAGES_UPDATE === 'true', MESSAGES_DELETE: process.env?.RABBITMQ_EVENTS_MESSAGES_DELETE === 'true', SEND_MESSAGE: process.env?.RABBITMQ_EVENTS_SEND_MESSAGE === 'true', + SEND_MESSAGE_UPDATE: process.env?.RABBITMQ_EVENTS_SEND_MESSAGE_UPDATE === 'true', CONTACTS_SET: process.env?.RABBITMQ_EVENTS_CONTACTS_SET === 'true', CONTACTS_UPDATE: process.env?.RABBITMQ_EVENTS_CONTACTS_UPDATE === 'true', CONTACTS_UPSERT: process.env?.RABBITMQ_EVENTS_CONTACTS_UPSERT === 'true', @@ -421,6 +425,7 @@ export class ConfigService { MESSAGES_UPDATE: process.env?.PUSHER_EVENTS_MESSAGES_UPDATE === 'true', MESSAGES_DELETE: process.env?.PUSHER_EVENTS_MESSAGES_DELETE === 'true', SEND_MESSAGE: process.env?.PUSHER_EVENTS_SEND_MESSAGE === 'true', + SEND_MESSAGE_UPDATE: process.env?.PUSHER_EVENTS_SEND_MESSAGE_UPDATE === 'true', CONTACTS_SET: process.env?.PUSHER_EVENTS_CONTACTS_SET === 'true', CONTACTS_UPDATE: process.env?.PUSHER_EVENTS_CONTACTS_UPDATE === 'true', CONTACTS_UPSERT: process.env?.PUSHER_EVENTS_CONTACTS_UPSERT === 'true', @@ -477,6 +482,7 @@ export class ConfigService { MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true', MESSAGES_DELETE: process.env?.WEBHOOK_EVENTS_MESSAGES_DELETE === 'true', SEND_MESSAGE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE === 'true', + SEND_MESSAGE_UPDATE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE_UPDATE === 'true', CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true', CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true', CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true', diff --git a/src/validate/instance.schema.ts b/src/validate/instance.schema.ts index 06c34df9..842d80b3 100644 --- a/src/validate/instance.schema.ts +++ b/src/validate/instance.schema.ts @@ -68,6 +68,7 @@ export const instanceSchema: JSONSchema7 = { 'MESSAGES_UPDATE', 'MESSAGES_DELETE', 'SEND_MESSAGE', + 'SEND_MESSAGE_UPDATE', 'CONTACTS_SET', 'CONTACTS_UPSERT', 'CONTACTS_UPDATE', @@ -104,6 +105,7 @@ export const instanceSchema: JSONSchema7 = { 'MESSAGES_UPDATE', 'MESSAGES_DELETE', 'SEND_MESSAGE', + 'SEND_MESSAGE_UPDATE', 'CONTACTS_SET', 'CONTACTS_UPSERT', 'CONTACTS_UPDATE', @@ -140,6 +142,7 @@ export const instanceSchema: JSONSchema7 = { 'MESSAGES_UPDATE', 'MESSAGES_DELETE', 'SEND_MESSAGE', + 'SEND_MESSAGE_UPDATE', 'CONTACTS_SET', 'CONTACTS_UPSERT', 'CONTACTS_UPDATE', From 17bd1082513b9530b2525d7d2fb955485d9100da Mon Sep 17 00:00:00 2001 From: pedro-php Date: Fri, 28 Mar 2025 10:56:19 -0300 Subject: [PATCH 2/6] treating errors gracefully --- docker-compose.yaml | 2 +- .../whatsapp/whatsapp.baileys.service.ts | 98 ++++++++++--------- 2 files changed, 54 insertions(+), 46 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 33918c38..ee4f4e05 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,7 +1,7 @@ services: api: container_name: evolution_api - image: evoapicloud/evolution-api:latest + build: . restart: always depends_on: - redis diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 071f7ff7..36e31ebf 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -3980,53 +3980,61 @@ export class BaileysStartupService extends ChannelStartupService { edit: data.key, }); if (messageSent) { - const messageId = messageSent.message?.protocolMessage?.key?.id; - if (messageId) { - let message = await this.prismaRepository.message.findFirst({ - where: { - key: { - path: ['id'], - equals: messageId, - }, - }, - }); - if (!message) throw new NotFoundException('Message not found'); - - if (!(message.key.valueOf() as any).fromMe) { - new BadRequestException('You cannot edit others messages'); - } - if ((message.key.valueOf() as any)?.deleted) { - new BadRequestException('You cannot edit deleted messages'); - } - if (oldMessage.messageType === 'conversation' || oldMessage.messageType === 'extendedTextMessage') { - oldMessage.message.conversation = data.text; - } else { - oldMessage.message[oldMessage.messageType].caption = data.text; - } - message = await this.prismaRepository.message.update({ - where: { id: message.id }, - data: { - message: oldMessage.message, - status: 'EDITED', - messageTimestamp: Math.floor(Date.now() / 1000), // Convert to int32 by dividing by 1000 to get seconds - }, - }); - const messageUpdate: any = { - messageId: message.id, - keyId: messageId, - remoteJid: messageSent.key.remoteJid, - fromMe: messageSent.key.fromMe, - participant: messageSent.key?.remoteJid, - status: 'EDITED', - instanceId: this.instanceId, - }; - await this.prismaRepository.messageUpdate.create({ - data: messageUpdate, - }); - - const editedMessage = messageSent?.message?.protocolMessage || messageSent?.message?.editedMessage?.message?.protocolMessage; + const editedMessage = messageSent?.message?.protocolMessage || messageSent?.message?.editedMessage?.message?.protocolMessage; + if (editedMessage) { this.sendDataWebhook(Events.SEND_MESSAGE_UPDATE, editedMessage); + if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot?.enabled) + this.chatwootService.eventWhatsapp( + 'send.message.update', + { instanceName: this.instance.name, instanceId: this.instance.id }, + editedMessage, + ); + + const messageId = messageSent.message?.protocolMessage?.key?.id; + if (messageId) { + let message = await this.prismaRepository.message.findFirst({ + where: { + key: { + path: ['id'], + equals: messageId, + }, + }, + }); + if (!message) throw new NotFoundException('Message not found'); + + if (!(message.key.valueOf() as any).fromMe) { + new BadRequestException('You cannot edit others messages'); + } + if ((message.key.valueOf() as any)?.deleted) { + new BadRequestException('You cannot edit deleted messages'); + } + if (oldMessage.messageType === 'conversation' || oldMessage.messageType === 'extendedTextMessage') { + oldMessage.message.conversation = data.text; + } else { + oldMessage.message[oldMessage.messageType].caption = data.text; + } + message = await this.prismaRepository.message.update({ + where: { id: message.id }, + data: { + message: oldMessage.message, + status: 'EDITED', + messageTimestamp: Math.floor(Date.now() / 1000), // Convert to int32 by dividing by 1000 to get seconds + }, + }); + const messageUpdate: any = { + messageId: message.id, + keyId: messageId, + remoteJid: messageSent.key.remoteJid, + fromMe: messageSent.key.fromMe, + participant: messageSent.key?.remoteJid, + status: 'EDITED', + instanceId: this.instanceId, + }; + await this.prismaRepository.messageUpdate.create({ + data: messageUpdate, + }); + } } } From 3d40b0850b4b5de2ab2647cb20ec0c5532b1e764 Mon Sep 17 00:00:00 2001 From: pedro-php Date: Fri, 28 Mar 2025 10:57:59 -0300 Subject: [PATCH 3/6] lint changes --- .../whatsapp/whatsapp.baileys.service.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 36e31ebf..13d48944 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -382,7 +382,7 @@ export class BaileysStartupService extends ChannelStartupService { qrcodeTerminal.generate(qr, { small: true }, (qrcode) => this.logger.log( `\n{ instance: ${this.instance.name} pairingCode: ${this.instance.qrcode.pairingCode}, qrcodeCount: ${this.instance.qrcode.count} }\n` + - qrcode, + qrcode, ), ); @@ -1023,18 +1023,18 @@ export class BaileysStartupService extends ChannelStartupService { const messagesRepository: Set = new Set( chatwootImport.getRepositoryMessagesCache(instance) ?? - ( - await this.prismaRepository.message.findMany({ - select: { key: true }, - where: { instanceId: this.instanceId }, - }) - ).map((message) => { - const key = message.key as { - id: string; - }; + ( + await this.prismaRepository.message.findMany({ + select: { key: true }, + where: { instanceId: this.instanceId }, + }) + ).map((message) => { + const key = message.key as { + id: string; + }; - return key.id; - }), + return key.id; + }), ); if (chatwootImport.getRepositoryMessagesCache(instance) === null) { @@ -1132,7 +1132,8 @@ export class BaileysStartupService extends ChannelStartupService { } } - const editedMessage = received?.message?.protocolMessage || received?.message?.editedMessage?.message?.protocolMessage; + const editedMessage = + received?.message?.protocolMessage || received?.message?.editedMessage?.message?.protocolMessage; if (received.message?.protocolMessage?.editedMessage && editedMessage) { if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot?.enabled) @@ -1145,7 +1146,6 @@ export class BaileysStartupService extends ChannelStartupService { await this.sendDataWebhook(Events.MESSAGES_EDITED, editedMessage); const oldMessage = await this.getMessage(editedMessage.key, true); if ((oldMessage as any)?.id) { - if (Long.isLong(editedMessage?.timestampMs)) { editedMessage.timestampMs = editedMessage.timestampMs?.toNumber(); } @@ -1169,7 +1169,6 @@ export class BaileysStartupService extends ChannelStartupService { }, }); } - } // if (received.messageStubParameters && received.messageStubParameters[0] === 'Message absent from node') { @@ -3980,7 +3979,8 @@ export class BaileysStartupService extends ChannelStartupService { edit: data.key, }); if (messageSent) { - const editedMessage = messageSent?.message?.protocolMessage || messageSent?.message?.editedMessage?.message?.protocolMessage; + const editedMessage = + messageSent?.message?.protocolMessage || messageSent?.message?.editedMessage?.message?.protocolMessage; if (editedMessage) { this.sendDataWebhook(Events.SEND_MESSAGE_UPDATE, editedMessage); From 829032dc086db7ffd8acbae5c47106737e99fbb0 Mon Sep 17 00:00:00 2001 From: pedro-php Date: Fri, 28 Mar 2025 10:58:32 -0300 Subject: [PATCH 4/6] lint changes --- docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index ee4f4e05..33918c38 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,7 +1,7 @@ services: api: container_name: evolution_api - build: . + image: evoapicloud/evolution-api:latest restart: always depends_on: - redis From 645f305cd6dec59e645fcd6ac93ffae0ad169c28 Mon Sep 17 00:00:00 2001 From: pedro-php Date: Fri, 28 Mar 2025 11:08:38 -0300 Subject: [PATCH 5/6] fixing build error on prisma --- .../channel/whatsapp/whatsapp.baileys.service.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 13d48944..3940cff0 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1146,15 +1146,14 @@ export class BaileysStartupService extends ChannelStartupService { await this.sendDataWebhook(Events.MESSAGES_EDITED, editedMessage); const oldMessage = await this.getMessage(editedMessage.key, true); if ((oldMessage as any)?.id) { - if (Long.isLong(editedMessage?.timestampMs)) { - editedMessage.timestampMs = editedMessage.timestampMs?.toNumber(); - } + + const editedMessageTimestamp = Long.isLong(editedMessage?.timestampMs) ? editedMessage.timestampMs?.toNumber() : editedMessage.timestampMs as number; await this.prismaRepository.message.update({ where: { id: (oldMessage as any).id }, data: { message: editedMessage.editedMessage as any, - messageTimestamp: editedMessage.timestampMs, + messageTimestamp: editedMessageTimestamp, status: 'EDITED', }, }); From 7e8044a77724341dc9ee003d91e48edce3699be7 Mon Sep 17 00:00:00 2001 From: pedro-php Date: Fri, 28 Mar 2025 11:10:48 -0300 Subject: [PATCH 6/6] lint changes --- .../channel/whatsapp/whatsapp.baileys.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 3940cff0..542e2f72 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1146,8 +1146,9 @@ export class BaileysStartupService extends ChannelStartupService { await this.sendDataWebhook(Events.MESSAGES_EDITED, editedMessage); const oldMessage = await this.getMessage(editedMessage.key, true); if ((oldMessage as any)?.id) { - - const editedMessageTimestamp = Long.isLong(editedMessage?.timestampMs) ? editedMessage.timestampMs?.toNumber() : editedMessage.timestampMs as number; + const editedMessageTimestamp = Long.isLong(editedMessage?.timestampMs) + ? editedMessage.timestampMs?.toNumber() + : (editedMessage.timestampMs as number); await this.prismaRepository.message.update({ where: { id: (oldMessage as any).id },