From 6e432610be434b9b61bffa99b8a3559b515a64b9 Mon Sep 17 00:00:00 2001 From: IgorLimaJesus Date: Thu, 13 Aug 2026 12:18:42 -0300 Subject: [PATCH 1/2] fix(baileys): resolve Brazilian 9th digit lost in LID/PN mapping (#2687) --- .../whatsapp/whatsapp.baileys.service.ts | 13 +++++++- src/api/services/channel.service.ts | 30 +++++++++-------- src/utils/createJid.ts | 30 +++++++++-------- src/utils/onWhatsappCache.ts | 33 ++++++++++++++++--- 4 files changed, 75 insertions(+), 31 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 22839fd451..428d0362c5 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -4114,7 +4114,18 @@ export class BaileysStartupService extends ChannelStartupService { numberVerified = verify.find((v) => v.jid === user.jid); } - const numberJid = numberVerified?.jid || user.jid; + let numberJid = numberVerified?.jid || user.jid; + if ( + user.number.startsWith('55') && + user.number.length === 13 && + user.number[4] === '9' && + numberJid.includes('@s.whatsapp.net') + ) { + const jidNum = numberJid.split('@')[0]; + if (jidNum.length === 12) { + numberJid = `${user.number}@s.whatsapp.net`; + } + } return new OnWhatsAppDto( numberJid, diff --git a/src/api/services/channel.service.ts b/src/api/services/channel.service.ts index 3ba03d5a02..38d7d396d3 100644 --- a/src/api/services/channel.service.ts +++ b/src/api/services/channel.service.ts @@ -479,22 +479,26 @@ export class ChannelStartupService { } // Check if the number is br - public formatBRNumber(jid: string) { - const regexp = new RegExp(/^(\d{2})(\d{2})\d{1}(\d{8})$/); - if (regexp.test(jid)) { - const match = regexp.exec(jid); - if (match && match[1] === '55') { - const joker = Number.parseInt(match[3][0]); - const ddd = Number.parseInt(match[2]); - if (joker < 7 || ddd < 31) { - return match[0]; - } - return match[1] + match[2] + match[3]; - } + public formatBRNumber(jid: string): string { + if (!jid.startsWith('55')) { return jid; - } else { + } + + // 13-digit Brazilian mobile number (55 + DDD + 9 + 8 digits) + if (jid.length === 13) { return jid; } + + // 12-digit Brazilian number (55 + DDD + 8 digits) + if (jid.length === 12) { + const firstDigit = Number.parseInt(jid[4]); + // If local number starts with 6, 7, 8, or 9, it's a mobile number missing the 9th digit '9' + if (firstDigit >= 6) { + return `${jid.slice(0, 4)}9${jid.slice(4)}`; + } + } + + return jid; } public async fetchContacts(query: Query) { diff --git a/src/utils/createJid.ts b/src/utils/createJid.ts index 23a3afe1fa..21543aadad 100644 --- a/src/utils/createJid.ts +++ b/src/utils/createJid.ts @@ -14,22 +14,26 @@ function formatMXOrARNumber(jid: string): string { } // Check if the number is br -function formatBRNumber(jid: string) { - const regexp = new RegExp(/^(\d{2})(\d{2})\d{1}(\d{8})$/); - if (regexp.test(jid)) { - const match = regexp.exec(jid); - if (match && match[1] === '55') { - const joker = Number.parseInt(match[3][0]); - const ddd = Number.parseInt(match[2]); - if (joker < 7 || ddd < 31) { - return match[0]; - } - return match[1] + match[2] + match[3]; - } +function formatBRNumber(jid: string): string { + if (!jid.startsWith('55')) { return jid; - } else { + } + + // 13-digit Brazilian mobile number (55 + DDD + 9 + 8 digits) + if (jid.length === 13) { return jid; } + + // 12-digit Brazilian number (55 + DDD + 8 digits) + if (jid.length === 12) { + const firstDigit = Number.parseInt(jid[4]); + // If local number starts with 6, 7, 8, or 9, it's a mobile number missing the 9th digit '9' + if (firstDigit >= 6) { + return `${jid.slice(0, 4)}9${jid.slice(4)}`; + } + } + + return jid; } export function createJid(number: string): string { diff --git a/src/utils/onWhatsappCache.ts b/src/utils/onWhatsappCache.ts index 8d7a2c16a7..849351090e 100644 --- a/src/utils/onWhatsappCache.ts +++ b/src/utils/onWhatsappCache.ts @@ -22,9 +22,19 @@ function getAvailableNumbers(remoteJid: string) { // Brazilian numbers if (remoteJid.startsWith('55')) { - const numberWithDigit = - number.slice(4, 5) === '9' && number.length === 13 ? number : `${number.slice(0, 4)}9${number.slice(4)}`; - const numberWithoutDigit = number.length === 12 ? number : number.slice(0, 4) + number.slice(5); + let numberWithDigit = number; + let numberWithoutDigit = number; + + if (number.length === 13 && number.slice(4, 5) === '9') { + numberWithDigit = number; + numberWithoutDigit = `${number.slice(0, 4)}${number.slice(5)}`; + } else if (number.length === 12) { + const firstDigit = Number.parseInt(number.slice(4, 5)); + if (firstDigit >= 6) { + numberWithDigit = `${number.slice(0, 4)}9${number.slice(4)}`; + numberWithoutDigit = number; + } + } numbersAvailable.push(numberWithDigit); numbersAvailable.push(numberWithoutDigit); @@ -129,8 +139,23 @@ export async function saveOnWhatsappCache(data: ISaveOnWhatsappCacheParams[]) { const newJidOptionsString = sortedJidOptions.join(','); const newLid = item.lid === 'lid' || item.remoteJid?.includes('@lid') ? 'lid' : null; + let targetRemoteJid = remoteJid; + if (existingRecord?.remoteJid) { + const existingNum = existingRecord.remoteJid.split('@')[0]; + const newNum = remoteJid.split('@')[0]; + if ( + existingNum.startsWith('55') && + existingNum.length === 13 && + existingNum[4] === '9' && + newNum.startsWith('55') && + newNum.length === 12 + ) { + targetRemoteJid = existingRecord.remoteJid; + } + } + const dataPayload = { - remoteJid: remoteJid, + remoteJid: targetRemoteJid, jidOptions: newJidOptionsString, lid: newLid, }; From 9b94b49944b6f1a8660832dd34a93dc372447d54 Mon Sep 17 00:00:00 2001 From: IgorLimaJesus Date: Thu, 13 Aug 2026 12:37:15 -0300 Subject: [PATCH 2/2] refactor(utils): centralize formatBRNumber and deduplicate cache number variants --- src/api/services/channel.service.ts | 23 ++------------------- src/utils/createJid.ts | 31 +++++++++++++++++++---------- src/utils/onWhatsappCache.ts | 8 ++++++-- 3 files changed, 28 insertions(+), 34 deletions(-) diff --git a/src/api/services/channel.service.ts b/src/api/services/channel.service.ts index 38d7d396d3..a413452bf2 100644 --- a/src/api/services/channel.service.ts +++ b/src/api/services/channel.service.ts @@ -13,7 +13,7 @@ import { Auth, Chatwoot, ConfigService, Database, HttpServer, Proxy } from '@con import { Logger } from '@config/logger.config'; import { NotFoundException } from '@exceptions'; import { Contact, Message, Prisma } from '@prisma/client'; -import { createJid } from '@utils/createJid'; +import { createJid, formatBRNumber } from '@utils/createJid'; import { WASocket } from 'baileys'; import { isArray } from 'class-validator'; import EventEmitter2 from 'eventemitter2'; @@ -478,27 +478,8 @@ export class ChannelStartupService { return jid; } - // Check if the number is br public formatBRNumber(jid: string): string { - if (!jid.startsWith('55')) { - return jid; - } - - // 13-digit Brazilian mobile number (55 + DDD + 9 + 8 digits) - if (jid.length === 13) { - return jid; - } - - // 12-digit Brazilian number (55 + DDD + 8 digits) - if (jid.length === 12) { - const firstDigit = Number.parseInt(jid[4]); - // If local number starts with 6, 7, 8, or 9, it's a mobile number missing the 9th digit '9' - if (firstDigit >= 6) { - return `${jid.slice(0, 4)}9${jid.slice(4)}`; - } - } - - return jid; + return formatBRNumber(jid); } public async fetchContacts(query: Query) { diff --git a/src/utils/createJid.ts b/src/utils/createJid.ts index 21543aadad..0a3924ce46 100644 --- a/src/utils/createJid.ts +++ b/src/utils/createJid.ts @@ -13,23 +13,32 @@ function formatMXOrARNumber(jid: string): string { return jid; } -// Check if the number is br -function formatBRNumber(jid: string): string { - if (!jid.startsWith('55')) { +const BRAZIL_COUNTRY_CODE = '55'; +const BRAZIL_PHONE_WITH_NINTH_DIGIT_LENGTH = 13; +const BRAZIL_PHONE_WITHOUT_NINTH_DIGIT_LENGTH = 12; +const BRAZIL_MOBILE_NINTH_DIGIT = '9'; +const COUNTRY_AND_AREA_CODE_LENGTH = 4; +const FIRST_SUBSCRIBER_DIGIT_INDEX = 4; +const MINIMUM_MOBILE_FIRST_DIGIT = 6; + +export function formatBRNumber(jid: string): string { + if (!jid.startsWith(BRAZIL_COUNTRY_CODE)) { return jid; } - // 13-digit Brazilian mobile number (55 + DDD + 9 + 8 digits) - if (jid.length === 13) { + if (jid.length === BRAZIL_PHONE_WITH_NINTH_DIGIT_LENGTH) { return jid; } - // 12-digit Brazilian number (55 + DDD + 8 digits) - if (jid.length === 12) { - const firstDigit = Number.parseInt(jid[4]); - // If local number starts with 6, 7, 8, or 9, it's a mobile number missing the 9th digit '9' - if (firstDigit >= 6) { - return `${jid.slice(0, 4)}9${jid.slice(4)}`; + if (jid.length === BRAZIL_PHONE_WITHOUT_NINTH_DIGIT_LENGTH) { + const firstSubscriberDigit = Number.parseInt(jid[FIRST_SUBSCRIBER_DIGIT_INDEX]); + const isMobileMissingNinthDigit = firstSubscriberDigit >= MINIMUM_MOBILE_FIRST_DIGIT; + + if (isMobileMissingNinthDigit) { + const countryAndAreaCode = jid.slice(0, COUNTRY_AND_AREA_CODE_LENGTH); + const subscriberNumber = jid.slice(COUNTRY_AND_AREA_CODE_LENGTH); + + return `${countryAndAreaCode}${BRAZIL_MOBILE_NINTH_DIGIT}${subscriberNumber}`; } } diff --git a/src/utils/onWhatsappCache.ts b/src/utils/onWhatsappCache.ts index 849351090e..05233d6ebf 100644 --- a/src/utils/onWhatsappCache.ts +++ b/src/utils/onWhatsappCache.ts @@ -37,7 +37,9 @@ function getAvailableNumbers(remoteJid: string) { } numbersAvailable.push(numberWithDigit); - numbersAvailable.push(numberWithoutDigit); + if (numberWithoutDigit !== numberWithDigit) { + numbersAvailable.push(numberWithoutDigit); + } } // Mexican/Argentina numbers @@ -58,7 +60,9 @@ function getAvailableNumbers(remoteJid: string) { const numberWithoutDigit = number.length === 12 ? number : number.slice(0, 2) + number.slice(3); numbersAvailable.push(numberWithDigit); - numbersAvailable.push(numberWithoutDigit); + if (numberWithoutDigit !== numberWithDigit) { + numbersAvailable.push(numberWithoutDigit); + } } // Other countries