diff --git a/app/services/whatsapp/incoming_message_service_helpers.rb b/app/services/whatsapp/incoming_message_service_helpers.rb index c5474314b..e40dc408f 100644 --- a/app/services/whatsapp/incoming_message_service_helpers.rb +++ b/app/services/whatsapp/incoming_message_service_helpers.rb @@ -47,36 +47,8 @@ module Whatsapp::IncomingMessageServiceHelpers %w[reaction ephemeral unsupported request_welcome].include?(message_type) end - def brazil_phone_number?(phone_number) - phone_number.match(/^55/) - end - - # ref: https://github.com/chatwoot/chatwoot/issues/5840 - def normalised_brazil_mobile_number(phone_number) - # DDD : Area codes in Brazil are popularly known as "DDD codes" (códigos DDD) or simply "DDD", from the initials of "direct distance dialing" - # https://en.wikipedia.org/wiki/Telephone_numbers_in_Brazil - ddd = phone_number[2, 2] - # Remove country code and DDD to obtain the number - number = phone_number[4, phone_number.length - 4] - normalised_number = "55#{ddd}#{number}" - # insert 9 to convert the number to the new mobile number format - normalised_number = "55#{ddd}9#{number}" if normalised_number.length != 13 - normalised_number - end - def processed_waid(waid) - # in case of Brazil, we need to do additional processing - # https://github.com/chatwoot/chatwoot/issues/5840 - if brazil_phone_number?(waid) - # check if there is an existing contact inbox with the normalised waid - # We will create conversation against it - contact_inbox = inbox.contact_inboxes.find_by(source_id: normalised_brazil_mobile_number(waid)) - - # if there is no contact inbox with the waid without 9, - # We will create contact inboxes and contacts with the number 9 added - waid = contact_inbox.source_id if contact_inbox.present? - end - waid + Whatsapp::PhoneNumberNormalizationService.new(inbox).normalize_and_find_contact(waid) end def error_webhook_event?(message) diff --git a/app/services/whatsapp/phone_normalizers/base_phone_normalizer.rb b/app/services/whatsapp/phone_normalizers/base_phone_normalizer.rb new file mode 100644 index 000000000..91882b8e5 --- /dev/null +++ b/app/services/whatsapp/phone_normalizers/base_phone_normalizer.rb @@ -0,0 +1,19 @@ +# Base class for country-specific phone number normalizers +# Each country normalizer should inherit from this class and implement: +# - country_code_pattern: regex to identify the country code +# - normalize: logic to convert phone number to normalized format for contact lookup +class Whatsapp::PhoneNormalizers::BasePhoneNormalizer + def handles_country?(waid) + waid.match(country_code_pattern) + end + + def normalize(waid) + raise NotImplementedError, 'Subclasses must implement #normalize' + end + + private + + def country_code_pattern + raise NotImplementedError, 'Subclasses must implement #country_code_pattern' + end +end diff --git a/app/services/whatsapp/phone_normalizers/brazil_phone_normalizer.rb b/app/services/whatsapp/phone_normalizers/brazil_phone_normalizer.rb new file mode 100644 index 000000000..24a1c406e --- /dev/null +++ b/app/services/whatsapp/phone_normalizers/brazil_phone_normalizer.rb @@ -0,0 +1,26 @@ +# Handles Brazil phone number normalization +# ref: https://github.com/chatwoot/chatwoot/issues/5840 +# +# Brazil changed its mobile number system by adding a "9" prefix to existing numbers. +# This normalizer adds the "9" digit if the number is 12 digits (making it 13 digits total) +# to match the new format: 55 + DDD + 9 + number +class Whatsapp::PhoneNormalizers::BrazilPhoneNormalizer < Whatsapp::PhoneNormalizers::BasePhoneNormalizer + COUNTRY_CODE_LENGTH = 2 + DDD_LENGTH = 2 + + def normalize(waid) + return waid unless handles_country?(waid) + + ddd = waid[COUNTRY_CODE_LENGTH, DDD_LENGTH] + number = waid[COUNTRY_CODE_LENGTH + DDD_LENGTH, waid.length - (COUNTRY_CODE_LENGTH + DDD_LENGTH)] + normalized_number = "55#{ddd}#{number}" + normalized_number = "55#{ddd}9#{number}" if normalized_number.length != 13 + normalized_number + end + + private + + def country_code_pattern + /^55/ + end +end diff --git a/app/services/whatsapp/phone_number_normalization_service.rb b/app/services/whatsapp/phone_number_normalization_service.rb new file mode 100644 index 000000000..b8e416794 --- /dev/null +++ b/app/services/whatsapp/phone_number_normalization_service.rb @@ -0,0 +1,39 @@ +# Service to handle phone number normalization for WhatsApp messages +# Currently supports Brazil phone number format variations +# Designed to be extensible for additional countries in future PRs +# +# Usage: Whatsapp::PhoneNumberNormalizationService.new(inbox).normalize_and_find_contact(waid) +class Whatsapp::PhoneNumberNormalizationService + def initialize(inbox) + @inbox = inbox + end + + # Main entry point for phone number normalization + # Returns the source_id of an existing contact if found, otherwise returns original waid + def normalize_and_find_contact(waid) + normalizer = find_normalizer_for_country(waid) + return waid unless normalizer + + normalized_waid = normalizer.normalize(waid) + existing_contact_inbox = find_existing_contact_inbox(normalized_waid) + + existing_contact_inbox&.source_id || waid + end + + private + + attr_reader :inbox + + def find_normalizer_for_country(waid) + NORMALIZERS.map(&:new) + .find { |normalizer| normalizer.handles_country?(waid) } + end + + def find_existing_contact_inbox(normalized_waid) + inbox.contact_inboxes.find_by(source_id: normalized_waid) + end + + NORMALIZERS = [ + Whatsapp::PhoneNormalizers::BrazilPhoneNormalizer + ].freeze +end