From 42b46e0813677f0eab9f7c36b60e07e00cfafcaa Mon Sep 17 00:00:00 2001 From: Milton Sosa Date: Mon, 16 Feb 2026 04:30:13 -0300 Subject: [PATCH 1/2] fix(chatbot): closed session should not block bot re-activation When a chatbot session exists with status='closed', the emit() method returned early, preventing the bot from re-activating on new messages. Root cause: the guard 'if (session.status === closed) return' was meant to skip sessions not awaiting user input, but it also prevented new conversations from starting after a bot flow completed. Fix: nullify the session instead of returning, so processBot enters the '!session' branch and creates a fresh session. Also adds null guards: - getConversationMessage: return empty string instead of undefined - findBotByTrigger: handle null/undefined content gracefully --- src/api/integrations/chatbot/base-chatbot.controller.ts | 6 +++--- src/utils/findBotByTrigger.ts | 8 ++++++++ src/utils/getConversationMessage.ts | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/api/integrations/chatbot/base-chatbot.controller.ts b/src/api/integrations/chatbot/base-chatbot.controller.ts index a5b83e25..4108785e 100644 --- a/src/api/integrations/chatbot/base-chatbot.controller.ts +++ b/src/api/integrations/chatbot/base-chatbot.controller.ts @@ -797,7 +797,7 @@ export abstract class BaseChatbotController { + const normalizedContent = content?.trim() || ''; + // Check for triggerType 'all' or 'none' (both should match any message) const findTriggerAllOrNone = await botRepository.findFirst({ where: { @@ -16,6 +18,12 @@ export const findBotByTrigger = async (botRepository: any, content: string, inst return findTriggerAllOrNone; } + // If content is empty (null, undefined, whitespace-only, or media-only messages), + // only 'all'/'none' triggers apply — skip keyword/regex matching + if (!normalizedContent) { + return null; + } + const findTriggerAdvanced = await botRepository.findMany({ where: { enabled: true, diff --git a/src/utils/getConversationMessage.ts b/src/utils/getConversationMessage.ts index eca23b45..bd13f89e 100644 --- a/src/utils/getConversationMessage.ts +++ b/src/utils/getConversationMessage.ts @@ -76,5 +76,5 @@ export const getConversationMessage = (msg: any) => { const messageContent = getMessageContent(types); - return messageContent; + return messageContent ?? ''; }; From cb4a14d1ef0e279aed5042ec3a569d0cbd678423 Mon Sep 17 00:00:00 2001 From: Milton Sosa Date: Mon, 16 Feb 2026 04:30:40 -0300 Subject: [PATCH 2/2] fix(meta): normalize execution order and fix chatwootIds in Cloud API Two bugs in BusinessStartupService message processing: 1. Execution order: Chatwoot was processed AFTER the bot emit(), but Baileys channel processes Chatwoot FIRST. This inconsistency meant the bot could not access chatwootConversationId/chatwootInboxId when processing messages from the Cloud API. 2. chatwootIds assignment: chatwootInboxId and chatwootConversationId were both incorrectly set to chatwootSentMessage.id instead of .inbox_id and .conversation_id respectively. Fix: reorder to Chatwoot-first (consistent with Baileys) and use the correct property names from the Chatwoot response object. --- .../channel/meta/whatsapp.business.service.ts | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/api/integrations/channel/meta/whatsapp.business.service.ts b/src/api/integrations/channel/meta/whatsapp.business.service.ts index 1e4808c1..1f820d0e 100644 --- a/src/api/integrations/channel/meta/whatsapp.business.service.ts +++ b/src/api/integrations/channel/meta/whatsapp.business.service.ts @@ -668,6 +668,21 @@ export class BusinessStartupService extends ChannelStartupService { sendTelemetry(`received.message.${messageRaw.messageType ?? 'unknown'}`); + // Normalized order: Chatwoot first, then bot (consistent with Baileys channel) + if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot?.enabled) { + const chatwootSentMessage = await this.chatwootService.eventWhatsapp( + Events.MESSAGES_UPSERT, + { instanceName: this.instance.name, instanceId: this.instanceId }, + messageRaw, + ); + + if (chatwootSentMessage) { + messageRaw.chatwootMessageId = chatwootSentMessage.id; + messageRaw.chatwootInboxId = chatwootSentMessage.inbox_id; + messageRaw.chatwootConversationId = chatwootSentMessage.conversation_id; + } + } + this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); await chatbotController.emit({ @@ -677,20 +692,6 @@ export class BusinessStartupService extends ChannelStartupService { pushName: messageRaw.pushName, }); - if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot?.enabled) { - const chatwootSentMessage = await this.chatwootService.eventWhatsapp( - Events.MESSAGES_UPSERT, - { instanceName: this.instance.name, instanceId: this.instanceId }, - messageRaw, - ); - - if (chatwootSentMessage?.id) { - messageRaw.chatwootMessageId = chatwootSentMessage.id; - messageRaw.chatwootInboxId = chatwootSentMessage.id; - messageRaw.chatwootConversationId = chatwootSentMessage.id; - } - } - if (!this.isMediaMessage(message) && message.type !== 'sticker') { await this.prismaRepository.message.create({ data: messageRaw,