fix: Attach instagram images with file type story_mentions (#4100)

This commit is contained in:
Tejaswini Chile 2022-03-10 20:27:30 +05:30 committed by GitHub
parent b3545f42f1
commit 647efa12e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 219 additions and 18 deletions

View file

@ -1,10 +1,14 @@
class Messages::Messenger::MessageBuilder
include ::FileTypeHelper
def process_attachment(attachment)
return if attachment['type'].to_sym == :template
attachment_obj = @message.attachments.new(attachment_params(attachment).except(:remote_file_url))
attachment_obj.save!
attach_file(attachment_obj, attachment_params(attachment)[:remote_file_url]) if attachment_params(attachment)[:remote_file_url]
fetch_story_link(attachment_obj) if attachment_obj.file_type == 'story_mention'
update_attachment_file_type(attachment_obj)
end
def attach_file(attachment, file_url)
@ -22,7 +26,7 @@ class Messages::Messenger::MessageBuilder
file_type = attachment['type'].to_sym
params = { file_type: file_type, account_id: @message.account_id }
if [:image, :file, :audio, :video].include? file_type
if [:image, :file, :audio, :video, :share, :story_mention].include? file_type
params.merge!(file_type_params(attachment))
elsif file_type == :location
params.merge!(location_params(attachment))
@ -39,4 +43,31 @@ class Messages::Messenger::MessageBuilder
remote_file_url: attachment['payload']['url']
}
end
def update_attachment_file_type(attachment)
return unless attachment.file_type == 'share' || attachment.file_type == 'story_mention'
attachment.file_type = file_type(attachment.file&.content_type)
attachment.save!
end
def fetch_story_link(attachment)
message = attachment.message
begin
k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook?
result = k.get_object(message.source_id, fields: %w[story from]) || {}
rescue Koala::Facebook::AuthenticationError
@inbox.channel.authorization_error!
raise
rescue StandardError => e
result = {}
Sentry.capture_exception(e)
end
story_id = result['story']['mention']['id']
story_sender = result['from']['username']
message.content_attributes[:story_sender] = story_sender
message.content_attributes[:story_id] = story_id
message.content = I18n.t('conversations.messages.instagram_story_content', story_sender: story_sender)
message.save!
end
end

View file

@ -14,7 +14,8 @@ module FileTypeHelper
'image/png',
'image/gif',
'image/bmp',
'image/webp'
'image/webp',
'image'
].include?(content_type)
end
@ -23,7 +24,8 @@ module FileTypeHelper
'video/ogg',
'video/mp4',
'video/webm',
'video/quicktime'
'video/quicktime',
'video'
].include?(content_type)
end
end

View file

