Skip to content

Commit d8cd2d0

Browse files
possebonclaude
andcommitted
fix(chatwoot): force state sync, fix newlines, detect phone-sent messages
Addresses three upstream issues: - evolution-foundation#2403: Force connection state to 'close' in DB when sendMessage catches a connection-closed error, preventing stale 'connected' state in UI - evolution-foundation#2412/evolution-foundation#2415: Sanitize double-escaped newlines (\n → \n) and trim trailing newlines in messages sent from Chatwoot to WhatsApp - evolution-foundation#2446: Detect messages sent from the linked WhatsApp phone app (fromMe=true + event=messages.upsert) and classify them as private notes in Chatwoot with a prefix, preventing agent confusion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8922835 commit d8cd2d0

2 files changed

Lines changed: 62 additions & 12 deletions

File tree

src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2579,6 +2579,35 @@ export class BaileysStartupService extends ChannelStartupService {
25792579
return messageRaw;
25802580
} catch (error) {
25812581
this.logger.error(error);
2582+
2583+
// Force state sync when we detect a connection-closed error during send
2584+
const errorStr = error?.toString?.() || '';
2585+
const isConnectionError =
2586+
errorStr.includes('Connection Closed') ||
2587+
errorStr.includes('connection closed') ||
2588+
errorStr.includes('ECONNRESET') ||
2589+
errorStr.includes('socket closed') ||
2590+
error?.output?.statusCode === 428;
2591+
2592+
if (isConnectionError && this.stateConnection.state === 'open') {
2593+
this.logger.warn(
2594+
`Detected stale connection state for "${this.instance.name}". Forcing state sync to "close".`,
2595+
);
2596+
this.stateConnection = { state: 'close', statusReason: 428 };
2597+
2598+
this.prismaRepository.instance
2599+
.update({
2600+
where: { id: this.instanceId },
2601+
data: {
2602+
connectionStatus: 'close',
2603+
disconnectionAt: new Date(),
2604+
disconnectionReasonCode: 428,
2605+
disconnectionObject: JSON.stringify({ error: 'Connection closed detected during send' }),
2606+
},
2607+
})
2608+
.catch((err) => this.logger.error(`Failed to update connection status in DB: ${err}`));
2609+
}
2610+
25822611
throw new BadRequestException(error.toString());
25832612
}
25842613
}

