mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 02:57:57 +00:00 
			
		
		
		
	feat: Extract Brazil phone number normalization into generic service (#12492)
This PR refactors existing Brazil phone number normalization logic into a generic, extensible service while maintaining backward compatibility. Also extracts it into a dedicated service designed for expansion to support additional countries. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
		| @@ -47,36 +47,8 @@ module Whatsapp::IncomingMessageServiceHelpers | |||||||
|     %w[reaction ephemeral unsupported request_welcome].include?(message_type) |     %w[reaction ephemeral unsupported request_welcome].include?(message_type) | ||||||
|   end |   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) |   def processed_waid(waid) | ||||||
|     # in case of Brazil, we need to do additional processing |     Whatsapp::PhoneNumberNormalizationService.new(inbox).normalize_and_find_contact(waid) | ||||||
|     # 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 |  | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def error_webhook_event?(message) |   def error_webhook_event?(message) | ||||||
|   | |||||||
| @@ -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 | ||||||
| @@ -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 | ||||||
							
								
								
									
										39
									
								
								app/services/whatsapp/phone_number_normalization_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								app/services/whatsapp/phone_number_normalization_service.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
		Reference in New Issue
	
	Block a user
	 Muhsin Keloth
					Muhsin Keloth