Currently, we do not support reactions, ephemeral messages, or the request_welcome event for the WhatsApp channel. However, if this is the first event we receive in Chatwoot (i.e., there is no previous conversation or contact in Chatwoot), it will create a contact and a conversation without any messages. This confuses our customer, as it may appear that Chatwoot has missed some messages. There are multiple cases where this might be the first event we receive in Chatwoot. One quick example is when the user has sent an outbound campaign from another tool and their customers reacted to the message. Another event like this is request_welcome event. WhatsApp has a concept for welcome messages. You can send an outbound message even though the user has not send a message. You can receive notifications through a webhook whenever a WhatsApp user initiates a chat with you for the first time. (Read the Welcome message section: https://developers.facebook.com/docs/whatsapp/cloud-api/phone-numbers/conversational-components/ ). Although this can help the business send a pro-active message to the user, we don't have it scoped in our feature set. For now, I'm ignoring this event. Fixes https://linear.app/chatwoot/issue/CW-3018/whatsapp-handle-request-welcome-case-properly Fixes https://linear.app/chatwoot/issue/CW-3017/whatsapp-handle-reactions-properly
231 lines
9.1 KiB
Ruby
231 lines
9.1 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe Webhooks::WhatsappEventsJob do
|
|
subject(:job) { described_class }
|
|
|
|
let(:channel) { create(:channel_whatsapp, provider: 'whatsapp_cloud', sync_templates: false, validate_provider_config: false) }
|
|
let(:params) do
|
|
{
|
|
object: 'whatsapp_business_account',
|
|
phone_number: channel.phone_number,
|
|
entry: [{
|
|
changes: [
|
|
{
|
|
value: {
|
|
metadata: {
|
|
phone_number_id: channel.provider_config['phone_number_id'],
|
|
display_phone_number: channel.phone_number.delete('+')
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}]
|
|
}
|
|
end
|
|
let(:process_service) { double }
|
|
|
|
before do
|
|
allow(process_service).to receive(:perform)
|
|
end
|
|
|
|
it 'enqueues the job' do
|
|
expect { job.perform_later(params) }.to have_enqueued_job(described_class)
|
|
.with(params)
|
|
.on_queue('low')
|
|
end
|
|
|
|
context 'when whatsapp_cloud provider' do
|
|
it 'enqueue Whatsapp::IncomingMessageWhatsappCloudService' do
|
|
allow(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new).and_return(process_service)
|
|
expect(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new)
|
|
job.perform_now(params)
|
|
end
|
|
|
|
it 'will not enqueue message jobs based on phone number in the URL if the entry payload is not present' do
|
|
params = {
|
|
object: 'whatsapp_business_account',
|
|
phone_number: channel.phone_number,
|
|
entry: [{ changes: [{}] }]
|
|
}
|
|
allow(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new)
|
|
allow(Whatsapp::IncomingMessageService).to receive(:new)
|
|
|
|
expect(Whatsapp::IncomingMessageWhatsappCloudService).not_to receive(:new)
|
|
expect(Whatsapp::IncomingMessageService).not_to receive(:new)
|
|
job.perform_now(params)
|
|
end
|
|
|
|
it 'will not enqueue Whatsapp::IncomingMessageWhatsappCloudService if channel reauthorization required' do
|
|
channel.prompt_reauthorization!
|
|
allow(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new).and_return(process_service)
|
|
expect(Whatsapp::IncomingMessageWhatsappCloudService).not_to receive(:new)
|
|
job.perform_now(params)
|
|
end
|
|
|
|
it 'will not enqueue if channel is not present' do
|
|
allow(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new).and_return(process_service)
|
|
allow(Whatsapp::IncomingMessageService).to receive(:new).and_return(process_service)
|
|
|
|
expect(Whatsapp::IncomingMessageWhatsappCloudService).not_to receive(:new)
|
|
expect(Whatsapp::IncomingMessageService).not_to receive(:new)
|
|
job.perform_now(phone_number: 'random_phone_number')
|
|
end
|
|
|
|
it 'will not enqueue Whatsapp::IncomingMessageWhatsappCloudService if account is suspended' do
|
|
account = channel.account
|
|
account.update!(status: :suspended)
|
|
allow(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new).and_return(process_service)
|
|
allow(Whatsapp::IncomingMessageService).to receive(:new).and_return(process_service)
|
|
|
|
expect(Whatsapp::IncomingMessageWhatsappCloudService).not_to receive(:new)
|
|
expect(Whatsapp::IncomingMessageService).not_to receive(:new)
|
|
job.perform_now(params)
|
|
end
|
|
end
|
|
|
|
context 'when default provider' do
|
|
it 'enqueue Whatsapp::IncomingMessageService' do
|
|
stub_request(:post, 'https://waba.360dialog.io/v1/configs/webhook')
|
|
channel.update(provider: 'default')
|
|
allow(Whatsapp::IncomingMessageService).to receive(:new).and_return(process_service)
|
|
expect(Whatsapp::IncomingMessageService).to receive(:new)
|
|
job.perform_now(params)
|
|
end
|
|
end
|
|
|
|
context 'when whatsapp business params' do
|
|
it 'enqueue Whatsapp::IncomingMessageWhatsappCloudService based on the number in payload' do
|
|
other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false,
|
|
validate_provider_config: false)
|
|
wb_params = {
|
|
phone_number: channel.phone_number,
|
|
object: 'whatsapp_business_account',
|
|
entry: [
|
|
{
|
|
changes: [
|
|
{
|
|
value: {
|
|
metadata: {
|
|
phone_number_id: other_channel.provider_config['phone_number_id'],
|
|
display_phone_number: other_channel.phone_number.delete('+')
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
allow(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new).and_return(process_service)
|
|
expect(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new).with(inbox: other_channel.inbox, params: wb_params)
|
|
job.perform_now(wb_params)
|
|
end
|
|
|
|
it 'Ignore reaction type message and stop raising error' do
|
|
other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false,
|
|
validate_provider_config: false)
|
|
wb_params = {
|
|
phone_number: channel.phone_number,
|
|
object: 'whatsapp_business_account',
|
|
entry: [{
|
|
changes: [{
|
|
value: {
|
|
contacts: [{ profile: { name: 'Test Test' }, wa_id: '1111981136571' }],
|
|
messages: [{
|
|
from: '1111981136571', reaction: { emoji: '👍' }, timestamp: '1664799904', type: 'reaction'
|
|
}],
|
|
metadata: {
|
|
phone_number_id: other_channel.provider_config['phone_number_id'],
|
|
display_phone_number: other_channel.phone_number.delete('+')
|
|
}
|
|
}
|
|
}]
|
|
}]
|
|
}.with_indifferent_access
|
|
expect do
|
|
Whatsapp::IncomingMessageWhatsappCloudService.new(inbox: other_channel.inbox, params: wb_params).perform
|
|
end.not_to change(Message, :count)
|
|
end
|
|
|
|
it 'ignore reaction type message, would not create contact if the reaction is the first event' do
|
|
other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false,
|
|
validate_provider_config: false)
|
|
wb_params = {
|
|
phone_number: channel.phone_number,
|
|
object: 'whatsapp_business_account',
|
|
entry: [{
|
|
changes: [{
|
|
value: {
|
|
contacts: [{ profile: { name: 'Test Test' }, wa_id: '1111981136571' }],
|
|
messages: [{
|
|
from: '1111981136571', reaction: { emoji: '👍' }, timestamp: '1664799904', type: 'reaction'
|
|
}],
|
|
metadata: {
|
|
phone_number_id: other_channel.provider_config['phone_number_id'],
|
|
display_phone_number: other_channel.phone_number.delete('+')
|
|
}
|
|
}
|
|
}]
|
|
}]
|
|
}.with_indifferent_access
|
|
expect do
|
|
Whatsapp::IncomingMessageWhatsappCloudService.new(inbox: other_channel.inbox, params: wb_params).perform
|
|
end.not_to change(Contact, :count)
|
|
end
|
|
|
|
it 'ignore request_welcome type message, would not create contact or conversation' do
|
|
other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false,
|
|
validate_provider_config: false)
|
|
wb_params = {
|
|
phone_number: channel.phone_number,
|
|
object: 'whatsapp_business_account',
|
|
entry: [{
|
|
changes: [{
|
|
value: {
|
|
messages: [{
|
|
from: '1111981136571', timestamp: '1664799904', type: 'request_welcome'
|
|
}],
|
|
metadata: {
|
|
phone_number_id: other_channel.provider_config['phone_number_id'],
|
|
display_phone_number: other_channel.phone_number.delete('+')
|
|
}
|
|
}
|
|
}]
|
|
}]
|
|
}.with_indifferent_access
|
|
expect do
|
|
Whatsapp::IncomingMessageWhatsappCloudService.new(inbox: other_channel.inbox, params: wb_params).perform
|
|
end.not_to change(Contact, :count)
|
|
|
|
expect do
|
|
Whatsapp::IncomingMessageWhatsappCloudService.new(inbox: other_channel.inbox, params: wb_params).perform
|
|
end.not_to change(Conversation, :count)
|
|
end
|
|
|
|
it 'will not enque Whatsapp::IncomingMessageWhatsappCloudService when invalid phone number id' do
|
|
other_channel = create(:channel_whatsapp, phone_number: '+1987654', provider: 'whatsapp_cloud', sync_templates: false,
|
|
validate_provider_config: false)
|
|
wb_params = {
|
|
phone_number: channel.phone_number,
|
|
object: 'whatsapp_business_account',
|
|
entry: [
|
|
{
|
|
changes: [
|
|
{
|
|
value: {
|
|
metadata: {
|
|
phone_number_id: 'random phone number id',
|
|
display_phone_number: other_channel.phone_number.delete('+')
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
allow(Whatsapp::IncomingMessageWhatsappCloudService).to receive(:new).and_return(process_service)
|
|
expect(Whatsapp::IncomingMessageWhatsappCloudService).not_to receive(:new).with(inbox: other_channel.inbox, params: wb_params)
|
|
job.perform_now(wb_params)
|
|
end
|
|
end
|
|
end
|