From 4eee54ba8ca86add14fd26d76db11d88e12ac8e2 Mon Sep 17 00:00:00 2001 From: "samet.karakayali" Date: Thu, 3 Sep 2026 15:56:56 +0300 Subject: [PATCH 1/3] fix(chatwoot): handle missing remoteJidAlt for @lid contacts in createConversatio --- .../chatwoot/services/chatwoot.service.ts | 59 +++++++++++-------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index 906fff1881..4b8bae22e8 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -632,8 +632,10 @@ export class ChatwootService { public async createConversation(instance: InstanceDto, body: any) { const isLid = body.key.addressingMode === 'lid'; const isGroup = body.key.remoteJid.endsWith('@g.us'); - const phoneNumber = isLid && !isGroup ? body.key.remoteJidAlt : body.key.remoteJid; const { remoteJid } = body.key; + // When addressingMode is lid, remoteJidAlt holds the phone JID. + // If it is missing, fall back to the LID itself so conversation creation can proceed. + const phoneNumber = isLid && !isGroup ? body.key.remoteJidAlt || remoteJid : remoteJid; const cacheKey = `${instance.instanceName}:createConversation-${remoteJid}`; const lockKey = `${instance.instanceName}:lock:createConversation-${remoteJid}`; const maxWaitTime = 5000; // 5 seconds @@ -642,27 +644,36 @@ export class ChatwootService { try { // Processa atualização de contatos já criados @lid - if (phoneNumber && remoteJid && !isGroup) { - const contact = await this.findContact(instance, phoneNumber.split('@')[0]); - if (contact && contact.identifier !== remoteJid) { - this.logger.verbose( - `Identifier needs update: (contact.identifier: ${contact.identifier}, phoneNumber: ${phoneNumber}, body.key.remoteJidAlt: ${remoteJid}`, - ); - const updateContact = await this.updateContact(instance, contact.id, { - identifier: phoneNumber, - phone_number: `+${phoneNumber.split('@')[0]}`, - }); - - if (updateContact === null) { - const baseContact = await this.findContact(instance, phoneNumber.split('@')[0]); - if (baseContact) { - await this.mergeContacts(baseContact.id, contact.id); + try { + if (phoneNumber && remoteJid && !isGroup) { + const phoneNumberId = phoneNumber.split('@')?.[0]; + if (!phoneNumberId) { + this.logger.warn(`Unable to extract identifier from JID: ${phoneNumber}`); + } else { + const contact = await this.findContact(instance, phoneNumberId); + if (contact && contact.identifier !== remoteJid) { this.logger.verbose( - `Merge contacts: (${baseContact.id}) ${baseContact.phone_number} and (${contact.id}) ${contact.phone_number}`, + `Identifier needs update: (contact.identifier: ${contact.identifier}, phoneNumber: ${phoneNumber}, body.key.remoteJidAlt: ${remoteJid}`, ); + const updateContact = await this.updateContact(instance, contact.id, { + identifier: phoneNumber, + phone_number: `+${phoneNumberId}`, + }); + + if (updateContact === null) { + const baseContact = await this.findContact(instance, phoneNumberId); + if (baseContact) { + await this.mergeContacts(baseContact.id, contact.id); + this.logger.verbose( + `Merge contacts: (${baseContact.id}) ${baseContact.phone_number} and (${contact.id}) ${contact.phone_number}`, + ); + } + } } } } + } catch (error) { + this.logger.warn(`Failed to update LID contact mapping for ${remoteJid}: ${error}`); } this.logger.verbose(`--- Start createConversation ---`); this.logger.verbose(`Instance: ${JSON.stringify(instance)}`); @@ -723,7 +734,7 @@ export class ChatwootService { return (await this.cache.get(cacheKey)) as number; } - const chatId = isGroup ? remoteJid : phoneNumber.split('@')[0].split(':')[0]; + const chatId = isGroup ? remoteJid : phoneNumber?.split('@')?.[0]?.split(':')?.[0]; let nameContact = !body.key.fromMe ? body.pushName : chatId; const filterInbox = await this.getInbox(instance); if (!filterInbox) return null; @@ -733,15 +744,15 @@ export class ChatwootService { const group = await this.waMonitor.waInstances[instance.instanceName].client.groupMetadata(chatId); this.logger.verbose(`Group metadata: JID:${group.JID} - Subject:${group?.subject || group?.Name}`); - const participantJid = isLid && !body.key.fromMe ? body.key.participantAlt : body.key.participant; + const participantJid = + isLid && !body.key.fromMe ? body.key.participantAlt || body.key.participant : body.key.participant; nameContact = `${group.subject} (GROUP)`; - const picture_url = await this.waMonitor.waInstances[instance.instanceName].profilePicture( - participantJid.split('@')[0], - ); + const participantId = participantJid?.split('@')?.[0]; + const picture_url = await this.waMonitor.waInstances[instance.instanceName].profilePicture(participantId); this.logger.verbose(`Participant profile picture URL: ${JSON.stringify(picture_url)}`); - const findParticipant = await this.findContact(instance, participantJid.split('@')[0]); + const findParticipant = participantId ? await this.findContact(instance, participantId) : null; if (findParticipant) { this.logger.verbose( @@ -756,7 +767,7 @@ export class ChatwootService { } else { await this.createContact( instance, - participantJid.split('@')[0].split(':')[0], + participantId?.split(':')?.[0], filterInbox.id, false, body.pushName, From d1d06f71dbf64c2f4fe5191a07f91930bb15e077 Mon Sep 17 00:00:00 2001 From: "samet.karakayali" Date: Thu, 3 Sep 2026 16:09:50 +0300 Subject: [PATCH 2/3] fix(chatwoot): skip phone lookup when LID fallback has no remoteJidAlt Co-authored-by: Cursor --- .../chatbot/chatwoot/services/chatwoot.service.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index 4b8bae22e8..3b94ef842a 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -316,7 +316,9 @@ export class ChatwootService { avatar_url: avatar_url, }; - if ((jid && jid.includes('@')) || !jid) { + // LID identifiers are not E.164 numbers. Omit phone_number so Chatwoot + // does not persist the numeric LID as a fake phone number. + if (!jid?.includes('@lid') && ((jid && jid.includes('@')) || !jid)) { data['phone_number'] = `+${phoneNumber}`; } } else { @@ -645,7 +647,7 @@ export class ChatwootService { try { // Processa atualização de contatos já criados @lid try { - if (phoneNumber && remoteJid && !isGroup) { + if (phoneNumber && remoteJid && !isGroup && (!isLid || body.key.remoteJidAlt)) { const phoneNumberId = phoneNumber.split('@')?.[0]; if (!phoneNumberId) { this.logger.warn(`Unable to extract identifier from JID: ${phoneNumber}`); @@ -781,7 +783,10 @@ export class ChatwootService { this.logger.verbose(`Contact profile picture URL: ${JSON.stringify(picture_url)}`); this.logger.verbose(`Searching contact for: ${chatId}`); - let contact = await this.findContact(instance, chatId); + const isLidFallback = isLid && !isGroup && !body.key.remoteJidAlt; + let contact = isLidFallback + ? await this.findContactByIdentifier(instance, remoteJid) + : await this.findContact(instance, chatId); if (contact) { this.logger.verbose(`Found contact: ID:${contact.id} - Name:${contact.name}`); From 5b688d0b79815ce0ceab93a38689b1924bf1451f Mon Sep 17 00:00:00 2001 From: "samet.karakayali" Date: Mon, 14 Sep 2026 17:54:40 +0300 Subject: [PATCH 3/3] fix(chatwoot): improve error handling and refactor contact search logic - Updated contact search methods to use a more consistent API structure. - Added error handling for contact retrieval processes to log warnings on failures. - Simplified payload extraction from API responses for better readability. --- .../chatwoot/services/chatwoot.service.ts | 71 ++++++++++--------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index 3b94ef842a..f11b9f83ee 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -435,42 +435,42 @@ export class ChatwootService { return null; } - // Direct search by query (q) - most common way to search by identifier/email/phone - const contact = (await (client as any).get('contacts/search', { - params: { + try { + const contact = await client.contacts.search({ + accountId: this.provider.accountId, q: identifier, - sort: 'name', - }, - })) as any; - - if (contact && contact.data && contact.data.payload && contact.data.payload.length > 0) { - return contact.data.payload[0]; - } + }); - // Fallback for older API versions or different response structures - if (contact && contact.payload && contact.payload.length > 0) { - return contact.payload[0]; + const payload = contact?.payload || (contact as any)?.data?.payload; + if (Array.isArray(payload) && payload.length > 0) { + return payload.find((item) => item.identifier === identifier) || payload[0]; + } + } catch (error) { + this.logger.warn(`Contact search by identifier failed for ${identifier}: ${error}`); } - // Try search by attribute - const contactByAttr = (await (client as any).post('contacts/filter', { - payload: [ - { - attribute_key: 'identifier', - filter_operator: 'equal_to', - values: [identifier], - query_operator: null, + try { + const contactByAttr = await chatwootRequest(this.getClientCwConfig(), { + method: 'POST', + url: `/api/v1/accounts/${this.provider.accountId}/contacts/filter`, + body: { + payload: [ + { + attribute_key: 'identifier', + filter_operator: 'equal_to', + values: [identifier], + query_operator: null, + }, + ], }, - ], - })) as any; - - if (contactByAttr && contactByAttr.payload && contactByAttr.payload.length > 0) { - return contactByAttr.payload[0]; - } + }); - // Check inside data property if using axios interceptors wrapper - if (contactByAttr && contactByAttr.data && contactByAttr.data.payload && contactByAttr.data.payload.length > 0) { - return contactByAttr.data.payload[0]; + const payload = (contactByAttr as any)?.payload || (contactByAttr as any)?.data?.payload; + if (Array.isArray(payload) && payload.length > 0) { + return payload[0]; + } + } catch (error) { + this.logger.warn(`Contact filter by identifier failed for ${identifier}: ${error}`); } return null; @@ -784,9 +784,14 @@ export class ChatwootService { this.logger.verbose(`Searching contact for: ${chatId}`); const isLidFallback = isLid && !isGroup && !body.key.remoteJidAlt; - let contact = isLidFallback - ? await this.findContactByIdentifier(instance, remoteJid) - : await this.findContact(instance, chatId); + let contact = null; + try { + contact = isLidFallback + ? await this.findContactByIdentifier(instance, remoteJid) + : await this.findContact(instance, chatId); + } catch (error) { + this.logger.warn(`Failed to search contact for ${remoteJid}: ${error}`); + } if (contact) { this.logger.verbose(`Found contact: ID:${contact.id} - Name:${contact.name}`);