mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-30 18:47:51 +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) | ||||
|   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) | ||||
|   | ||||
| @@ -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