From b23070a8153f88971f9a260accc82832aa051861 Mon Sep 17 00:00:00 2001 From: Shimon Date: Sun, 11 Oct 2026 15:24:12 -0500 Subject: [PATCH] fix(chatwoot): make LID contact lookup work and ignore LID digits stored as phone findContactByIdentifier called client.get() and client.post(), which do not exist on ChatwootClient, so every call threw "TypeError: get is not a function". resolveLidToPhone swallowed it as "Error resolving LID from database", and the 422 recovery path in createContact rethrew it. Use client.contacts.search and the contacts/filter request already used by findContact, and accept only an exact identifier match because search is a substring match across several fields. Contacts created from a LID carry the LID digits as phone_number. Skip those in resolveLidToPhone so a LID is never mapped to @s.whatsapp.net. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../chatwoot/services/chatwoot.service.ts | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index c7a9753aa6..c66ea66a65 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -441,45 +441,40 @@ 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: { - q: identifier, - sort: 'name', - }, + // ChatwootClient exposes resource namespaces, not generic get()/post(). + // Search is a substring match across several fields, so only an exact + // identifier match is accepted. + const search = (await client.contacts.search({ + accountId: this.provider.accountId, + q: identifier, })) as any; - if (contact && contact.data && contact.data.payload && contact.data.payload.length > 0) { - return contact.data.payload[0]; - } + const searched = (search?.payload ?? search?.data?.payload ?? []).find( + (contact: any) => contact.identifier === identifier, + ); - // Fallback for older API versions or different response structures - if (contact && contact.payload && contact.payload.length > 0) { - return contact.payload[0]; + if (searched) { + return searched; } - // 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, - }, - ], + const filter = (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]; - } - - return null; + return ( + (filter?.payload ?? filter?.data?.payload ?? []).find((contact: any) => contact.identifier === identifier) ?? null + ); } public async findContact(instance: InstanceDto, phoneNumber: string) { @@ -2784,9 +2779,14 @@ export class ChatwootService { const lidIdentifier = this.normalizeJidIdentifier(remoteJid); const contact = await this.findContactByIdentifier(instance, lidIdentifier); - if (contact && contact.phone_number) { + // Contacts created from a LID carry the LID digits as phone_number. + // That is not a WhatsApp number and must not be used as one. + const lidDigits = remoteJid.split('@')[0].split(':')[0]; + const phoneDigits = contact?.phone_number?.replace(/\D/g, ''); + + if (phoneDigits && phoneDigits !== lidDigits) { // Converte +554498860240 → 554498860240@s.whatsapp.net - const phoneNumber = contact.phone_number.replace('+', '') + '@s.whatsapp.net'; + const phoneNumber = phoneDigits + '@s.whatsapp.net'; this.saveLidMapping(remoteJid, phoneNumber); this.logger.verbose(`LID resolved from database: ${remoteJid} → ${phoneNumber}`); return phoneNumber;