mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-04 04:57:51 +00:00 
			
		
		
		
	feat: Add support for Instagram delivery reports (#8125)
This commit is contained in:
		@@ -231,7 +231,11 @@ export default {
 | 
				
			|||||||
        return contactLastSeenAt >= this.createdAt;
 | 
					        return contactLastSeenAt >= this.createdAt;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (this.isAWhatsAppChannel || this.isATwilioChannel) {
 | 
					      if (
 | 
				
			||||||
 | 
					        this.isAWhatsAppChannel ||
 | 
				
			||||||
 | 
					        this.isATwilioChannel ||
 | 
				
			||||||
 | 
					        this.isAFacebookInbox
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
        return this.sourceId && this.isRead;
 | 
					        return this.sourceId && this.isRead;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ class Webhooks::InstagramEventsJob < MutexApplicationJob
 | 
				
			|||||||
  base_uri 'https://graph.facebook.com/v11.0/me'
 | 
					  base_uri 'https://graph.facebook.com/v11.0/me'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # @return [Array] We will support further events like reaction or seen in future
 | 
					  # @return [Array] We will support further events like reaction or seen in future
 | 
				
			||||||
  SUPPORTED_EVENTS = [:message].freeze
 | 
					  SUPPORTED_EVENTS = [:message, :read].freeze
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def perform(entries)
 | 
					  def perform(entries)
 | 
				
			||||||
    @entries = entries
 | 
					    @entries = entries
 | 
				
			||||||
@@ -45,6 +45,10 @@ class Webhooks::InstagramEventsJob < MutexApplicationJob
 | 
				
			|||||||
    ::Instagram::MessageText.new(messaging).perform
 | 
					    ::Instagram::MessageText.new(messaging).perform
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def read(messaging)
 | 
				
			||||||
 | 
					    ::Instagram::ReadStatusService.new(params: messaging).perform
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def messages(entry)
 | 
					  def messages(entry)
 | 
				
			||||||
    (entry[:messaging].presence || entry[:standby] || [])
 | 
					    (entry[:messaging].presence || entry[:standby] || [])
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								app/services/instagram/read_status_service.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/services/instagram/read_status_service.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					class Instagram::ReadStatusService
 | 
				
			||||||
 | 
					  pattr_initialize [:params!]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def perform
 | 
				
			||||||
 | 
					    return if instagram_channel.blank?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    process_status if message.present?
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def process_status
 | 
				
			||||||
 | 
					    @message.status = 'read'
 | 
				
			||||||
 | 
					    @message.save!
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def instagram_id
 | 
				
			||||||
 | 
					    params[:recipient][:id]
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def instagram_channel
 | 
				
			||||||
 | 
					    @instagram_channel ||= Channel::FacebookPage.find_by(instagram_id: instagram_id)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def message
 | 
				
			||||||
 | 
					    return unless params[:read][:mid]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @message ||= @instagram_channel.inbox.messages.find_by(source_id: params[:read][:mid])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
@@ -242,4 +242,30 @@ FactoryBot.define do
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
    initialize_with { attributes }
 | 
					    initialize_with { attributes }
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  factory :messaging_seen_event, class: Hash do
 | 
				
			||||||
 | 
					    entry do
 | 
				
			||||||
 | 
					      [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          'id': 'instagram-message-id-123',
 | 
				
			||||||
 | 
					          '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',
 | 
				
			||||||
 | 
					              'read': {
 | 
				
			||||||
 | 
					                'mid': 'message-id-1'
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    initialize_with { attributes }
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ describe Webhooks::InstagramEventsJob do
 | 
				
			|||||||
  let!(:attachment_params) { build(:instagram_message_attachment_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!(:story_mention_params) { build(:instagram_story_mention_event).with_indifferent_access }
 | 
				
			||||||
  let!(:story_mention_echo_params) { build(:instagram_story_mention_event_with_echo).with_indifferent_access }
 | 
					  let!(:story_mention_echo_params) { build(:instagram_story_mention_event_with_echo).with_indifferent_access }
 | 
				
			||||||
 | 
					  let!(:messaging_seen_event) { build(:messaging_seen_event).with_indifferent_access }
 | 
				
			||||||
  let(:fb_object) { double }
 | 
					  let(:fb_object) { double }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe '#perform' do
 | 
					  describe '#perform' do
 | 
				
			||||||
@@ -151,6 +152,11 @@ describe Webhooks::InstagramEventsJob do
 | 
				
			|||||||
        expect(instagram_inbox.contact_inboxes.count).to be 0
 | 
					        expect(instagram_inbox.contact_inboxes.count).to be 0
 | 
				
			||||||
        expect(instagram_inbox.messages.count).to be 0
 | 
					        expect(instagram_inbox.messages.count).to be 0
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'handle messaging_seen callback' do
 | 
				
			||||||
 | 
					        expect(Instagram::ReadStatusService).to receive(:new).with(params: messaging_seen_event[:entry][0][:messaging][0]).and_call_original
 | 
				
			||||||
 | 
					        instagram_webhook.perform_now(messaging_seen_event[:entry])
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										48
									
								
								spec/services/instagram/read_status_service_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								spec/services/instagram/read_status_service_spec.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					require 'rails_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe Instagram::ReadStatusService do
 | 
				
			||||||
 | 
					  before do
 | 
				
			||||||
 | 
					    create(:message, message_type: :incoming, inbox: instagram_inbox, account: account, conversation: conversation,
 | 
				
			||||||
 | 
					                     source_id: 'chatwoot-app-user-id-1')
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let!(:account) { create(:account) }
 | 
				
			||||||
 | 
					  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!(:contact) { create(:contact, account: account) }
 | 
				
			||||||
 | 
					  let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: instagram_inbox) }
 | 
				
			||||||
 | 
					  let(:conversation) { create(:conversation, contact: contact, inbox: instagram_inbox, contact_inbox: contact_inbox) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe '#perform' do
 | 
				
			||||||
 | 
					    context 'when messaging_seen callback is fired' do
 | 
				
			||||||
 | 
					      let(:message) { conversation.messages.last }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'updates the message status to read if the status is delivered' do
 | 
				
			||||||
 | 
					        params = {
 | 
				
			||||||
 | 
					          recipient: {
 | 
				
			||||||
 | 
					            id: 'chatwoot-app-user-id-1'
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          read: {
 | 
				
			||||||
 | 
					            mid: message.source_id
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        described_class.new(params: params).perform
 | 
				
			||||||
 | 
					        expect(conversation.reload.messages.last.status).to eq('read')
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      it 'does not update the status if message is not found' do
 | 
				
			||||||
 | 
					        params = {
 | 
				
			||||||
 | 
					          recipient: {
 | 
				
			||||||
 | 
					            id: 'chatwoot-app-user-id-1'
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          read: {
 | 
				
			||||||
 | 
					            mid: 'random-message-id'
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        described_class.new(params: params).perform
 | 
				
			||||||
 | 
					        expect(conversation.reload.messages.last.status).not_to eq('read')
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
		Reference in New Issue
	
	Block a user