src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,7 @@ export class ChatwootService {
10571057
messageBody?: any,
10581058
sourceId?: string,
10591059
quotedMsg?: MessageModel,
1060+
privateMessage?: boolean,
10601061
) {
10611062
if (sourceId && this.isImportHistoryAvailable()) {
10621063
const messageAlreadySaved = await chatwootImport.getExistingSourceIds([sourceId], conversationId);
@@ -1075,6 +1076,10 @@ export class ChatwootService {
10751076

10761077
data.append('message_type', messageType);
10771078

1079+
if (privateMessage) {
1080+
data.append('private', 'true');
1081+
}
1082+
10781083
data.append('attachments[]', fileStream, { filename: fileName });
10791084

10801085
const sourceReplyId = quotedMsg?.chatwootMessageId || null;
@@ -1338,6 +1343,8 @@ export class ChatwootService {
13381343
// Chatwoot to Whatsapp
13391344
const messageReceived = body.content
13401345
? body.content
1346+
.replaceAll(/\\\n/g, '\n') // Fix #2412: remove backslash before newlines (chatwoot double-escaping)
1347+
.replace(/\n+$/g, '') // Fix #2415: trim trailing newlines
13411348
.replaceAll(/(?<!\*)\*((?!\s)([^\n*]+?)(?<!\s))\*(?!\*)/g, '_$1_') // Substitui * por _
13421349
.replaceAll(/\*{2}((?!\s)([^\n*]+?)(?<!\s))\*{2}/g, '*$1*') // Substitui ** por *
13431350
.replaceAll(/~{2}((?!\s)([^\n*]+?)(?<!\s))~{2}/g, '~$1~') // Substitui ~~ por ~
@@ -2086,7 +2093,17 @@ export class ChatwootService {
20862093
return;
20872094
}
20882095

2089-
const messageType = body.key.fromMe ? 'outgoing' : 'incoming';
2096+
// Fix #2446: Detect messages sent from the WhatsApp app (not via Chatwoot/API)
2097+
// When event is 'send.message', it was sent by evolution-api (via chatwoot or API call)
2098+
// When event is 'messages.upsert' + fromMe=true, it was sent from the linked phone app
2099+
const sentFromWhatsAppApp = body.key.fromMe && event !== 'send.message';
2100+
const messageType: 'incoming' | 'outgoing' = sentFromWhatsAppApp ? 'incoming' : body.key.fromMe ? 'outgoing' : 'incoming';
2101+
2102+
// Prefix content for messages sent from the phone app so agents can distinguish them
2103+
const prefixAppMessage = (text: string | undefined): string => {
2104+
if (!sentFromWhatsAppApp || !text) return text || '';
2105+
return `📱 *[${i18next.t('cw.message.sentFromApp', { defaultValue: 'Sent from WhatsApp app' })}]*\n${text}`;
2106+
};
20902107

20912108
if (isMedia) {
20922109
const downloadBase64 = await waInstance?.getBase64FromMediaMessage({
@@ -2140,11 +2157,12 @@ export class ChatwootService {
21402157
fileStream,
21412158
nameFile,
21422159
messageType,
2143-
content,
2160+
prefixAppMessage(content),
21442161
instance,
21452162
body,
21462163
'WAID:' + body.key.id,
21472164
quotedMsg,
2165+
sentFromWhatsAppApp,
21482166
);
21492167

21502168
if (!send) {
@@ -2159,11 +2177,12 @@ export class ChatwootService {
21592177
fileStream,
21602178
nameFile,
21612179
messageType,
2162-
bodyMessage,
2180+
prefixAppMessage(bodyMessage),
21632181
instance,
21642182
body,
21652183
'WAID:' + body.key.id,
21662184
quotedMsg,
2185+
sentFromWhatsAppApp,
21672186
);
21682187

21692188
if (!send) {
@@ -2180,9 +2199,9 @@ export class ChatwootService {
21802199
const send = await this.createMessage(
21812200
instance,
21822201
getConversation,
2183-
reactionMessage.text,
2202+
prefixAppMessage(reactionMessage.text),
21842203
messageType,
2185-
false,
2204+
sentFromWhatsAppApp,
21862205
[],
21872206
{
21882207
message: { extendedTextMessage: { contextInfo: { stanzaId: reactionMessage.key.id } } },
@@ -2227,9 +2246,9 @@ export class ChatwootService {
22272246
const send = await this.createMessage(
22282247
instance,
22292248
getConversation,
2230-
content,
2249+
prefixAppMessage(content),
22312250
messageType,
2232-
false,
2251+
sentFromWhatsAppApp,
22332252
[],
22342253
body,
22352254
'WAID:' + body.key.id,
@@ -2285,10 +2304,12 @@ export class ChatwootService {
22852304
fileStream,
22862305
nameFile,
22872306
messageType,
2288-
`${bodyMessage}\n\n\n**${title}**\n${description}\n${adsMessage.sourceUrl}`,
2307+
prefixAppMessage(`${bodyMessage}\n\n\n**${title}**\n${description}\n${adsMessage.sourceUrl}`),
22892308
instance,
22902309
body,
22912310
'WAID:' + body.key.id,
2311+
undefined,
2312+
sentFromWhatsAppApp,
22922313
);
22932314

22942315
if (!send) {
@@ -2318,9 +2339,9 @@ export class ChatwootService {
23182339
const send = await this.createMessage(
23192340
instance,
23202341
getConversation,
2321-
content,
2342+
prefixAppMessage(content),
23222343
messageType,
2323-
false,
2344+
sentFromWhatsAppApp,
23242345
[],
23252346
body,
23262347
'WAID:' + body.key.id,
@@ -2337,9 +2358,9 @@ export class ChatwootService {
23372358
const send = await this.createMessage(
23382359
instance,
23392360
getConversation,
2340-
bodyMessage,
2361+
prefixAppMessage(bodyMessage),
23412362
messageType,
2342-
false,
2363+
sentFromWhatsAppApp,
23432364
[],
23442365
body,
23452366
'WAID:' + body.key.id,

0 commit comments

Comments
 (0)