feat: Add support for shared post and story attachment types in Instagram messages (#12997)
When users share Instagram posts or stories via DM, Instagram sends webhooks with type `ig_post` and `ig_story` attachments. The system was failing on these types because they weren't defined in the file_types. This PR fixes the issue by handling all shared types and rendering them on the front end. **Shared post** <img width="2154" height="1828" alt="CleanShot 2025-12-03 at 16 29 14@2x" src="https://github.com/user-attachments/assets/7e731171-4904-43a6-abeb-b1db2c262742" /> **Shared status** <img width="1702" height="1676" alt="CleanShot 2025-12-03 at 16 10 25@2x" src="https://github.com/user-attachments/assets/6a151233-ce47-429d-b7c2-061514b20e05" /> Fixes https://linear.app/chatwoot/issue/CW-5441/argumenterror-ig-story-is-not-a-valid-file-type-argumenterror
This commit is contained in:
@@ -348,6 +348,85 @@ FactoryBot.define do
|
||||
initialize_with { attributes }
|
||||
end
|
||||
|
||||
factory :instagram_ig_story_event, class: Hash do
|
||||
transient do
|
||||
ig_entry_id { SecureRandom.uuid }
|
||||
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
|
||||
end
|
||||
entry do
|
||||
[
|
||||
{
|
||||
'id': ig_entry_id,
|
||||
'time': '2021-09-08T06:34:04+0000',
|
||||
'messaging': [
|
||||
{
|
||||
'sender': {
|
||||
'id': sender_id
|
||||
},
|
||||
'recipient': {
|
||||
'id': 'chatwoot-app-user-id-1'
|
||||
},
|
||||
'timestamp': '2021-09-08T06:34:04+0000',
|
||||
'message': {
|
||||
'mid': 'ig-story-message-id-1',
|
||||
'attachments': [
|
||||
{
|
||||
'type': 'ig_story',
|
||||
'payload': {
|
||||
'story_media_id': '17949487764033669',
|
||||
'story_media_url': 'https://lookaside.fbsbx.com/ig_messaging_cdn/?asset_id=17949487764033669&signature=test'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
end
|
||||
initialize_with { attributes }
|
||||
end
|
||||
|
||||
factory :instagram_ig_post_event, class: Hash do
|
||||
transient do
|
||||
ig_entry_id { SecureRandom.uuid }
|
||||
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
|
||||
end
|
||||
entry do
|
||||
[
|
||||
{
|
||||
'id': ig_entry_id,
|
||||
'time': '2021-09-08T06:34:04+0000',
|
||||
'messaging': [
|
||||
{
|
||||
'sender': {
|
||||
'id': sender_id
|
||||
},
|
||||
'recipient': {
|
||||
'id': 'chatwoot-app-user-id-1'
|
||||
},
|
||||
'timestamp': '2021-09-08T06:34:04+0000',
|
||||
'message': {
|
||||
'mid': 'ig-post-message-id-1',
|
||||
'attachments': [
|
||||
{
|
||||
'type': 'ig_post',
|
||||
'payload': {
|
||||
'ig_post_media_id': '18091626484740369',
|
||||
'title': 'Shared Instagram post',
|
||||
'url': 'https://lookaside.fbsbx.com/ig_messaging_cdn/?asset_id=18091626484740369&signature=test'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
end
|
||||
initialize_with { attributes }
|
||||
end
|
||||
|
||||
factory :instagram_message_unsupported_event, class: Hash do
|
||||
transient do
|
||||
ig_entry_id { SecureRandom.uuid }
|
||||
|
||||
@@ -7,6 +7,10 @@ describe Webhooks::InstagramEventsJob do
|
||||
stub_request(:post, /graph\.facebook\.com/)
|
||||
stub_request(:get, 'https://www.example.com/test.jpeg')
|
||||
.to_return(status: 200, body: '', headers: {})
|
||||
stub_request(:get, 'https://lookaside.fbsbx.com/ig_messaging_cdn/?asset_id=17949487764033669&signature=test')
|
||||
.to_return(status: 200, body: '', headers: {})
|
||||
stub_request(:get, 'https://lookaside.fbsbx.com/ig_messaging_cdn/?asset_id=18091626484740369&signature=test')
|
||||
.to_return(status: 200, body: '', headers: {})
|
||||
end
|
||||
|
||||
let!(:account) { create(:account) }
|
||||
@@ -131,6 +135,52 @@ describe Webhooks::InstagramEventsJob do
|
||||
expect(attachment.push_event_data[:data_url]).to eq(attachment.external_url)
|
||||
end
|
||||
|
||||
it 'creates incoming message with ig_story attachment in the instagram inbox' do
|
||||
ig_story_event = build(:instagram_ig_story_event).with_indifferent_access
|
||||
sender_id = ig_story_event[:entry][0][:messaging][0][:sender][:id]
|
||||
|
||||
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
|
||||
allow(fb_object).to receive(:get_object).and_return(
|
||||
return_object_for(sender_id).with_indifferent_access
|
||||
)
|
||||
|
||||
instagram_webhook.perform_now(ig_story_event[:entry])
|
||||
|
||||
expect(instagram_messenger_inbox.messages.count).to be 1
|
||||
expect(instagram_messenger_inbox.messages.last.attachments.count).to be 1
|
||||
|
||||
message = instagram_messenger_inbox.messages.last
|
||||
attachment = message.attachments.last
|
||||
|
||||
expect(attachment.file_type).to eq 'ig_story'
|
||||
expect(attachment.external_url).to include 'lookaside.fbsbx.com'
|
||||
expect(message.content).to eq 'Shared story'
|
||||
expect(message.content_attributes['image_type']).to eq 'ig_story'
|
||||
end
|
||||
|
||||
it 'creates incoming message with ig_post attachment in the instagram inbox' do
|
||||
ig_post_event = build(:instagram_ig_post_event).with_indifferent_access
|
||||
sender_id = ig_post_event[:entry][0][:messaging][0][:sender][:id]
|
||||
|
||||
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
|
||||
allow(fb_object).to receive(:get_object).and_return(
|
||||
return_object_for(sender_id).with_indifferent_access
|
||||
)
|
||||
|
||||
instagram_webhook.perform_now(ig_post_event[:entry])
|
||||
|
||||
expect(instagram_messenger_inbox.messages.count).to be 1
|
||||
expect(instagram_messenger_inbox.messages.last.attachments.count).to be 1
|
||||
|
||||
message = instagram_messenger_inbox.messages.last
|
||||
attachment = message.attachments.last
|
||||
|
||||
expect(attachment.file_type).to eq 'ig_post'
|
||||
expect(attachment.external_url).to include 'ig_messaging_cdn'
|
||||
expect(message.content).to eq 'Shared post'
|
||||
expect(message.content_attributes['image_type']).to eq 'ig_post'
|
||||
end
|
||||
|
||||
it 'does not create contact or messages when Facebook API call fails' do
|
||||
story_mention_echo_event = build(:instagram_story_mention_event_with_echo).with_indifferent_access
|
||||
|
||||
@@ -262,6 +312,38 @@ describe Webhooks::InstagramEventsJob do
|
||||
expect(instagram_inbox.messages.last.content_attributes['is_unsupported']).to be true
|
||||
end
|
||||
|
||||
it 'creates incoming message with ig_story attachment in the instagram direct inbox' do
|
||||
ig_story_event = build(:instagram_ig_story_event).with_indifferent_access
|
||||
instagram_webhook.perform_now(ig_story_event[:entry])
|
||||
|
||||
expect(instagram_inbox.messages.count).to be 1
|
||||
expect(instagram_inbox.messages.last.attachments.count).to be 1
|
||||
|
||||
message = instagram_inbox.messages.last
|
||||
attachment = message.attachments.last
|
||||
|
||||
expect(attachment.file_type).to eq 'ig_story'
|
||||
expect(attachment.external_url).to include 'lookaside.fbsbx.com'
|
||||
expect(message.content).to eq 'Shared story'
|
||||
expect(message.content_attributes['image_type']).to eq 'ig_story'
|
||||
end
|
||||
|
||||
it 'creates incoming message with ig_post attachment in the instagram direct inbox' do
|
||||
ig_post_event = build(:instagram_ig_post_event).with_indifferent_access
|
||||
instagram_webhook.perform_now(ig_post_event[:entry])
|
||||
|
||||
expect(instagram_inbox.messages.count).to be 1
|
||||
expect(instagram_inbox.messages.last.attachments.count).to be 1
|
||||
|
||||
message = instagram_inbox.messages.last
|
||||
attachment = message.attachments.last
|
||||
|
||||
expect(attachment.file_type).to eq 'ig_post'
|
||||
expect(attachment.external_url).to include 'ig_messaging_cdn'
|
||||
expect(message.content).to eq 'Shared post'
|
||||
expect(message.content_attributes['image_type']).to eq 'ig_post'
|
||||
end
|
||||
|
||||
it 'does not create contact or messages when Instagram API call fails' do
|
||||
story_mention_echo_event = build(:instagram_story_mention_event_with_echo).with_indifferent_access
|
||||
|
||||
|
||||
Reference in New Issue
Block a user