From 85653416822e2c0802572576047610d60e788f0a Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Thu, 22 May 2025 11:02:28 +0530 Subject: [PATCH] fix: Prevent creating duplicate messages via Instagram echo events (#11535) Fixes https://linear.app/chatwoot/issue/CW-4383/copilot-resolution-message-creating-duplicate-conversations-due-to and https://linear.app/chatwoot/issue/CW-4287/instagram-channel-missing-existing-source-id-check When the copilot/system resolves a conversation with a system resolve message (with `lock_to_single_conversation` disabled), echo events were creating new conversations instead of using existing ones. This occurred because we were checking the echo_id in the new conversation rather than in the resolved conversation. This PR fixes the issue by checking if the message exists anywhere in the system instead of checking within a particular conversation. --- .../messages/instagram/base_message_builder.rb | 10 ++++++---- .../messages/instagram/message_builder_spec.rb | 16 ++++++++++++++++ .../instagram/messenger/message_builder_spec.rb | 9 ++++++--- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/app/builders/messages/instagram/base_message_builder.rb b/app/builders/messages/instagram/base_message_builder.rb index 767115bc6..8b40ba3c9 100644 --- a/app/builders/messages/instagram/base_message_builder.rb +++ b/app/builders/messages/instagram/base_message_builder.rb @@ -152,11 +152,13 @@ class Messages::Instagram::BaseMessageBuilder < Messages::Messenger::MessageBuil end def message_already_exists? - cw_message = conversation.messages.where( - source_id: @messaging[:message][:mid] - ).first + find_message_by_source_id(@messaging[:message][:mid]).present? + end - cw_message.present? + def find_message_by_source_id(source_id) + return unless source_id + + @message = Message.find_by(source_id: source_id) end def all_unsupported_files? diff --git a/spec/builders/messages/instagram/message_builder_spec.rb b/spec/builders/messages/instagram/message_builder_spec.rb index 0dbe87afa..8e863823a 100644 --- a/spec/builders/messages/instagram/message_builder_spec.rb +++ b/spec/builders/messages/instagram/message_builder_spec.rb @@ -79,6 +79,18 @@ describe Messages::Instagram::MessageBuilder do expect(instagram_inbox.messages.count).to be 1 end + it 'discards duplicate messages from webhook events with the same message_id' do + messaging = dm_params[:entry][0]['messaging'][0] + described_class.new(messaging, instagram_inbox).perform + + initial_message_count = instagram_inbox.messages.count + expect(initial_message_count).to be 1 + + described_class.new(messaging, instagram_inbox).perform + + expect(instagram_inbox.messages.count).to eq initial_message_count + end + it 'creates message for shared reel' do messaging = shared_reel_params[:entry][0]['messaging'][0] described_class.new(messaging, instagram_inbox).perform @@ -151,11 +163,15 @@ describe Messages::Instagram::MessageBuilder do end it 'does not create message for unsupported file type' do + conversation + + # try to create a message with unsupported file type story_mention_params[:entry][0][:messaging][0]['message']['attachments'][0]['type'] = 'unsupported_type' messaging = story_mention_params[:entry][0][:messaging][0] described_class.new(messaging, instagram_inbox, outgoing_echo: false).perform + # Conversation should exist but no new message should be created expect(instagram_inbox.conversations.count).to be 1 expect(instagram_inbox.messages.count).to be 0 end diff --git a/spec/builders/messages/instagram/messenger/message_builder_spec.rb b/spec/builders/messages/instagram/messenger/message_builder_spec.rb index 78c97b071..03194673a 100644 --- a/spec/builders/messages/instagram/messenger/message_builder_spec.rb +++ b/spec/builders/messages/instagram/messenger/message_builder_spec.rb @@ -189,19 +189,22 @@ describe Messages::Instagram::Messenger::MessageBuilder do profile_pic: 'https://chatwoot-assets.local/sample.png' }.with_indifferent_access ) + + conversation + + # create a message with unsupported file type story_mention_params[:entry][0][:messaging][0]['message']['attachments'][0]['type'] = 'unsupported_type' messaging = story_mention_params[:entry][0][:messaging][0] - contact_inbox + described_class.new(messaging, instagram_messenger_inbox, outgoing_echo: false).perform instagram_messenger_inbox.reload - # we would have contact created but message and attachments won't be created + # Conversation should exist but no new message should be created expect(instagram_messenger_inbox.conversations.count).to be 1 expect(instagram_messenger_inbox.messages.count).to be 0 contact = instagram_messenger_channel.inbox.contacts.first - expect(contact.name).to eq('Jane Dae') end end