From f912353a5c2304e896b366bb543fd7de9382d880 Mon Sep 17 00:00:00 2001 From: devlikepro Date: Sun, 12 Oct 2025 11:45:54 +0700 Subject: [PATCH] [core] ChatWoot - Event Message (cherry picked from bergpinheiro/waha@696de94859cea3915de2b0ef2c4298afaa33a2d5) Thanks @bergpinheiro Co-authored-by: bergpinheiro --- package.json | 1 + .../chatwoot/consumers/waha/message.any.ts | 7 +++ src/apps/chatwoot/i18n/locale.ts | 27 +++++++++++ src/apps/chatwoot/i18n/locales/en-US.yaml | 34 ++++++++++++++ src/apps/chatwoot/i18n/locales/pt-BR.yaml | 34 ++++++++++++++ src/apps/chatwoot/i18n/templates.ts | 7 +++ .../messages/to/chatwoot/EventMessage.ts | 45 +++++++++++++++++++ .../chatwoot/messages/to/chatwoot/index.ts | 1 + src/core/engines/noweb/utils.ts | 11 ++++- yarn.lock | 3 +- 10 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 src/apps/chatwoot/messages/to/chatwoot/EventMessage.ts diff --git a/package.json b/package.json index b468ac0c..43dbd9b3 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "libsignal": "github:devlikeapro/libsignal-node#fork-master", "link-preview-js": "^3.0.4", "lodash": "^4.17.21", + "long": "^5.3.2", "mime-types": "^2.1.27", "mongodb": "6.9.0", "mustache": "^4.2.0", diff --git a/src/apps/chatwoot/consumers/waha/message.any.ts b/src/apps/chatwoot/consumers/waha/message.any.ts index a0c178a8..48e53b84 100644 --- a/src/apps/chatwoot/consumers/waha/message.any.ts +++ b/src/apps/chatwoot/consumers/waha/message.any.ts @@ -19,6 +19,7 @@ import { Job } from 'bullmq'; import { PinoLogger } from 'nestjs-pino'; import { FacebookAdMessage, + EventMessage, LocationMessage, MessageToChatWootConverter, PollMessage, @@ -102,6 +103,12 @@ class MessageAnyHandler extends MessageBaseHandler { return msg; } + converter = new EventMessage(this.l); + msg = await converter.convert(payload, protoMessage); + if (msg) { + return msg; + } + converter = new UnsupportedMessage(this.l, this.job); msg = await converter.convert(payload, protoMessage); return msg; diff --git a/src/apps/chatwoot/i18n/locale.ts b/src/apps/chatwoot/i18n/locale.ts index 7273d184..e12fb051 100644 --- a/src/apps/chatwoot/i18n/locale.ts +++ b/src/apps/chatwoot/i18n/locale.ts @@ -1,5 +1,7 @@ import * as Mustache from 'mustache'; import { TemplatePayloads, TKey } from '@waha/apps/chatwoot/i18n/templates'; +import Long from 'long'; +import { ensureNumber } from '@waha/core/engines/noweb/utils'; export class Locale { constructor(private readonly strings: Record) {} @@ -15,6 +17,31 @@ export class Locale { override(strings: Record): Locale { return new Locale({ ...this.strings, ...strings }); } + + FormatDate(date: Date | null): string | null { + if (!date) { + return null; + } + const options: any = this.strings['datetime'] || {}; + options.timezone = options.timeZone ?? process.env.TZ; + return date.toLocaleString(options.locales, options); + } + + FormatTimestamp(timestamp: Long | string | number | null): string | null { + const value = ensureNumber(timestamp); + if (!value) { + return undefined; + } + if (!Number.isFinite(value)) { + return undefined; + } + const milliseconds = value >= 1e12 ? value : value * 1000; + const date = new Date(milliseconds); + if (Number.isNaN(date.getTime())) { + return undefined; + } + return this.FormatDate(date); + } } export class Template { diff --git a/src/apps/chatwoot/i18n/locales/en-US.yaml b/src/apps/chatwoot/i18n/locales/en-US.yaml index bb2e5a67..0fba99d3 100644 --- a/src/apps/chatwoot/i18n/locales/en-US.yaml +++ b/src/apps/chatwoot/i18n/locales/en-US.yaml @@ -4,6 +4,15 @@ locale.name: '🇺🇸 English (English)' app.inbox.contact.name: 'WhatsApp Integration (WAHA)' app.inbox.contact.avatar.url: 'https://raw.githubusercontent.com/devlikeapro/waha/core/logo.png' +datetime: + locales: en-US + day: 2-digit + month: 2-digit + year: numeric + hour: 2-digit + minute: 2-digit + # timeZone: 'America/New_York' + job.report.error: |- ⚠️ **{{{header}}}** {{#attempts.nextDelay}} @@ -143,6 +152,31 @@ whatsapp.to.chatwoot.message.poll: |- {{/options}} {{/poll}} +whatsapp.to.chatwoot.message.event: |- + {{#message}} + {{#eventMessage}} + 📅 **Event** + + **{{name}}** + {{#description}} + {{description}} + {{/description}} + + {{#location.name}} + 📍 **Location:** {{location.name}} + {{/location.name}} + {{#startTime}} + 🕐 **Start:** {{startTime}} + {{/startTime}} + {{#endTime}} + 🕐 **End:** {{endTime}} + {{/endTime}} + {{#joinLink}} + 🔗 **Join Link:** [{{joinLink}}]({{joinLink}}) + {{/joinLink}} + {{/eventMessage}} + {{/message}} + whatsapp.to.chatwoot.message.unsupported: |- ⚠️ **This message type is not supported in ChatWoot.** 📱 Please open **WhatsApp** to view it. diff --git a/src/apps/chatwoot/i18n/locales/pt-BR.yaml b/src/apps/chatwoot/i18n/locales/pt-BR.yaml index 1e9cac75..a21d41d1 100644 --- a/src/apps/chatwoot/i18n/locales/pt-BR.yaml +++ b/src/apps/chatwoot/i18n/locales/pt-BR.yaml @@ -267,6 +267,40 @@ whatsapp.to.chatwoot.message.poll: |- {{/options}} {{/poll}} +whatsapp.to.chatwoot.message.event: |- + {{#message}} + {{#eventMessage}} + {{#isCanceled}} + ❌ **Evento Cancelado** + {{/isCanceled}} + {{^isCanceled}} + 📅 **Evento** + {{/isCanceled}} + + **{{name}}** + + {{#description}} + {{description}} + {{/description}} + + {{#location.name}} + 📍 **Local:** {{location.name}} + {{/location.name}} + + {{#startTime}} + 🕐 **Início:** {{startTime}} + {{/startTime}} + + {{#endTime}} + 🕐 **Fim:** {{endTime}} + {{/endTime}} + + {{#joinLink}} + 🔗 **Link de participação:** {{joinLink}} + {{/joinLink}} + {{/eventMessage}} + {{/message}} + whatsapp.to.chatwoot.message.unsupported: |- ⚠️ **Esse tipo de mensagem não é suportado no ChatWoot.** 📱 Por favor, abra o **WhatsApp** para visualizá-la. diff --git a/src/apps/chatwoot/i18n/templates.ts b/src/apps/chatwoot/i18n/templates.ts index 4c289c62..ab29dae3 100644 --- a/src/apps/chatwoot/i18n/templates.ts +++ b/src/apps/chatwoot/i18n/templates.ts @@ -52,6 +52,7 @@ export enum TKey { WA_TO_CW_MESSAGE_CONTACTS = 'whatsapp.to.chatwoot.message.contacts', WA_TO_CW_MESSAGE_LOCATION = 'whatsapp.to.chatwoot.message.location', WA_TO_CW_MESSAGE_POLL = 'whatsapp.to.chatwoot.message.poll', + WA_TO_CW_MESSAGE_EVENT = 'whatsapp.to.chatwoot.message.event', WA_TO_CW_MESSAGE_UNSUPPORTED = 'whatsapp.to.chatwoot.message.unsupported', WA_TO_CW_MESSAGE_FACEBOOK_AD = 'whatsapp.to.chatwoot.message.facebook.ad', @@ -156,6 +157,12 @@ export type TemplatePayloads = { poll: PollCreationMessage; message: proto.Message; }; + [TKey.WA_TO_CW_MESSAGE_EVENT]: { + payload: WAMessage; + message: { + eventMessage: Record; + }; + }; [TKey.WA_TO_CW_MESSAGE_UNSUPPORTED]: { details: Link }; [TKey.WA_TO_CW_MESSAGE_FACEBOOK_AD]: { payload: WAMessage; diff --git a/src/apps/chatwoot/messages/to/chatwoot/EventMessage.ts b/src/apps/chatwoot/messages/to/chatwoot/EventMessage.ts new file mode 100644 index 00000000..4d8586a6 --- /dev/null +++ b/src/apps/chatwoot/messages/to/chatwoot/EventMessage.ts @@ -0,0 +1,45 @@ +import { ChatWootMessagePartial } from '@waha/apps/chatwoot/consumers/waha/base'; +import { Locale } from '@waha/apps/chatwoot/i18n/locale'; +import { TKey } from '@waha/apps/chatwoot/i18n/templates'; +import { MessageToChatWootConverter } from '@waha/apps/chatwoot/messages/to/chatwoot'; +import { isEmptyString } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/proto'; +import { WhatsappToMarkdown } from '@waha/apps/chatwoot/messages/to/chatwoot/utils/markdown'; +import type { proto } from '@adiwajshing/baileys'; +import { WAMessage } from '@waha/structures/responses.dto'; + +export class EventMessage implements MessageToChatWootConverter { + constructor(private readonly l: Locale) {} + + convert( + payload: WAMessage, + protoMessage: proto.Message | null, + ): ChatWootMessagePartial | null { + const eventMessage = protoMessage?.eventMessage; + if (!eventMessage) { + return null; + } + + const formattedEventMessage = { + ...eventMessage, + startTime: this.l.FormatTimestamp(eventMessage.startTime), + endTime: this.l.FormatTimestamp(eventMessage.endTime), + }; + + const content = this.l.key(TKey.WA_TO_CW_MESSAGE_EVENT).r({ + payload, + message: { + eventMessage: formattedEventMessage, + }, + }); + + if (isEmptyString(content)) { + return null; + } + + return { + content: WhatsappToMarkdown(content), + attachments: [], + private: undefined, + }; + } +} diff --git a/src/apps/chatwoot/messages/to/chatwoot/index.ts b/src/apps/chatwoot/messages/to/chatwoot/index.ts index 621b44bc..b0753862 100644 --- a/src/apps/chatwoot/messages/to/chatwoot/index.ts +++ b/src/apps/chatwoot/messages/to/chatwoot/index.ts @@ -10,6 +10,7 @@ export { UnsupportedMessage } from './UnsupportedMessage'; export { MessageEdited } from './MessageEdited'; export { FacebookAdMessage } from './FacebookAdMessage'; export { PollMessage } from './PollMessage'; +export { EventMessage } from './EventMessage'; export type Awaitable = T | Promise; diff --git a/src/core/engines/noweb/utils.ts b/src/core/engines/noweb/utils.ts index cceffef8..69f3315d 100644 --- a/src/core/engines/noweb/utils.ts +++ b/src/core/engines/noweb/utils.ts @@ -79,12 +79,19 @@ const isObjectALong = (value: any): value is Long => { ); }; -export function ensureNumber(value: number | Long): number { +export function ensureNumber(value: number | Long | string | null): number { if (!value) { // @ts-ignore return value; } - return typeof value === 'number' ? value : toNumber(value); + if (typeof value === 'string') { + return Number.parseInt(value, 10); + } + if (isObjectALong(value)) { + return toNumber(value); + } + // number + return value; } const toNumber = (longValue: Long): number => { diff --git a/yarn.lock b/yarn.lock index 0ca50067..d9ea15c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8684,7 +8684,7 @@ __metadata: languageName: node linkType: hard -"long@npm:^5.0.0": +"long@npm:^5.0.0, long@npm:^5.3.2": version: 5.3.2 resolution: "long@npm:5.3.2" checksum: be215816b563f4ca27ad3677678b53415bc489f9e3466414e54d2d85f5f8e86768547fa58493bacfb363ffc57a664debc83403ccc2178aef0c40aca28bad47c9 @@ -12561,6 +12561,7 @@ __metadata: libsignal: "github:devlikeapro/libsignal-node#fork-master" link-preview-js: ^3.0.4 lodash: ^4.17.21 + long: ^5.3.2 mime-types: ^2.1.27 mongodb: 6.9.0 mustache: ^4.2.0