diff --git a/app/services/whatsapp/incoming_message_base_service.rb b/app/services/whatsapp/incoming_message_base_service.rb index 94ad5c7d1..0aed8dba0 100644 --- a/app/services/whatsapp/incoming_message_base_service.rb +++ b/app/services/whatsapp/incoming_message_base_service.rb @@ -92,6 +92,9 @@ class Whatsapp::IncomingMessageBaseService @contact_inbox = contact_inbox @contact = contact_inbox.contact + + # Update existing contact name if ProfileName is available and current name is just phone number + update_contact_with_profile_name(contact_params) end def set_conversation @@ -171,4 +174,21 @@ class Whatsapp::IncomingMessageBaseService ) end end + + def update_contact_with_profile_name(contact_params) + profile_name = contact_params.dig(:profile, :name) + return if profile_name.blank? + return if @contact.name == profile_name + + # Only update if current name exactly matches the phone number or formatted phone number + return unless contact_name_matches_phone_number? + + @contact.update!(name: profile_name) + end + + def contact_name_matches_phone_number? + phone_number = "+#{@processed_params[:messages].first[:from]}" + formatted_phone_number = TelephoneNumber.parse(phone_number).international_number + @contact.name == phone_number || @contact.name == formatted_phone_number + end end diff --git a/app/services/whatsapp/oneoff_campaign_service.rb b/app/services/whatsapp/oneoff_campaign_service.rb index 47a971f41..de2713ac0 100644 --- a/app/services/whatsapp/oneoff_campaign_service.rb +++ b/app/services/whatsapp/oneoff_campaign_service.rb @@ -84,7 +84,7 @@ class Whatsapp::OneoffCampaignService namespace: namespace, lang_code: lang_code, parameters: processed_parameters - }) + }, nil) rescue StandardError => e Rails.logger.error "Failed to send WhatsApp template message to #{to}: #{e.message}" diff --git a/app/services/whatsapp/providers/base_service.rb b/app/services/whatsapp/providers/base_service.rb index 97665f7ef..9fd1f6267 100644 --- a/app/services/whatsapp/providers/base_service.rb +++ b/app/services/whatsapp/providers/base_service.rb @@ -15,7 +15,7 @@ class Whatsapp::Providers::BaseService raise 'Overwrite this method in child class' end - def send_template(_phone_number, _template_info) + def send_template(_phone_number, _template_info, _message) raise 'Overwrite this method in child class' end @@ -31,27 +31,27 @@ class Whatsapp::Providers::BaseService raise 'Overwrite this method in child class' end - def process_response(response) + def process_response(response, message) parsed_response = response.parsed_response if response.success? && parsed_response['error'].blank? parsed_response['messages'].first['id'] else - handle_error(response) + handle_error(response, message) nil end end - def handle_error(response) + def handle_error(response, message) Rails.logger.error response.body - return if @message.blank? + return if message.blank? # https://developers.facebook.com/docs/whatsapp/cloud-api/support/error-codes/#sample-response error_message = error_message(response) return if error_message.blank? - @message.external_error = error_message - @message.status = :failed - @message.save! + message.external_error = error_message + message.status = :failed + message.save! end def create_buttons(items) diff --git a/app/services/whatsapp/providers/whatsapp_360_dialog_service.rb b/app/services/whatsapp/providers/whatsapp_360_dialog_service.rb index beb11d556..352f2d246 100644 --- a/app/services/whatsapp/providers/whatsapp_360_dialog_service.rb +++ b/app/services/whatsapp/providers/whatsapp_360_dialog_service.rb @@ -10,7 +10,7 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS end end - def send_template(phone_number, template_info) + def send_template(phone_number, template_info, message) response = HTTParty.post( "#{api_base_path}/messages", headers: api_headers, @@ -21,7 +21,7 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS }.to_json ) - process_response(response) + process_response(response, message) end def sync_templates @@ -68,7 +68,7 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS }.to_json ) - process_response(response) + process_response(response, message) end def send_attachment_message(phone_number, message) @@ -90,7 +90,7 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS }.to_json ) - process_response(response) + process_response(response, message) end def error_message(response) @@ -123,6 +123,6 @@ class Whatsapp::Providers::Whatsapp360DialogService < Whatsapp::Providers::BaseS }.to_json ) - process_response(response) + process_response(response, message) end end diff --git a/app/services/whatsapp/providers/whatsapp_cloud_service.rb b/app/services/whatsapp/providers/whatsapp_cloud_service.rb index 34939048a..68e965595 100644 --- a/app/services/whatsapp/providers/whatsapp_cloud_service.rb +++ b/app/services/whatsapp/providers/whatsapp_cloud_service.rb @@ -11,7 +11,7 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi end end - def send_template(phone_number, template_info) + def send_template(phone_number, template_info, message) template_body = template_body_parameters(template_info) request_body = { @@ -28,7 +28,7 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi body: request_body.to_json ) - process_response(response) + process_response(response, message) end def sync_templates @@ -92,7 +92,7 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi }.to_json ) - process_response(response) + process_response(response, message) end def send_attachment_message(phone_number, message) @@ -115,7 +115,7 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi }.to_json ) - process_response(response) + process_response(response, message) end def error_message(response) @@ -179,6 +179,6 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi }.to_json ) - process_response(response) + process_response(response, message) end end diff --git a/app/services/whatsapp/send_on_whatsapp_service.rb b/app/services/whatsapp/send_on_whatsapp_service.rb index 5f91bce16..20419c0cd 100644 --- a/app/services/whatsapp/send_on_whatsapp_service.rb +++ b/app/services/whatsapp/send_on_whatsapp_service.rb @@ -33,7 +33,7 @@ class Whatsapp::SendOnWhatsappService < Base::SendOnChannelService namespace: namespace, lang_code: lang_code, parameters: processed_parameters - }) + }, message) message.update!(source_id: message_id) if message_id.present? end diff --git a/spec/services/whatsapp/incoming_message_service_spec.rb b/spec/services/whatsapp/incoming_message_service_spec.rb index 4035a47df..ede1ba824 100644 --- a/spec/services/whatsapp/incoming_message_service_spec.rb +++ b/spec/services/whatsapp/incoming_message_service_spec.rb @@ -371,5 +371,100 @@ describe Whatsapp::IncomingMessageService do Redis::Alfred.delete(key) end end + + context 'when profile name is available for contact updates' do + let(:wa_id) { '1234567890' } + let(:phone_number) { "+#{wa_id}" } + + it 'updates existing contact name when current name matches phone number' do + # Create contact with phone number as name + existing_contact = create(:contact, + account: whatsapp_channel.inbox.account, + name: phone_number, + phone_number: phone_number) + create(:contact_inbox, + contact: existing_contact, + inbox: whatsapp_channel.inbox, + source_id: wa_id) + + params = { + 'contacts' => [{ 'profile' => { 'name' => 'Jane Smith' }, 'wa_id' => wa_id }], + 'messages' => [{ 'from' => wa_id, 'id' => 'message123', 'text' => { 'body' => 'Hello' }, + 'timestamp' => '1633034394', 'type' => 'text' }] + }.with_indifferent_access + + described_class.new(inbox: whatsapp_channel.inbox, params: params).perform + existing_contact.reload + expect(existing_contact.name).to eq('Jane Smith') + end + + it 'does not update contact name when current name is different from phone number' do + # Create contact with human name + existing_contact = create(:contact, + account: whatsapp_channel.inbox.account, + name: 'John Doe', + phone_number: phone_number) + create(:contact_inbox, + contact: existing_contact, + inbox: whatsapp_channel.inbox, + source_id: wa_id) + + params = { + 'contacts' => [{ 'profile' => { 'name' => 'Jane Smith' }, 'wa_id' => wa_id }], + 'messages' => [{ 'from' => wa_id, 'id' => 'message123', 'text' => { 'body' => 'Hello' }, + 'timestamp' => '1633034394', 'type' => 'text' }] + }.with_indifferent_access + + described_class.new(inbox: whatsapp_channel.inbox, params: params).perform + existing_contact.reload + expect(existing_contact.name).to eq('John Doe') # Should not change + end + + it 'updates contact name when current name matches formatted phone number' do + formatted_number = TelephoneNumber.parse(phone_number).international_number + + # Create contact with formatted phone number as name + existing_contact = create(:contact, + account: whatsapp_channel.inbox.account, + name: formatted_number, + phone_number: phone_number) + create(:contact_inbox, + contact: existing_contact, + inbox: whatsapp_channel.inbox, + source_id: wa_id) + + params = { + 'contacts' => [{ 'profile' => { 'name' => 'Alice Johnson' }, 'wa_id' => wa_id }], + 'messages' => [{ 'from' => wa_id, 'id' => 'message123', 'text' => { 'body' => 'Hello' }, + 'timestamp' => '1633034394', 'type' => 'text' }] + }.with_indifferent_access + + described_class.new(inbox: whatsapp_channel.inbox, params: params).perform + existing_contact.reload + expect(existing_contact.name).to eq('Alice Johnson') + end + + it 'does not update when profile name is blank' do + # Create contact with phone number as name + existing_contact = create(:contact, + account: whatsapp_channel.inbox.account, + name: phone_number, + phone_number: phone_number) + create(:contact_inbox, + contact: existing_contact, + inbox: whatsapp_channel.inbox, + source_id: wa_id) + + params = { + 'contacts' => [{ 'profile' => { 'name' => '' }, 'wa_id' => wa_id }], + 'messages' => [{ 'from' => wa_id, 'id' => 'message123', 'text' => { 'body' => 'Hello' }, + 'timestamp' => '1633034394', 'type' => 'text' }] + }.with_indifferent_access + + described_class.new(inbox: whatsapp_channel.inbox, params: params).perform + existing_contact.reload + expect(existing_contact.name).to eq(phone_number) # Should not change + end + end end end diff --git a/spec/services/whatsapp/oneoff_campaign_service_spec.rb b/spec/services/whatsapp/oneoff_campaign_service_spec.rb index 599081e23..dd8d51c54 100644 --- a/spec/services/whatsapp/oneoff_campaign_service_spec.rb +++ b/spec/services/whatsapp/oneoff_campaign_service_spec.rb @@ -133,7 +133,8 @@ describe Whatsapp::OneoffCampaignService do ) ) ) - ) + ), + nil ) described_class.new(campaign: campaign).perform @@ -164,8 +165,8 @@ describe Whatsapp::OneoffCampaignService do allow(whatsapp_channel).to receive(:send_template).and_return(nil) - expect(whatsapp_channel).to receive(:send_template).with(contact_error.phone_number, anything).and_raise(StandardError, error_message) - expect(whatsapp_channel).to receive(:send_template).with(contact_success.phone_number, anything).once + expect(whatsapp_channel).to receive(:send_template).with(contact_error.phone_number, anything, nil).and_raise(StandardError, error_message) + expect(whatsapp_channel).to receive(:send_template).with(contact_success.phone_number, anything, nil).once expect(Rails.logger).to receive(:error) .with("Failed to send WhatsApp template message to #{contact_error.phone_number}: #{error_message}") diff --git a/spec/services/whatsapp/providers/whatsapp_cloud_service_spec.rb b/spec/services/whatsapp/providers/whatsapp_cloud_service_spec.rb index 8735ccfbb..69ba69379 100644 --- a/spec/services/whatsapp/providers/whatsapp_cloud_service_spec.rb +++ b/spec/services/whatsapp/providers/whatsapp_cloud_service_spec.rb @@ -187,7 +187,7 @@ describe Whatsapp::Providers::WhatsappCloudService do ) .to_return(status: 200, body: whatsapp_response.to_json, headers: response_headers) - expect(service.send_template('+123456789', template_info)).to eq('message_id') + expect(service.send_template('+123456789', template_info, message)).to eq('message_id') end end end @@ -287,7 +287,7 @@ describe Whatsapp::Providers::WhatsappCloudService do context 'when there is a message' do it 'logs error and updates message status' do service.instance_variable_set(:@message, message) - service.send(:handle_error, error_response_object) + service.send(:handle_error, error_response_object, message) expect(message.reload.status).to eq('failed') expect(message.reload.external_error).to eq(error_message) @@ -305,7 +305,7 @@ describe Whatsapp::Providers::WhatsappCloudService do it 'logs error but does not update message' do service.instance_variable_set(:@message, message) - service.send(:handle_error, error_response_object) + service.send(:handle_error, error_response_object, message) expect(message.reload.status).not_to eq('failed') expect(message.reload.external_error).to be_nil