Restructure omni services and add Chatwoot research snapshot
This commit is contained in:
102
research/chatwoot/app/jobs/webhooks/whatsapp_events_job.rb
Normal file
102
research/chatwoot/app/jobs/webhooks/whatsapp_events_job.rb
Normal file
@@ -0,0 +1,102 @@
|
||||
class Webhooks::WhatsappEventsJob < ApplicationJob
|
||||
queue_as :low
|
||||
|
||||
def perform(params = {})
|
||||
channel = find_channel_from_whatsapp_business_payload(params)
|
||||
|
||||
if channel_is_inactive?(channel)
|
||||
Rails.logger.warn("Inactive WhatsApp channel: #{channel&.phone_number || "unknown - #{params[:phone_number]}"}")
|
||||
return
|
||||
end
|
||||
|
||||
if message_echo_event?(params)
|
||||
handle_message_echo(channel, params)
|
||||
else
|
||||
handle_message_events(channel, params)
|
||||
end
|
||||
end
|
||||
|
||||
# Detects if the webhook is an SMB message echo event (message sent from WhatsApp Business app)
|
||||
# This is part of WhatsApp coexistence feature where businesses can respond from both
|
||||
# Chatwoot and the WhatsApp Business app, with messages synced to Chatwoot.
|
||||
#
|
||||
# Regular message payload (field: "messages"):
|
||||
# {
|
||||
# "entry": [{
|
||||
# "changes": [{
|
||||
# "field": "messages",
|
||||
# "value": {
|
||||
# "contacts": [{ "wa_id": "919745786257", "profile": { "name": "Customer" } }],
|
||||
# "messages": [{ "from": "919745786257", "id": "wamid...", "text": { "body": "Hello" } }]
|
||||
# }
|
||||
# }]
|
||||
# }]
|
||||
# }
|
||||
#
|
||||
# Echo message payload (field: "smb_message_echoes"):
|
||||
# {
|
||||
# "entry": [{
|
||||
# "changes": [{
|
||||
# "field": "smb_message_echoes",
|
||||
# "value": {
|
||||
# "message_echoes": [{ "from": "971545296927", "to": "919745786257", "id": "wamid...", "text": { "body": "Hi" } }]
|
||||
# }
|
||||
# }]
|
||||
# }]
|
||||
# }
|
||||
#
|
||||
# Key differences:
|
||||
# - field: "smb_message_echoes" instead of "messages"
|
||||
# - message_echoes[] instead of messages[]
|
||||
# - "from" is the business number, "to" is the contact (reversed from regular messages)
|
||||
# - No "contacts" array in echo payload
|
||||
def message_echo_event?(params)
|
||||
params.dig(:entry, 0, :changes, 0, :field) == 'smb_message_echoes'
|
||||
end
|
||||
|
||||
def handle_message_echo(channel, params)
|
||||
Whatsapp::IncomingMessageWhatsappCloudService.new(inbox: channel.inbox, params: params, outgoing_echo: true).perform
|
||||
end
|
||||
|
||||
def handle_message_events(channel, params)
|
||||
case channel.provider
|
||||
when 'whatsapp_cloud'
|
||||
Whatsapp::IncomingMessageWhatsappCloudService.new(inbox: channel.inbox, params: params).perform
|
||||
else
|
||||
Whatsapp::IncomingMessageService.new(inbox: channel.inbox, params: params).perform
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def channel_is_inactive?(channel)
|
||||
return true if channel.blank?
|
||||
return true if channel.reauthorization_required?
|
||||
return true unless channel.account.active?
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def find_channel_by_url_param(params)
|
||||
return unless params[:phone_number]
|
||||
|
||||
Channel::Whatsapp.find_by(phone_number: params[:phone_number])
|
||||
end
|
||||
|
||||
def find_channel_from_whatsapp_business_payload(params)
|
||||
# for the case where facebook cloud api support multiple numbers for a single app
|
||||
# https://github.com/chatwoot/chatwoot/issues/4712#issuecomment-1173838350
|
||||
# we will give priority to the phone_number in the payload
|
||||
return get_channel_from_wb_payload(params) if params[:object] == 'whatsapp_business_account'
|
||||
|
||||
find_channel_by_url_param(params)
|
||||
end
|
||||
|
||||
def get_channel_from_wb_payload(wb_params)
|
||||
phone_number = "+#{wb_params[:entry].first[:changes].first.dig(:value, :metadata, :display_phone_number)}"
|
||||
phone_number_id = wb_params[:entry].first[:changes].first.dig(:value, :metadata, :phone_number_id)
|
||||
channel = Channel::Whatsapp.find_by(phone_number: phone_number)
|
||||
# validate to ensure the phone number id matches the whatsapp channel
|
||||
return channel if channel && channel.provider_config['phone_number_id'] == phone_number_id
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user