Compare commits

...
5 changed files with 143 additions and 2 deletions
@@ -0,0 +1,23 @@
# Handles Mexico phone number normalization
#
# WhatsApp may include a "1" after Mexico's country code (521), while contacts
# can already be stored without it (52). Normalize to the stored WAID (digits-only)
# format; provider formatting (whatsapp:+...) happens in the normalization service.
# The mobile "1" only appears on the 13-digit form (52 + 1 + 10 digits), so guard
# on length to avoid stripping a legitimate "1" from a shorter national number.
class Whatsapp::PhoneNormalizers::MexicoPhoneNormalizer < Whatsapp::PhoneNormalizers::BasePhoneNormalizer
COUNTRY_CODE_WITH_MOBILE_LENGTH = 13
def normalize(waid)
return waid unless handles_country?(waid)
return waid unless waid.length == COUNTRY_CODE_WITH_MOBILE_LENGTH
waid.sub(/^521/, '52')
end
private
def country_code_pattern
/^52/
end
end
@@ -1,5 +1,5 @@
# Service to handle phone number normalization for WhatsApp messages
# Currently supports Brazil and Argentina phone number format variations
# Currently supports Brazil, Argentina, and Mexico phone number format variations
# Supports both WhatsApp Cloud API and Twilio WhatsApp providers
class Whatsapp::PhoneNumberNormalizationService
def initialize(inbox)
@@ -64,6 +64,7 @@ class Whatsapp::PhoneNumberNormalizationService
NORMALIZERS = [
Whatsapp::PhoneNormalizers::BrazilPhoneNormalizer,
Whatsapp::PhoneNormalizers::ArgentinaPhoneNormalizer
Whatsapp::PhoneNormalizers::ArgentinaPhoneNormalizer,
Whatsapp::PhoneNormalizers::MexicoPhoneNormalizer
].freeze
end
@@ -788,6 +788,57 @@ describe Twilio::IncomingMessageService do
expect(whatsapp_twilio_channel.inbox.contact_inboxes.first.source_id).to eq('whatsapp:+541123456789')
end
end
describe 'When the incoming number is a Mexican WhatsApp number with 1 after country code' do
let!(:whatsapp_twilio_channel) do
create(:channel_twilio_sms, :whatsapp, account: account, account_sid: 'ACxxx',
inbox: create(:inbox, account: account, greeting_enabled: false))
end
it 'appends to existing contact when contact inbox exists without the extra 1' do
contact = create(:contact, account: account, phone_number: '+525512345678')
contact_inbox = create(:contact_inbox,
source_id: 'whatsapp:+525512345678',
contact: contact,
inbox: whatsapp_twilio_channel.inbox)
last_conversation = create(:conversation, inbox: whatsapp_twilio_channel.inbox, contact_inbox: contact_inbox)
params = {
SmsSid: 'SMxx',
From: 'whatsapp:+5215512345678',
AccountSid: 'ACxxx',
MessagingServiceSid: whatsapp_twilio_channel.messaging_service_sid,
Body: 'Test message from Mexico',
ProfileName: 'Maria Lopez'
}
described_class.new(params: params).perform
expect(whatsapp_twilio_channel.inbox.conversations.count).to eq(1)
expect(last_conversation.messages.last.content).to eq('Test message from Mexico')
expect(whatsapp_twilio_channel.inbox.contact_inboxes.first.source_id)
.to eq('whatsapp:+525512345678')
end
it 'creates contact inbox with incoming number when no existing contact matches' do
params = {
SmsSid: 'SMxx',
From: 'whatsapp:+5215512345678',
AccountSid: 'ACxxx',
MessagingServiceSid: whatsapp_twilio_channel.messaging_service_sid,
Body: 'Test message from Mexico',
ProfileName: 'Maria Lopez'
}
described_class.new(params: params).perform
expect(whatsapp_twilio_channel.inbox.conversations.count).not_to eq(0)
expect(whatsapp_twilio_channel.inbox.contacts.first.name).to eq('Maria Lopez')
expect(whatsapp_twilio_channel.inbox.messages.first.content).to eq('Test message from Mexico')
expect(whatsapp_twilio_channel.inbox.contact_inboxes.first.source_id)
.to eq('whatsapp:+5215512345678')
end
end
end
end
end
@@ -224,6 +224,49 @@ describe Whatsapp::IncomingMessageWhatsappCloudService do
end
end
describe 'when the incoming waid is a Mexican number with 1 after country code' do
let(:mexico_params) do
{
phone_number: whatsapp_channel.phone_number,
object: 'whatsapp_business_account',
entry: [{
changes: [{
value: {
contacts: [{ profile: { name: 'Maria Lopez' }, wa_id: '5215512345678' }],
messages: [{
from: '5215512345678',
id: 'wamid.cloud-mexico-message',
text: { body: 'Test message from Mexico' },
timestamp: '1664799904',
type: 'text'
}]
}
}]
}]
}.with_indifferent_access
end
it 'appends to existing contact when a contact inbox exists without the extra 1' do
contact_inbox = create(:contact_inbox, inbox: whatsapp_channel.inbox, source_id: '525512345678')
last_conversation = create(:conversation, inbox: whatsapp_channel.inbox, contact_inbox: contact_inbox)
described_class.new(inbox: whatsapp_channel.inbox, params: mexico_params).perform
expect(whatsapp_channel.inbox.conversations.count).to eq(1)
expect(last_conversation.messages.last.content).to eq('Test message from Mexico')
expect(whatsapp_channel.inbox.contact_inboxes.first.source_id).to eq('525512345678')
end
it 'creates a contact inbox with the incoming waid when no existing contact matches' do
described_class.new(inbox: whatsapp_channel.inbox, params: mexico_params).perform
expect(whatsapp_channel.inbox.conversations.count).not_to eq(0)
expect(whatsapp_channel.inbox.contacts.first.name).to eq('Maria Lopez')
expect(whatsapp_channel.inbox.messages.first.content).to eq('Test message from Mexico')
expect(whatsapp_channel.inbox.contact_inboxes.first.source_id).to eq('5215512345678')
end
end
context 'when invalid params' do
it 'will not throw error' do
described_class.new(inbox: whatsapp_channel.inbox, params: { phone_number: whatsapp_channel.phone_number,
@@ -0,0 +1,23 @@
require 'rails_helper'
describe Whatsapp::PhoneNormalizers::MexicoPhoneNormalizer do
subject(:normalizer) { described_class.new }
describe '#normalize' do
it 'strips the mobile "1" from the 13-digit form (521 + 10 digits)' do
expect(normalizer.normalize('5215512345678')).to eq('525512345678')
end
it 'leaves the already-normalized 12-digit form untouched' do
expect(normalizer.normalize('525512345678')).to eq('525512345678')
end
it 'does not strip a legitimate leading "1" from a 12-digit national number' do
expect(normalizer.normalize('521512345678')).to eq('521512345678')
end
it 'returns non-Mexico numbers unchanged' do
expect(normalizer.normalize('5511999998888')).to eq('5511999998888')
end
end
end