feat: support reply to for incoming messages on facebook (#8076)

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
Shivam Mishra
2023-10-13 16:33:50 +05:30
committed by GitHub
parent 7b09fa4a03
commit 62d8ec7edb
7 changed files with 83 additions and 3 deletions

View File

@@ -93,6 +93,9 @@ class Messages::Facebook::MessageBuilder < Messages::Messenger::MessageBuilder
message_type: @message_type,
content: response.content,
source_id: response.identifier,
content_attributes: {
in_reply_to_external_id: response.in_reply_to_external_id
},
sender: @outgoing_echo ? nil : @contact_inbox.contact
}
end

View File

@@ -35,8 +35,9 @@
/>
</blockquote>
<bubble-reply-to
v-if="inReplyToMessageId && inboxSupportsReplyTo"
v-if="inReplyToMessageId"
:message="inReplyTo"
:message-type="data.message_type"
/>
<bubble-text
v-if="data.content"

View File

@@ -1,6 +1,11 @@
<template>
<div
class="px-2 py-1.5 -mx-2 rounded-md bg-woot-600 text-woot-50 min-w-[15rem] mb-2"
class="px-2 py-1.5 -mx-2 rounded-sm min-w-[15rem] mb-2"
:class="{
'bg-slate-100 dark:bg-slate-600 dark:text-slate-50':
messageType === MESSAGE_TYPE.INCOMING,
'bg-woot-600 text-woot-50': messageType === MESSAGE_TYPE.OUTGOING,
}"
>
<message-preview
:message="message"
@@ -12,6 +17,7 @@
<script>
import MessagePreview from 'dashboard/components/widgets/conversation/MessagePreview.vue';
import { MESSAGE_TYPE } from 'shared/constants/messages';
export default {
name: 'ReplyTo',
@@ -23,6 +29,13 @@ export default {
type: Object,
required: true,
},
messageType: {
type: Number,
required: true,
},
},
data() {
return { MESSAGE_TYPE };
},
};
</script>

View File

@@ -20,7 +20,6 @@ export const INBOX_FEATURES = {
export const INBOX_FEATURE_MAP = {
[INBOX_FEATURES.REPLY_TO]: [
INBOX_TYPES.WEB,
INBOX_TYPES.FB,
INBOX_TYPES.TWITTER,
INBOX_TYPES.WHATSAPP,
INBOX_TYPES.LINE,

View File

@@ -60,6 +60,7 @@ class Message < ApplicationRecord
before_validation :ensure_content_type
before_save :ensure_processed_message_content
before_save :ensure_in_reply_to
validates :account_id, presence: true
validates :inbox_id, presence: true
@@ -233,6 +234,20 @@ class Message < ApplicationRecord
self.processed_message_content = message_content&.truncate(150_000)
end
# fetch the in_reply_to message and set the external id
def ensure_in_reply_to
in_reply_to = content_attributes[:in_reply_to]
in_reply_to_external_id = content_attributes[:in_reply_to_external_id]
if in_reply_to.present? && in_reply_to_external_id.blank?
message = conversation.messages.find_by(id: in_reply_to)
content_attributes[:in_reply_to_external_id] = message.try(:source_id)
elsif in_reply_to_external_id.present? && in_reply_to.blank?
message = conversation.messages.find_by(source_id: in_reply_to_external_id)
content_attributes[:in_reply_to] = message.try(:id)
end
end
def ensure_content_type
self.content_type ||= Message.content_types[:text]
end

View File

@@ -47,6 +47,10 @@ class Integrations::Facebook::MessageParser
def sent_from_chatwoot_app?
app_id && app_id == GlobalConfigService.load('FB_APP_ID', '').to_i
end
def in_reply_to_external_id
@messaging.dig('message', 'reply_to', 'mid')
end
end
# Sample Response

View File

@@ -415,4 +415,49 @@ RSpec.describe Message do
expect(instagram_message.reload.attachments.count).to eq 1
end
end
describe '#ensure_in_reply_to' do
let(:conversation) { create(:conversation) }
let(:message) { create(:message, conversation: conversation, source_id: 12_345) }
context 'when in_reply_to is present' do
let(:content_attributes) { { in_reply_to: message.id } }
let(:new_message) { build(:message, conversation: conversation, content_attributes: content_attributes) }
it 'sets in_reply_to_external_id based on the source_id of the referenced message' do
new_message.send(:ensure_in_reply_to)
expect(new_message.content_attributes[:in_reply_to_external_id]).to eq(message.source_id)
end
end
context 'when in_reply_to is not present' do
let(:content_attributes) { { in_reply_to_external_id: message.source_id } }
let(:new_message) { build(:message, conversation: conversation, content_attributes: content_attributes) }
it 'sets in_reply_to based on the source_id of the referenced message' do
new_message.send(:ensure_in_reply_to)
expect(new_message.content_attributes[:in_reply_to]).to eq(message.id)
end
end
context 'when the referenced message is not found' do
let(:content_attributes) { { in_reply_to: message.id + 1 } }
let(:new_message) { build(:message, conversation: conversation, content_attributes: content_attributes) }
it 'does not set in_reply_to_external_id' do
new_message.send(:ensure_in_reply_to)
expect(new_message.content_attributes[:in_reply_to_external_id]).to be_nil
end
end
context 'when the source message is not found' do
let(:content_attributes) { { in_reply_to_external_id: 'source-id-that-does-not-exist' } }
let(:new_message) { build(:message, conversation: conversation, content_attributes: content_attributes) }
it 'does not set in_reply_to' do
new_message.send(:ensure_in_reply_to)
expect(new_message.content_attributes[:in_reply_to]).to be_nil
end
end
end
end