@ -50,7 +50,10 @@
<bubble-actions
:id="data.id"
:sender="data.sender"
:story-sender="storySender"
:story-id="storyId"
:is-a-tweet="isATweet"
:has-instagram-story="hasInstagramStory"
:is-email="isEmailContentType"
:is-private="data.private"
:message-type="data.message_type"
@ -146,6 +149,10 @@ export default {
type: Boolean,
default: false,
},
hasInstagramStory: {
type: Boolean,
default: false,
},
},
data() {
return {
@ -209,6 +216,12 @@ export default {
sender() {
return this.data.sender || {};
},
storySender() {
return this.contentAttributes.story_sender || {};
},
storyId() {
return this.contentAttributes.story_id || {};
},
contentType() {
const {
data: { content_type: contentType },

View file

@ -47,6 +47,7 @@
class="message--read"
:data="message"
:is-a-tweet="isATweet"
:has-instagram-story="hasInstagramStory"
/>
<li v-show="getUnreadCount != 0" class="unread--toast">
<span class="text-uppercase">
@ -64,6 +65,7 @@
class="message--unread"
:data="message"
:is-a-tweet="isATweet"
:has-instagram-story="hasInstagramStory"
/>
</ul>
<div
@ -215,6 +217,10 @@ export default {
return this.conversationType === 'tweet';
},
hasInstagramStory() {
return this.conversationType === 'instagram_direct_message';
},
selectedTweet() {
if (this.selectedTweetId) {
const { messages = [] } = this.getMessages;

View file

@ -35,6 +35,19 @@
size="16"
/>
</button>
<a
v-if="hasInstagramStory && (isIncoming || isOutgoing) && linkToStory"
:href="linkToStory"
target="_blank"
rel="noopener noreferrer nofollow"
>
<fluent-icon
v-tooltip.top-start="$t('CHAT_LIST.LINK_TO_STORY')"
icon="open"
class="action--icon cursor-pointer"
size="16"
/>
</a>
<a
v-if="isATweet && (isOutgoing || isIncoming) && linkToTweet"
:href="linkToTweet"
@ -67,6 +80,14 @@ export default {
type: String,
default: '',
},
storySender: {
type: String,
default: '',
},
storyId: {
type: String,
default: '',
},
isEmail: {
type: Boolean,
default: true,
@ -79,6 +100,10 @@ export default {
type: Boolean,
default: true,
},
hasInstagramStory: {
type: Boolean,
default: true,
},
messageType: {
type: Number,
default: 1,
@ -119,6 +144,13 @@ export default {
return `https://twitter.com/${screenName ||
this.inbox.name}/status/${sourceId}`;
},
linkToStory() {
if (!this.storyId || !this.storySender) {
return '';
}
const { storySender, storyId } = this;
return `https://www.instagram.com/stories/${storySender}/${storyId}`;
},
showSentIndicator() {
return this.isOutgoing && this.sourceId && this.isAnEmailChannel;
},

View file

@ -76,6 +76,7 @@
"RECEIVED_VIA_EMAIL": "Received via email",
"VIEW_TWEET_IN_TWITTER": "View tweet in Twitter",
"REPLY_TO_TWEET": "Reply to this tweet",
"LINK_TO_STORY": "Go to instagram story",
"SENT": "Sent successfully",
"NO_MESSAGES": "No Messages",
"NO_CONTENT": "No content available",

View file

@ -34,7 +34,7 @@ class Attachment < ApplicationRecord
has_one_attached :file
validate :acceptable_file
enum file_type: [:image, :audio, :video, :file, :location, :fallback]
enum file_type: [:image, :audio, :video, :file, :location, :fallback, :share, :story_mention]
def push_event_data
return unless file_type

View file

@ -64,7 +64,7 @@ class Message < ApplicationRecord
# [:deleted] : Used to denote whether the message was deleted by the agent
# [:external_created_at] : Can specify if the message was created at a different timestamp externally
store :content_attributes, accessors: [:submitted_email, :items, :submitted_values, :email, :in_reply_to, :deleted,
:external_created_at], coder: JSON
:external_created_at, :story_sender, :story_id], coder: JSON
store :external_source_ids, accessors: [:slack], coder: JSON, prefix: :external_source_id

View file

@ -43,7 +43,7 @@ en:
failed: Signup failed
contacts:
import:
failed: File is blank
failed: File is blank
reports:
period: Reporting period %{since} to %{until}
@ -67,6 +67,7 @@ en:
conversation_mention: "You have been mentioned in conversation [ID - %{display_id}] by %{name}"
conversations:
messages:
instagram_story_content: "%{story_sender} mentioned you in the story: "
deleted: This message was deleted
activity:
status:

View file

@ -55,4 +55,72 @@ FactoryBot.define do
end
initialize_with { attributes }
end
factory :instagram_message_attachment_event, class: Hash do
entry do
[
{
'id': 'instagram-message-id-1234',
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': 'Sender-id-1'
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-1',
'attachments': [
{
'type': 'share',
'payload': {
'url': 'https://imagekit.io/blog/content/images/2020/05/media_library.jpeg'
}
}
]
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_story_mention_event, class: Hash do
entry do
[
{
'id': 'instagram-message-id-1234',
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': 'Sender-id-1'
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-1',
'attachments': [
{
'type': 'story_mention',
'payload': {
'url': 'https://imagekit.io/blog/content/images/2020/05/media_library.jpeg'
}
}
]
}
}
]
}
]
end
initialize_with { attributes }
end
end

View file

@ -6,13 +6,30 @@ describe Webhooks::InstagramEventsJob do
before do
stub_request(:post, /graph.facebook.com/)
stub_request(:get, 'https://imagekit.io/blog/content/images/2020/05/media_library.jpeg')
.with(
headers: {
'Accept' => '*/*',
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'User-Agent' => 'Down/5.3.0'
}
)
.to_return(status: 200, body: '', headers: {})
end
let!(:account) { create(:account) }
let(:return_onject) do
{ name: 'Jane',
id: 'Sender-id-1',
account_id: instagram_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png' }
end
let!(:instagram_channel) { create(:channel_instagram_fb_page, account: account, instagram_id: 'chatwoot-app-user-id-1') }
let!(:instagram_inbox) { create(:inbox, channel: instagram_channel, account: account, greeting_enabled: false) }
let!(:dm_params) { build(:instagram_message_create_event).with_indifferent_access }
let!(:test_params) { build(:instagram_test_text_event).with_indifferent_access }
let!(:attachment_params) { build(:instagram_message_attachment_event).with_indifferent_access }
let!(:story_mention_params) { build(:instagram_story_mention_event).with_indifferent_access }
let(:fb_object) { double }
describe '#perform' do
@ -20,12 +37,7 @@ describe Webhooks::InstagramEventsJob do
it 'creates incoming message in the instagram inbox' do
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_return(
{
name: 'Jane',
id: 'Sender-id-1',
account_id: instagram_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access
return_onject.with_indifferent_access
)
instagram_webhook.perform_now(dm_params[:entry])
@ -39,12 +51,7 @@ describe Webhooks::InstagramEventsJob do
it 'creates test text message in the instagram inbox' do
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_return(
{
name: 'Jane',
id: 'Sender-id-1',
account_id: instagram_inbox.account_id,
profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access
return_onject.with_indifferent_access
)
instagram_webhook.perform_now(test_params[:entry])
@ -53,6 +60,46 @@ describe Webhooks::InstagramEventsJob do
expect(instagram_inbox.messages.count).to be 1
expect(instagram_inbox.messages.last.content).to eq('This is a test message from facebook.')
end
it 'creates incoming message with attachments in the instagram inbox' do
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_return(
return_onject.with_indifferent_access
)
instagram_webhook.perform_now(attachment_params[:entry])
instagram_inbox.reload
expect(instagram_inbox.contacts.count).to be 1
expect(instagram_inbox.messages.count).to be 1
expect(instagram_inbox.messages.last.attachments.count).to be 1
end
it 'creates incoming message with attachments in the instagram inbox for story mention' do
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
allow(fb_object).to receive(:get_object).and_return(
return_onject.with_indifferent_access,
{ story:
{
mention: {
link:
'https://lookaside.fbsbx.com/ig_messaging_cdn/?asset_id=17920786367196703&signature=Aby8EXbvNu4on9efDQecXDasiJX2s0FgWhFGz3mNFB__CsHR22O_1bJiYHkbp3mC1NQeW4jHxls9WyqVgRPcyonUbSJmD44UwLfFhbCK2obesWnFi7VOnisqLu48Xd6KYuNex7uSCQKWM-nw55zQ23bBgfCYw6h5hiJjFHwJDZYm65zXpQ',
id: '17920786367196703'
}
},
from: {
username: 'Sender-id-1', id: 'Sender-id-1'
},
id: 'instagram-message-id-1234' }.with_indifferent_access
)
instagram_webhook.perform_now(story_mention_params[:entry])
instagram_inbox.reload
expect(instagram_inbox.messages.count).to be 1
expect(instagram_inbox.messages.last.attachments.count).to be 1
end
end
end
end