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
|
provider_service.disconnect_channel_provider
|
||||||
end
|
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 :setup_channel_provider, to: :provider_service
|
||||||
delegate :send_message, to: :provider_service
|
delegate :send_message, to: :provider_service
|
||||||
delegate :send_template, to: :provider_service
|
delegate :send_template, to: :provider_service
|
||||||
|
|||||||
@ -75,6 +75,8 @@ module Whatsapp::BaileysHandlers::MessagesUpsert
|
|||||||
handle_attach_media if attach_media
|
handle_attach_media if attach_media
|
||||||
|
|
||||||
@message.save!
|
@message.save!
|
||||||
|
|
||||||
|
inbox.channel.received_messages([@message], @conversation) if incoming?
|
||||||
end
|
end
|
||||||
|
|
||||||
def message_content_attributes
|
def message_content_attributes
|
||||||
|
|||||||
@ -121,8 +121,7 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
{
|
{
|
||||||
id: message.source_id,
|
id: message.source_id,
|
||||||
remoteJid: remote_jid,
|
remoteJid: remote_jid,
|
||||||
# NOTE: It only makes sense to mark received messages as read
|
fromMe: message.message_type == 'outgoing'
|
||||||
fromMe: false
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
}.to_json
|
}.to_json
|
||||||
@ -156,6 +155,26 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
process_response(response)
|
process_response(response)
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
def provider_url
|
def provider_url
|
||||||
@ -257,5 +276,6 @@ class Whatsapp::Providers::WhatsappBaileysService < Whatsapp::Providers::BaseSer
|
|||||||
:toggle_typing_status,
|
:toggle_typing_status,
|
||||||
:update_presence,
|
:update_presence,
|
||||||
:read_messages,
|
:read_messages,
|
||||||
:unread_message
|
:unread_message,
|
||||||
|
:received_messages
|
||||||
end
|
end
|
||||||
|
|||||||
@ -69,12 +69,14 @@ FactoryBot.define do
|
|||||||
transient do
|
transient do
|
||||||
sync_templates { true }
|
sync_templates { true }
|
||||||
validate_provider_config { true }
|
validate_provider_config { true }
|
||||||
|
received_messages { true }
|
||||||
end
|
end
|
||||||
|
|
||||||
before(:create) do |channel_whatsapp, options|
|
before(:create) do |channel_whatsapp, options|
|
||||||
# since factory already has the required message templates, we just need to bypass it getting updated
|
# 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(: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(: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'
|
if channel_whatsapp.provider == 'baileys'
|
||||||
channel_whatsapp.provider_config = channel_whatsapp.provider_config.merge({ 'api_key' => 'test_key', 'provider_url' => 'https://baileys.api',
|
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 })
|
'phone_number_id' => '123456789', 'mark_as_read' => true })
|
||||||
|
|||||||
@ -208,6 +208,32 @@ RSpec.describe Channel::Whatsapp do
|
|||||||
end
|
end
|
||||||
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 'callbacks' do
|
||||||
describe '#disconnect_channel_provider' do
|
describe '#disconnect_channel_provider' do
|
||||||
context 'when provider is baileys' do
|
context 'when provider is baileys' do
|
||||||
|
|||||||
@ -7,7 +7,8 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
create(:channel_whatsapp,
|
create(:channel_whatsapp,
|
||||||
provider: 'baileys',
|
provider: 'baileys',
|
||||||
provider_config: { webhook_verify_token: webhook_verify_token },
|
provider_config: { webhook_verify_token: webhook_verify_token },
|
||||||
validate_provider_config: false)
|
validate_provider_config: false,
|
||||||
|
received_messages: false)
|
||||||
end
|
end
|
||||||
let(:inbox) { whatsapp_channel.inbox }
|
let(:inbox) { whatsapp_channel.inbox }
|
||||||
|
|
||||||
@ -198,6 +199,16 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
expect(inbox.messages).to be_empty
|
expect(inbox.messages).to be_empty
|
||||||
expect(inbox.contact_inboxes).to be_empty
|
expect(inbox.contact_inboxes).to be_empty
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
context 'when message is context message' do
|
context 'when message is context message' do
|
||||||
@ -233,6 +244,30 @@ describe Whatsapp::IncomingMessageBaileysService do
|
|||||||
end
|
end
|
||||||
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 message type is text' do
|
||||||
context 'when has key conversation' do # rubocop:disable RSpec/NestedGroups
|
context 'when has key conversation' do # rubocop:disable RSpec/NestedGroups
|
||||||
it 'creates an incoming message' do
|
it 'creates an incoming message' do
|
||||||
|
|||||||
@ -438,6 +438,22 @@ describe Whatsapp::Providers::WhatsappBaileysService do
|
|||||||
end
|
end
|
||||||
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
|
describe '#toggle_typing_status' do
|
||||||
let(:request_path) { "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/presence" }
|
let(:request_path) { "#{whatsapp_channel.provider_config['provider_url']}/connections/#{whatsapp_channel.phone_number}/presence" }
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user