fix: send receipt to received messages (#70)
* feat: add received messages handling in WhatsApp services * feat: ensure received messages are processed in the conversation * tests: add received messages specs * tests: add spec for call provider received_messages after save incoming message * test: add spec to prevent calling received_messages for outgoing messages * feat: add received_messages to error handling methods * test: add spec for sending received messages request in WhatsappBaileysService * fix: update fromMe flag in WhatsappBaileysService * test: add spec to verify received_messages is not called for protocol messages * test: improve received_messages stub --------- Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
This commit is contained in:
parent
a355b53464
commit
6a5c0b515d
@ -111,6 +111,12 @@ class Channel::Whatsapp < ApplicationRecord
|
||||
provider_service.disconnect_channel_provider
|
||||
end
|
||||
|
||||
def received_messages(messages, conversation)
|
||||
return unless provider_service.respond_to?(:received_messages)
|
||||
|
||||
provider_service.received_messages(conversation.contact.phone_number, messages)
|
||||
end
|
||||
|
||||
delegate :setup_channel_provider, to: :provider_service
|
||||
delegate :send_message, to: :provider_service
|
||||
delegate :send_template, to: :provider_service
|
||||
|
||||
@ -75,6 +75,8 @@ module Whatsapp::BaileysHandlers::MessagesUpsert
|
||||
handle_attach_media if attach_media
|
||||
|
||||
@message.save!
|
||||
|
||||
inbox.channel.received_messages([@message], @conversation) if incoming?
|
||||
end
|
||||
|
||||
def message_content_attributes
|
||||
|
||||
@ -121,8 +121,7 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
{
|
||||
id: message.source_id,
|
||||
remoteJid: remote_jid,
|
||||
# NOTE: It only makes sense to mark received messages as read
|
||||
fromMe: false
|
||||
fromMe: message.message_type == 'outgoing'
|
||||
}
|
||||
end
|
||||
}.to_json
|
||||
@ -156,6 +155,26 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
process_response(response)
|
||||
end
|
||||
|
||||
def received_messages(phone_number, messages)
|
||||
@phone_number = phone_number
|
||||
|
||||
response = HTTParty.post(
|
||||
"#{provider_url}/connections/#{whatsapp_channel.phone_number}/send-receipts",
|
||||
headers: api_headers,
|
||||
body: {
|
||||
keys: messages.map do |message|
|
||||
{
|
||||
id: message.source_id,
|
||||
remoteJid: remote_jid,
|
||||
fromMe: message.message_type == 'outgoing'
|
||||
}
|
||||
end
|
||||
}.to_json
|
||||
)
|
||||
|
||||
process_response(response)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def provider_url
|
||||
@ -257,5 +276,6 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
||||
:toggle_typing_status,
|
||||
:update_presence,
|
||||
:read_messages,
|
||||
:unread_message
|
||||
:unread_message,
|
||||
:received_messages
|
||||
end
|
||||
|
||||
@ -69,12 +69,14 @@ FactoryBot.define do
|
||||
transient do
|
||||
sync_templates { true }
|
||||
validate_provider_config { true }
|
||||
received_messages { true }
|
||||
end
|
||||
|
||||
before(:create) do |channel_whatsapp, options|
|
||||
# since factory already has the required message templates, we just need to bypass it getting updated
|
||||
channel_whatsapp.define_singleton_method(:sync_templates) { nil } unless options.sync_templates
|
||||
channel_whatsapp.define_singleton_method(:validate_provider_config) { nil } unless options.validate_provider_config
|
||||
channel_whatsapp.define_singleton_method(:received_messages) { |_, _| nil } unless options.received_messages
|
||||
if channel_whatsapp.provider == 'baileys'
|
||||
channel_whatsapp.provider_config = channel_whatsapp.provider_config.merge({ 'api_key' => 'test_key', 'provider_url' => 'https://baileys.api',
|
||||
'phone_number_id' => '123456789', 'mark_as_read' => true })
|
||||
|
||||
@ -208,6 +208,32 @@ RSpec.describe Channel::Whatsapp do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#received_messages' do
|
||||
let(:channel) { create(:channel_whatsapp, provider: 'baileys', validate_provider_config: false, sync_templates: false) }
|
||||
let(:conversation) { create(:conversation) }
|
||||
let(:messages) { [create(:message, conversation: conversation)] }
|
||||
|
||||
it 'calls provider service method' do
|
||||
provider_double = instance_double(Whatsapp::Providers::WhatsappBaileysService, received_messages: nil)
|
||||
allow(provider_double).to receive(:received_messages).with(conversation.contact.phone_number, messages)
|
||||
allow(Whatsapp::Providers::WhatsappBaileysService).to receive(:new)
|
||||
.with(whatsapp_channel: channel)
|
||||
.and_return(provider_double)
|
||||
|
||||
channel.received_messages(messages, conversation)
|
||||
|
||||
expect(provider_double).to have_received(:received_messages)
|
||||
end
|
||||
|
||||
it 'does not call method if provider service does not implement it' do
|
||||
channel.update!(provider: 'whatsapp_cloud')
|
||||
|
||||
expect do
|
||||
channel.received_messages(messages, conversation)
|
||||
end.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe 'callbacks' do
|
||||
describe '#disconnect_channel_provider' do
|
||||
context 'when provider is baileys' do
|
||||
|
||||
@ -7,7 +7,8 @@ describe Whatsapp::IncomingMessageBaileysService do
|
||||
create(:channel_whatsapp,
|
||||
provider: 'baileys',
|
||||
provider_config: { webhook_verify_token: webhook_verify_token },
|
||||
validate_provider_config: false)
|
||||
validate_provider_config: false,
|
||||
received_messages: false)
|
||||
end
|
||||
let(:inbox) { whatsapp_channel.inbox }
|
||||
|
||||
@ -198,6 +199,16 @@ describe Whatsapp::IncomingMessageBaileysService do
|
||||
expect(inbox.messages).to be_empty
|
||||
expect(inbox.contact_inboxes).to be_empty
|
||||
end
|
||||
|
||||
it 'does not call channel received_messages method' do
|
||||
raw_message[:message] = { protocolMessage: { type: 1 } }
|
||||
|
||||
allow(inbox.channel).to receive(:received_messages)
|
||||
|
||||
described_class.new(inbox: inbox, params: params).perform
|
||||
|
||||
expect(inbox.channel).not_to have_received(:received_messages)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when message is context message' do
|
||||
@ -233,6 +244,30 @@ describe Whatsapp::IncomingMessageBaileysService do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when message is saved' do
|
||||
it 'calls channel received_messages method' do
|
||||
allow(inbox.channel).to receive(:received_messages)
|
||||
|
||||
described_class.new(inbox: inbox, params: params).perform
|
||||
|
||||
conversation = inbox.conversations.last
|
||||
message = conversation.messages.last
|
||||
|
||||
expect(inbox.channel).to have_received(:received_messages).with([message], conversation)
|
||||
end
|
||||
|
||||
it 'does not call channel received_messages method if message is outgoing' do
|
||||
raw_message[:key][:fromMe] = true
|
||||
create(:account_user, account: inbox.account)
|
||||
|
||||
allow(inbox.channel).to receive(:received_messages)
|
||||
|
||||
described_class.new(inbox: inbox, params: params).perform
|
||||
|
||||
expect(inbox.channel).not_to have_received(:received_messages)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when message type is text' do
|
||||
context 'when has key conversation' do # rubocop:disable RSpec/NestedGroups
|
||||
it 'creates an incoming message' do
|
||||
|
||||
@ -438,6 +438,22 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#received_messages' do
|
||||
it 'send received messages request' do
|
||||
stub_request(:post, "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/send-receipts")
|
||||
.with(
|
||||
headers: stub_headers(whatsapp_channel),
|
||||
body: {
|
||||
keys: [{ id: message.source_id, remoteJid: test_send_jid, fromMe: false }]
|
||||
}.to_json
|
||||
).to_return(status: 200)
|
||||
|
||||
result = service.received_messages(test_send_phone_number, [message])
|
||||
|
||||
expect(result).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#toggle_typing_status' do
|
||||
let(:request_path) { "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/presence" }
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user