From 166dae8e2763ebd9e8ceab50b4b3d661c9e1418f Mon Sep 17 00:00:00 2001 From: devlikepro Date: Fri, 26 Sep 2025 11:44:15 +0700 Subject: [PATCH] [core] ChatWoot - fix brazil numbers fix #1261 --- src/apps/chatwoot/client/ContactAPI.ts | 5 +- .../chatwoot/contacts/WhatsAppContactInfo.ts | 4 +- src/core/utils/PhoneJidNormalizer.test.ts | 36 ++++++++++ src/core/utils/PhoneJidNormalizer.ts | 66 +++++++++++++++++++ 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 src/core/utils/PhoneJidNormalizer.test.ts create mode 100644 src/core/utils/PhoneJidNormalizer.ts diff --git a/src/apps/chatwoot/client/ContactAPI.ts b/src/apps/chatwoot/client/ContactAPI.ts index 7f802bd1..091582dd 100644 --- a/src/apps/chatwoot/client/ContactAPI.ts +++ b/src/apps/chatwoot/client/ContactAPI.ts @@ -13,6 +13,7 @@ import { isJidCusFormat } from '@waha/utils/wa'; import * as lodash from 'lodash'; import { AttributeKey } from '../const'; +import { E164Parser } from '@waha/core/utils/PhoneJidNormalizer'; export interface ContactResponse { data: generic_id & contact; @@ -64,8 +65,8 @@ export class ContactAPI { if (isJidCusFormat(chatId)) { // Search by phone - let phone_number = chatId.split('@')[0]; - phone_number = phone_number.replace('+', ''); + const phoneNumberE164 = E164Parser.fromJid(chatId); + const phone_number = phoneNumberE164.replace('+', ''); payload[payload.length - 1].query_operator = 'OR'; payload.push({ attribute_key: 'phone_number', diff --git a/src/apps/chatwoot/contacts/WhatsAppContactInfo.ts b/src/apps/chatwoot/contacts/WhatsAppContactInfo.ts index 5fa94242..deaade1e 100644 --- a/src/apps/chatwoot/contacts/WhatsAppContactInfo.ts +++ b/src/apps/chatwoot/contacts/WhatsAppContactInfo.ts @@ -15,6 +15,7 @@ import { isPnUser, } from '@waha/core/utils/jids'; import { UnknownJIDFormat } from '@waha/apps/chatwoot/errors'; +import { E164Parser } from '@waha/core/utils/PhoneJidNormalizer'; /** * Base WhatsApp contact info class @@ -68,8 +69,7 @@ class JidContactInfo extends ChatContactInfo { const contact: any = await this.session.getContact(this.chatId); const name = contact?.name || contact?.pushName || contact?.pushname || this.chatId; - const phoneNumber = this.chatId.split('@')[0]; - const phoneNumberE164 = '+' + phoneNumber; + const phoneNumberE164 = E164Parser.fromJid(this.chatId); const result: Contact = { name: name, diff --git a/src/core/utils/PhoneJidNormalizer.test.ts b/src/core/utils/PhoneJidNormalizer.test.ts new file mode 100644 index 00000000..e519373a --- /dev/null +++ b/src/core/utils/PhoneJidNormalizer.test.ts @@ -0,0 +1,36 @@ +import { E164Parser } from './PhoneJidNormalizer'; + +describe('E164Parser.fromJid', () => { + it('parses basic JID to +number', () => { + expect(E164Parser.fromJid('14155552671@s.whatsapp.net')).toBe( + '+14155552671', + ); + }); + + it('returns null for empty or missing local part', () => { + expect(E164Parser.fromJid('')).toBeNull(); + expect(E164Parser.fromJid('@s.whatsapp.net')).toBeNull(); + }); + + it('applies Brazil DDD + add-9 rule when local has 8 digits and not starting with 9', () => { + // +55 and local does not start with 9 + expect(E164Parser.fromJid('553188888888@s.whatsapp.net')).toBe( + '+5531988888888', + ); + }); + + it('does not add 9 when local already starts with 9 (Brazil)', () => { + expect(E164Parser.fromJid('553198888888@s.whatsapp.net')).toBe( + '+553198888888', + ); + expect(E164Parser.fromJid('553199999999@s.whatsapp.net')).toBe( + '+553199999999', + ); + }); + + it('leaves non-Brazil numbers unchanged', () => { + expect(E164Parser.fromJid('447911123456@s.whatsapp.net')).toBe( + '+447911123456', + ); + }); +}); diff --git a/src/core/utils/PhoneJidNormalizer.ts b/src/core/utils/PhoneJidNormalizer.ts new file mode 100644 index 00000000..265eea7d --- /dev/null +++ b/src/core/utils/PhoneJidNormalizer.ts @@ -0,0 +1,66 @@ +type RewriteRule = { + name: string; + re: RegExp; + replace: string; +}; + +/** + * Convert JID to Phone Number if possible + * Applies some formatting rules + */ +export class PhoneJidNormalizer { + constructor(private rules: RewriteRule[] = []) {} + + /** + * jid like "553188888888@s.whatsapp.net" → "+553188888888" + */ + private parseFromJid(jid: string): string | null { + if (!jid) { + return null; + } + const local = jid.split('@', 1)[0] ?? ''; + if (!local) { + return null; + } + return `+${local}`; + } + + /** + * Apply rewrite rules + */ + private rewrite(number: string): string { + for (const rule of this.rules) { + if (rule.re.test(number)) { + number = number.replace(rule.re, rule.replace); + return number; + } + } + return number; + } + + /** + * Converts a JID (Jabber ID) into an E.164 formatted phone number string. + * Applies rules if any + */ + fromJid(jid: string): string | null { + let number = this.parseFromJid(jid); + if (!number) { + return null; + } + number = this.rewrite(number); + return number; + } +} + +const RULES = [ + // Brazil - add 9 before Direct Distance Dialing + // +55 , not already starting with 9 + // +553188888888 => +5531988888888 + { + name: 'br-add-9-after-ddd', + re: /^\+55(\d{2})(?!9)(\d{8})$/, + replace: '+55$19$2', + }, +]; + +export const E164Parser = new PhoneJidNormalizer(RULES);