fix(whatsapp): update conversation lookup to handle multiple contact inboxes in single conversation mode (#197)

* fix(whatsapp): update conversation lookup to handle multiple contact inboxes in single conversation mode

* fix(whatsapp): update conversation retrieval logic to ensure correct conversation is reopened
This commit is contained in:
Gabriel Jablonski 2026-01-25 19:51:56 -03:00 committed by GitHub
parent b5d3250a2a
commit b1aaf58097
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 80 additions and 7 deletions

View File

@ -10,7 +10,7 @@ class ConversationBuilder
def look_up_exising_conversation
return unless @contact_inbox.inbox.lock_to_single_conversation?
@contact_inbox.conversations.last
@contact_inbox.inbox.conversations.where(contact_id: @contact_inbox.contact_id).last
end
def create_new_conversation

View File

@ -114,7 +114,7 @@ class Whatsapp::IncomingMessageBaseService
def set_conversation
# if lock to single conversation is disabled, we will create a new conversation if previous conversation is resolved
@conversation = if @inbox.lock_to_single_conversation
@contact_inbox.conversations.last
@inbox.conversations.where(contact_id: @contact_inbox.contact_id).last
else
@contact_inbox.conversations
.where.not(status: :resolved).last

View File

@ -44,8 +44,8 @@ describe ConversationBuilder do
end
it 'returns last from existing sms conversations when existing conversation is not present' do
create(:conversation, contact_inbox: contact_sms_inbox)
existing_conversation = create(:conversation, contact_inbox: contact_sms_inbox)
create(:conversation, contact_inbox: contact_sms_inbox, contact: contact, inbox: sms_inbox)
existing_conversation = create(:conversation, contact_inbox: contact_sms_inbox, contact: contact, inbox: sms_inbox)
conversation = described_class.new(
contact_inbox: contact_sms_inbox,
params: {}
@ -70,8 +70,8 @@ describe ConversationBuilder do
end
it 'returns last from existing api conversations when existing conversation is not present' do
create(:conversation, contact_inbox: contact_api_inbox)
existing_conversation = create(:conversation, contact_inbox: contact_api_inbox)
create(:conversation, contact_inbox: contact_api_inbox, contact: contact, inbox: api_inbox)
existing_conversation = create(:conversation, contact_inbox: contact_api_inbox, contact: contact, inbox: api_inbox)
conversation = described_class.new(
contact_inbox: contact_api_inbox,
params: {}
@ -80,5 +80,23 @@ describe ConversationBuilder do
expect(conversation.id).to eq(existing_conversation.id)
end
end
context 'when lock_to_single_conversation is true for whatsapp inbox with multiple contact_inboxes' do
let!(:whatsapp_channel) { create(:channel_whatsapp, account: account, sync_templates: false, validate_provider_config: false) }
let!(:whatsapp_inbox) { whatsapp_channel.inbox }
let(:contact_with_phone) { create(:contact, account: account, phone_number: '+5511912345678') }
before { whatsapp_inbox.update!(lock_to_single_conversation: true) }
it 'finds conversation from different contact_inbox with same contact' do
lid_contact_inbox = create(:contact_inbox, contact: contact_with_phone, inbox: whatsapp_inbox, source_id: '12345678')
existing_conversation = create(:conversation, contact_inbox: lid_contact_inbox, inbox: whatsapp_inbox, contact: contact_with_phone)
phone_contact_inbox = create(:contact_inbox, contact: contact_with_phone, inbox: whatsapp_inbox, source_id: '5511912345678')
conversation = described_class.new(contact_inbox: phone_contact_inbox, params: {}).perform
expect(conversation.id).to eq(existing_conversation.id)
end
end
end
end

View File

@ -44,7 +44,7 @@ describe Whatsapp::IncomingMessageService do
it 'reopen last conversation if last conversation is resolved and lock to single conversation is enabled' do
whatsapp_channel.inbox.update!(lock_to_single_conversation: true)
contact_inbox = create(:contact_inbox, inbox: whatsapp_channel.inbox, source_id: params[:messages].first[:from])
last_conversation = create(:conversation, inbox: whatsapp_channel.inbox, contact_inbox: contact_inbox)
last_conversation = create(:conversation, inbox: whatsapp_channel.inbox, contact_inbox: contact_inbox, contact: contact_inbox.contact)
last_conversation.update!(status: 'resolved')
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
# no new conversation should be created

View File

@ -541,6 +541,61 @@ describe Whatsapp::ZapiHandlers::ReceivedCallback do
it_behaves_like 'routes messages to the new conversation', first_msg_id: 'msg_003', second_msg_id: 'msg_004'
end
end
describe 'single conversation mode' do
let(:phone) { '5511912345678' }
let(:lid) { '12345678' }
before { inbox.update!(lock_to_single_conversation: true) }
def build_params(message_id:, text:)
{
type: 'ReceivedCallback',
messageId: message_id,
momment: Time.current.to_i * 1000,
fromMe: false,
phone: phone,
chatLid: "#{lid}@lid",
chatName: 'John Doe',
text: { message: text }
}
end
it 'reopens resolved conversation when contact sends new message' do
Whatsapp::IncomingMessageZapiService.new(inbox: inbox, params: build_params(message_id: 'msg_1', text: 'First')).perform
conversation = Conversation.last
conversation.update!(status: :resolved)
expect { Whatsapp::IncomingMessageZapiService.new(inbox: inbox, params: build_params(message_id: 'msg_2', text: 'Second')).perform }
.not_to change(Conversation, :count)
expect(conversation.reload.status).to eq('open')
end
it 'migrates phone source_id to LID and routes to existing conversation' do
contact = create(:contact, account: inbox.account, phone_number: "+#{phone}")
contact_inbox = create(:contact_inbox, inbox: inbox, contact: contact, source_id: phone)
conversation = create(:conversation, inbox: inbox, contact: contact, contact_inbox: contact_inbox)
Whatsapp::IncomingMessageZapiService.new(inbox: inbox, params: build_params(message_id: 'msg_3', text: 'Response')).perform
expect(contact_inbox.reload.source_id).to eq(lid)
expect(conversation.messages.count).to eq(1)
end
it 'finds conversation via ConversationBuilder when agent creates new contact_inbox with phone' do
Whatsapp::IncomingMessageZapiService.new(inbox: inbox, params: build_params(message_id: 'msg_4', text: 'Hello')).perform
first_conversation = Conversation.last
first_conversation.update!(status: :resolved)
contact = Contact.last
phone_contact_inbox = ContactInboxBuilder.new(contact: contact, inbox: inbox, source_id: nil).perform
conversation = ConversationBuilder.new(params: ActionController::Parameters.new({}), contact_inbox: phone_contact_inbox).perform
expect(conversation.id).to eq(first_conversation.id)
end
end
end
describe '#process_received_callback' do