From 441fe4db1147c4392e0e026c6a97c3d4d5de75ef Mon Sep 17 00:00:00 2001 From: Pranav Date: Thu, 2 Apr 2026 07:26:23 -0700 Subject: [PATCH] fix: scope external_url override to Instagram DM conversations only (#13982) Previously, all incoming messages from Facebook channel with instagram_id had their attachment data_url and thumb_url overridden with external_url. This caused issues for non-Instagram conversations originating from Facebook Message where the file URL should be used instead. Narrows the override to only apply when the conversation type is instagram_direct_message, which is the only case where Instagram's CDN URLs need to be used directly. Fixes https://linear.app/chatwoot/issue/CW-6722/videos-are-missing-in-facebook-conversation --------- Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) --- app/models/attachment.rb | 10 ++++- spec/models/attachment_spec.rb | 82 +++++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/app/models/attachment.rb b/app/models/attachment.rb index c6b4e1d80..769e134be 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -120,7 +120,7 @@ class Attachment < ApplicationRecord height: file.metadata[:height] } - metadata[:data_url] = metadata[:thumb_url] = external_url if message.inbox.instagram? && message.incoming? + metadata[:data_url] = metadata[:thumb_url] = external_url if instagram_incoming_message? metadata end @@ -156,6 +156,14 @@ class Attachment < ApplicationRecord } end + def instagram_incoming_message? + return false unless message.incoming? + + return true if message.inbox.instagram_direct? + + message.inbox.instagram? && message.conversation&.additional_attributes&.dig('type') == 'instagram_direct_message' + end + def set_extension return unless file.attached? return if extension.present? diff --git a/spec/models/attachment_spec.rb b/spec/models/attachment_spec.rb index 5e1dd2107..82fb51bf1 100644 --- a/spec/models/attachment_spec.rb +++ b/spec/models/attachment_spec.rb @@ -57,11 +57,6 @@ RSpec.describe Attachment do }.to_json, headers: {}) end - it 'returns external url as data and thumb urls when message is incoming' do - external_url = instagram_message.attachments.first.external_url - expect(instagram_message.attachments.first.push_event_data[:data_url]).to eq external_url - end - it 'returns original attachment url as data url if the message is outgoing' do message = create(:message, :instagram_story_mention, message_type: :outgoing) expect(message.attachments.first.push_event_data[:data_url]).not_to eq message.attachments.first.external_url @@ -155,6 +150,83 @@ RSpec.describe Attachment do end end + describe 'push_event_data for instagram direct message attachments' do + let(:account) { create(:account) } + let(:instagram_inbox) do + create(:inbox, account: account, + channel: create(:channel_instagram_fb_page, account: account, instagram_id: 'instagram-dm-test')) + end + + context 'when conversation type is instagram_direct_message' do + let(:conversation) do + create(:conversation, account: account, inbox: instagram_inbox, + additional_attributes: { 'type' => 'instagram_direct_message' }) + end + let(:instagram_message) { create(:message, account: account, inbox: instagram_inbox, conversation: conversation, message_type: :incoming) } + + it 'uses external_url for data_url and thumb_url' do + attachment = instagram_message.attachments.new(account_id: account.id, file_type: :image, external_url: 'https://instagram.com/image.jpg') + attachment.file.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png') + attachment.save! + + event_data = attachment.push_event_data + expect(event_data[:data_url]).to eq('https://instagram.com/image.jpg') + expect(event_data[:thumb_url]).to eq('https://instagram.com/image.jpg') + end + end + + context 'when conversation type is not instagram_direct_message' do + let(:conversation) do + create(:conversation, account: account, inbox: instagram_inbox, + additional_attributes: { 'type' => 'other_type' }) + end + let(:instagram_message) { create(:message, account: account, inbox: instagram_inbox, conversation: conversation, message_type: :incoming) } + + it 'uses file_url for data_url instead of external_url' do + attachment = instagram_message.attachments.new(account_id: account.id, file_type: :image, external_url: 'https://instagram.com/image.jpg') + attachment.file.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png') + attachment.save! + + event_data = attachment.push_event_data + expect(event_data[:data_url]).not_to eq('https://instagram.com/image.jpg') + end + end + + context 'when message is outgoing on instagram DM conversation' do + let(:conversation) do + create(:conversation, account: account, inbox: instagram_inbox, + additional_attributes: { 'type' => 'instagram_direct_message' }) + end + let(:outgoing_message) { create(:message, account: account, inbox: instagram_inbox, conversation: conversation, message_type: :outgoing) } + + it 'does not override data_url with external_url' do + attachment = outgoing_message.attachments.new(account_id: account.id, file_type: :image, external_url: 'https://instagram.com/image.jpg') + attachment.file.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png') + attachment.save! + + event_data = attachment.push_event_data + expect(event_data[:data_url]).not_to eq('https://instagram.com/image.jpg') + end + end + + context 'when inbox is Channel::Instagram (direct login)' do + let(:instagram_channel) { create(:channel_instagram, account: account) } + let(:direct_inbox) { instagram_channel.inbox } + let(:conversation) { create(:conversation, account: account, inbox: direct_inbox) } + let(:incoming_message) { create(:message, account: account, inbox: direct_inbox, conversation: conversation, message_type: :incoming) } + + it 'uses external_url for data_url and thumb_url' do + attachment = incoming_message.attachments.new(account_id: account.id, file_type: :image, external_url: 'https://instagram.com/image.jpg') + attachment.file.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png') + attachment.save! + + event_data = attachment.push_event_data + expect(event_data[:data_url]).to eq('https://instagram.com/image.jpg') + expect(event_data[:thumb_url]).to eq('https://instagram.com/image.jpg') + end + end + end + describe 'push_event_data for ig_reel attachments' do it 'returns external_url as data_url when no file is attached' do attachment = message.attachments.create!(