diff --git a/Docker/.env.example b/Docker/.env.example index 0d056dd5..f55e3d65 100644 --- a/Docker/.env.example +++ b/Docker/.env.example @@ -122,6 +122,8 @@ TYPEBOT_KEEP_OPEN=false #Chatwoot # If you leave this option as false, when deleting the message for everyone on WhatsApp, it will not be deleted on Chatwoot. CHATWOOT_MESSAGE_DELETE=false # false | true +# If you leave this option as true, when sending a message in Chatwoot, the client's last message will be marked as read on WhatsApp. +CHATWOOT_MESSAGE_READ=false # false | true # This db connection is used to import messages from whatsapp to chatwoot database CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgres://user:password@hostname:port/dbname CHATWOOT_IMPORT_DATABASE_PLACEHOLDER_MEDIA_MESSAGE=true @@ -139,3 +141,5 @@ AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true # seconds - 3600s ===1h | zero (0) - never expires AUTHENTICATION_JWT_EXPIRIN_IN=0 AUTHENTICATION_JWT_SECRET='L=0YWt]b2w[WF>#>:&E`' + +LANGUAGE=en # pt-BR, en \ No newline at end of file diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 1baac567..9ee778c1 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -164,6 +164,7 @@ export type QrCode = { LIMIT: number; COLOR: string }; export type Typebot = { API_VERSION: string; KEEP_OPEN: boolean }; export type Chatwoot = { MESSAGE_DELETE: boolean; + MESSAGE_READ: boolean; IMPORT: { DATABASE: { CONNECTION: { @@ -379,10 +380,11 @@ export class ConfigService { }, CHATWOOT: { MESSAGE_DELETE: process.env.CHATWOOT_MESSAGE_DELETE === 'false', + MESSAGE_READ: process.env.CHATWOOT_MESSAGE_READ === 'false', IMPORT: { DATABASE: { CONNECTION: { - URI: process.env.CHATWOOT_DATABASE_CONNECTION_URI || '', + URI: process.env.CHATWOOT_IMPORT_DATABASE_CONNECTION_URI || '', }, }, PLACEHOLDER_MEDIA_MESSAGE: process.env?.CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE === 'true', diff --git a/src/dev-env.yml b/src/dev-env.yml index 9f7a413f..adc308e8 100644 --- a/src/dev-env.yml +++ b/src/dev-env.yml @@ -166,6 +166,8 @@ TYPEBOT: CHATWOOT: # If you leave this option as false, when deleting the message for everyone on WhatsApp, it will not be deleted on Chatwoot. MESSAGE_DELETE: true # false | true + # If you leave this option as true, when sending a message in Chatwoot, the client's last message will be marked as read on WhatsApp. + MESSAGE_READ: false # false | true IMPORT: # This db connection is used to import messages from whatsapp to chatwoot database DATABASE: diff --git a/src/utils/translations/en.json b/src/utils/translations/en.json index 2eeeccb1..f0a64def 100644 --- a/src/utils/translations/en.json +++ b/src/utils/translations/en.json @@ -21,5 +21,6 @@ "cw.locationMessage.locationUrl": "URL", "cw.contactMessage.contact": "Contact", "cw.contactMessage.name": "Name", - "cw.contactMessage.number": "Number" + "cw.contactMessage.number": "Number", + "cw.message.edited": "Edited Message" } \ No newline at end of file diff --git a/src/utils/translations/pt-BR.json b/src/utils/translations/pt-BR.json index 52bfcd69..a8d62417 100644 --- a/src/utils/translations/pt-BR.json +++ b/src/utils/translations/pt-BR.json @@ -21,5 +21,6 @@ "cw.locationMessage.locationUrl": "URL", "cw.contactMessage.contact": "Contato", "cw.contactMessage.name": "Nome", - "cw.contactMessage.number": "Número" + "cw.contactMessage.number": "Número", + "cw.message.edited": "Mensagem editada" } \ No newline at end of file diff --git a/src/whatsapp/models/message.model.ts b/src/whatsapp/models/message.model.ts index 717ab67f..893d1f2a 100644 --- a/src/whatsapp/models/message.model.ts +++ b/src/whatsapp/models/message.model.ts @@ -15,6 +15,7 @@ class ChatwootMessage { inboxId?: number; conversationId?: number; contactInbox?: { sourceId: string }; + isRead?: boolean; } export class MessageRaw { @@ -36,8 +37,9 @@ export class MessageRaw { type MessageRawBoolean = { [P in keyof T]?: 0 | 1; }; -export type MessageRawSelect = Omit, 'key'> & { +export type MessageRawSelect = Omit, 'key'>, 'chatwoot'> & { key?: MessageRawBoolean; + chatwoot?: MessageRawBoolean; }; const messageSchema = new Schema({ @@ -60,6 +62,7 @@ const messageSchema = new Schema({ inboxId: { type: Number }, conversationId: { type: Number }, contactInbox: { type: Object }, + isRead: { type: Boolean }, }, }); diff --git a/src/whatsapp/repository/message.repository.ts b/src/whatsapp/repository/message.repository.ts index 64dd155b..9802bfae 100644 --- a/src/whatsapp/repository/message.repository.ts +++ b/src/whatsapp/repository/message.repository.ts @@ -118,6 +118,7 @@ export class MessageRepository extends Repository { return await this.messageModel .find({ ...query.where }) + .select(query.select || {}) .sort({ messageTimestamp: -1 }) .limit(query?.limit ?? 0); } diff --git a/src/whatsapp/services/chatwoot.service.ts b/src/whatsapp/services/chatwoot.service.ts index 3f50f06d..43d8f5be 100644 --- a/src/whatsapp/services/chatwoot.service.ts +++ b/src/whatsapp/services/chatwoot.service.ts @@ -1354,6 +1354,37 @@ export class ChatwootService { ); } } + + const chatwootRead = this.configService.get('CHATWOOT').MESSAGE_READ; + if (chatwootRead) { + const lastMessage = await this.repository.message.find({ + where: { + key: { + fromMe: false, + }, + owner: instance.instanceName, + }, + limit: 1, + }); + if (lastMessage.length > 0 && !lastMessage[0].chatwoot?.isRead) { + waInstance?.markMessageAsRead({ + read_messages: lastMessage.map((msg) => ({ + id: msg.key?.id, + fromMe: msg.key?.fromMe, + remoteJid: msg.key?.remoteJid, + })), + }); + const updateMessage = lastMessage.map((msg) => ({ + key: msg.key, + owner: msg.owner, + chatwoot: { + ...msg.chatwoot, + isRead: true, + }, + })); + this.repository.message.update(updateMessage, instance.instanceName, true); + } + } } if (body.message_type === 'template' && body.event === 'message_created') { @@ -2047,6 +2078,34 @@ export class ChatwootService { } } + if (event === 'messages.edit') { + const editedText = `${ + body?.editedMessage?.conversation || body?.editedMessage?.extendedTextMessage?.text + }\n\n_\`${i18next.t('cw.message.edited')}.\`_`; + const message = await this.getMessageByKeyId(instance, body?.key?.id); + const messageType = message.key?.fromMe ? 'outgoing' : 'incoming'; + + if (message && message.chatwoot?.conversationId) { + const send = await this.createMessage( + instance, + message.chatwoot.conversationId, + editedText, + messageType, + false, + [], + { + message: { extendedTextMessage: { contextInfo: { stanzaId: message.key.id } } }, + }, + 'WAID:' + body.key.id, + ); + if (!send) { + this.logger.warn('edited message not sent'); + return; + } + } + return; + } + if (event === 'messages.read') { this.logger.verbose('read message from instance: ' + instance.instanceName); diff --git a/src/whatsapp/services/whatsapp.baileys.service.ts b/src/whatsapp/services/whatsapp.baileys.service.ts index 6dc55e0c..a081b5b7 100644 --- a/src/whatsapp/services/whatsapp.baileys.service.ts +++ b/src/whatsapp/services/whatsapp.baileys.service.ts @@ -937,6 +937,17 @@ export class BaileysStartupService extends WAStartupService { try { this.logger.verbose('Event received: messages.upsert'); for (const received of messages) { + if ( + this.localChatwoot.enabled && + (received.message?.protocolMessage?.editedMessage || received.message?.editedMessage?.message) + ) { + const editedMessage = + received.message?.protocolMessage || received.message?.editedMessage?.message?.protocolMessage; + if (editedMessage) { + this.chatwootService.eventWhatsapp('messages.edit', { instanceName: this.instance.name }, editedMessage); + } + } + if ( (type !== 'notify' && type !== 'append') || received.message?.protocolMessage ||