fix(voice): resolve contact via source_id and i18n call message bubble

This commit is contained in:
Tanmay Deep Sharma
2026-04-30 16:51:29 +07:00
parent f32a7b3f77
commit e5a03d08ea
5 changed files with 34 additions and 15 deletions
+3
View File
@@ -244,6 +244,9 @@ en:
whatsapp:
list_button_label: 'Choose an item'
call_permission_request_body: 'We would like to call you regarding your conversation.'
voice_call:
twilio: 'Voice Call'
whatsapp: 'WhatsApp Call'
delivery_status:
error_code: 'Error code: %{error_code}'
activity:
@@ -27,7 +27,7 @@ class Voice::CallMessageBuilder
def create_message!
params = {
content: 'Voice Call',
content: I18n.t("conversations.messages.voice_call.#{call.provider}"),
message_type: call.outgoing? ? 'outgoing' : 'incoming',
content_type: 'voice_call',
content_attributes: { 'data' => build_data_payload }
@@ -19,8 +19,8 @@ class Voice::InboundCallBuilder
return existing if existing
ActiveRecord::Base.transaction do
contact = ensure_contact!
contact_inbox = ensure_contact_inbox!(contact)
contact_inbox = ensure_contact_inbox!
contact = contact_inbox.contact
conversation = resolve_conversation!(contact, contact_inbox)
call = create_call!(contact, conversation)
message = Voice::CallMessageBuilder.new(call).perform!
@@ -43,21 +43,23 @@ class Voice::InboundCallBuilder
.find_by(provider: provider, provider_call_id: call_sid)
end
# Always look up by (inbox, source_id) first — that pair has a UNIQUE index, so
# creating with a colliding source_id under a different contact would raise
# RecordNotUnique. Reuse the existing ContactInbox (and its contact) when found.
def ensure_contact_inbox!
sid = source_id_for_provider
existing = inbox.contact_inboxes.find_by(source_id: sid)
return existing if existing
ContactInbox.create!(contact: ensure_contact!, inbox: inbox, source_id: sid)
end
def ensure_contact!
account.contacts.find_or_create_by!(phone_number: from_number) do |record|
record.name = from_number if record.name.blank?
end
end
def ensure_contact_inbox!(contact)
ContactInbox.find_or_create_by!(
contact_id: contact.id,
inbox_id: inbox.id
) do |record|
record.source_id = source_id_for_provider
end
end
# WhatsApp ContactInbox.source_id must be digits-only (the wa_id); Twilio accepts the +.
def source_id_for_provider
provider == :whatsapp ? from_number.to_s.delete_prefix('+') : from_number
@@ -29,10 +29,10 @@ class Whatsapp::CallPermissionReplyService
{ from_number: message[:from], accepted: accepted }
end
# WhatsApp routing is anchored on contact_inboxes.source_id (the wa_id), not on
# contacts.phone_number — phone numbers can drift via normalization or edits.
def find_contact(from_number)
inbox.contact_inboxes.joins(:contact)
.where(contacts: { phone_number: "+#{from_number}" })
.first&.contact
inbox.contact_inboxes.find_by(source_id: from_number)&.contact
end
# Filter to threads that actually requested permission; multiple open threads otherwise hit the wrong one.
@@ -86,6 +86,20 @@ RSpec.describe Voice::InboundCallBuilder do
end
end
context 'when a ContactInbox already exists for the source_id (different contact)' do
let!(:original_contact) { create(:contact, account: account, phone_number: '+15550009999') }
let!(:original_contact_inbox) do
create(:contact_inbox, contact: original_contact, inbox: inbox, source_id: from_number)
end
it 'reuses the existing ContactInbox instead of raising RecordNotUnique' do
call = perform_builder
expect(call.contact).to eq(original_contact)
expect(call.conversation.contact_inbox).to eq(original_contact_inbox)
end
end
context 'when the inbox has lock_to_single_conversation enabled' do
let!(:contact) { create(:contact, account: account, phone_number: from_number) }
let!(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: inbox, source_id: from_number) }