Restructure omni services and add Chatwoot research snapshot
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
class Sms::DeliveryStatusService
|
||||
pattr_initialize [:inbox!, :params!]
|
||||
|
||||
def perform
|
||||
return unless supported_status?
|
||||
|
||||
process_status if message.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_status
|
||||
@message.status = status
|
||||
@message.external_error = external_error if error_occurred?
|
||||
@message.save!
|
||||
end
|
||||
|
||||
def supported_status?
|
||||
%w[message-delivered message-failed].include?(params[:type])
|
||||
end
|
||||
|
||||
# Relevant documentation:
|
||||
# https://dev.bandwidth.com/docs/mfa/webhooks/international/message-delivered
|
||||
# https://dev.bandwidth.com/docs/mfa/webhooks/international/message-failed
|
||||
def status
|
||||
type_mapping = {
|
||||
'message-delivered' => 'delivered',
|
||||
'message-failed' => 'failed'
|
||||
}
|
||||
|
||||
type_mapping[params[:type]]
|
||||
end
|
||||
|
||||
def external_error
|
||||
return nil unless error_occurred?
|
||||
|
||||
error_message = params[:description]
|
||||
error_code = params[:errorCode]
|
||||
|
||||
"#{error_code} - #{error_message}"
|
||||
end
|
||||
|
||||
def error_occurred?
|
||||
params[:errorCode] && params[:type] == 'message-failed'
|
||||
end
|
||||
|
||||
def message
|
||||
return unless params[:message][:id]
|
||||
|
||||
@message ||= inbox.messages.find_by(source_id: params[:message][:id])
|
||||
end
|
||||
end
|
||||
102
research/chatwoot/app/services/sms/incoming_message_service.rb
Normal file
102
research/chatwoot/app/services/sms/incoming_message_service.rb
Normal file
@@ -0,0 +1,102 @@
|
||||
class Sms::IncomingMessageService
|
||||
include ::FileTypeHelper
|
||||
|
||||
pattr_initialize [:inbox!, :params!]
|
||||
|
||||
def perform
|
||||
set_contact
|
||||
set_conversation
|
||||
@message = @conversation.messages.create!(
|
||||
content: params[:text],
|
||||
account_id: @inbox.account_id,
|
||||
inbox_id: @inbox.id,
|
||||
message_type: :incoming,
|
||||
sender: @contact,
|
||||
source_id: params[:id]
|
||||
)
|
||||
attach_files
|
||||
@message.save!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account
|
||||
@account ||= @inbox.account
|
||||
end
|
||||
|
||||
def channel
|
||||
@channel ||= @inbox.channel
|
||||
end
|
||||
|
||||
def phone_number
|
||||
params[:from]
|
||||
end
|
||||
|
||||
def formatted_phone_number
|
||||
TelephoneNumber.parse(phone_number).international_number
|
||||
end
|
||||
|
||||
def set_contact
|
||||
contact_inbox = ::ContactInboxWithContactBuilder.new(
|
||||
source_id: params[:from],
|
||||
inbox: @inbox,
|
||||
contact_attributes: contact_attributes
|
||||
).perform
|
||||
|
||||
@contact_inbox = contact_inbox
|
||||
@contact = contact_inbox.contact
|
||||
end
|
||||
|
||||
def conversation_params
|
||||
{
|
||||
account_id: @inbox.account_id,
|
||||
inbox_id: @inbox.id,
|
||||
contact_id: @contact.id,
|
||||
contact_inbox_id: @contact_inbox.id
|
||||
}
|
||||
end
|
||||
|
||||
def set_conversation
|
||||
# if lock to single conversation is disabled, we will create a new conversation if previous conversation is resolved
|
||||
@conversation = if @inbox.lock_to_single_conversation
|
||||
@contact_inbox.conversations.last
|
||||
else
|
||||
@contact_inbox.conversations.where
|
||||
.not(status: :resolved).last
|
||||
end
|
||||
return if @conversation
|
||||
|
||||
@conversation = ::Conversation.create!(conversation_params)
|
||||
end
|
||||
|
||||
def contact_attributes
|
||||
{
|
||||
name: formatted_phone_number,
|
||||
phone_number: phone_number
|
||||
}
|
||||
end
|
||||
|
||||
def attach_files
|
||||
return if params[:media].blank?
|
||||
|
||||
params[:media].each do |media_url|
|
||||
# we don't need to process this files since chatwoot doesn't support it
|
||||
next if media_url.end_with?('.smil', '.xml')
|
||||
|
||||
attachment_file = Down.download(
|
||||
media_url,
|
||||
http_basic_authentication: [channel.provider_config['api_key'], channel.provider_config['api_secret']]
|
||||
)
|
||||
|
||||
@message.attachments.new(
|
||||
account_id: @message.account_id,
|
||||
file_type: file_type(attachment_file.content_type),
|
||||
file: {
|
||||
io: attachment_file,
|
||||
filename: attachment_file.original_filename,
|
||||
content_type: attachment_file.content_type
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
class Sms::OneoffSmsCampaignService
|
||||
pattr_initialize [:campaign!]
|
||||
|
||||
def perform
|
||||
raise "Invalid campaign #{campaign.id}" if campaign.inbox.inbox_type != 'Sms' || !campaign.one_off?
|
||||
raise 'Completed Campaign' if campaign.completed?
|
||||
|
||||
# marks campaign completed so that other jobs won't pick it up
|
||||
campaign.completed!
|
||||
|
||||
audience_label_ids = campaign.audience.select { |audience| audience['type'] == 'Label' }.pluck('id')
|
||||
audience_labels = campaign.account.labels.where(id: audience_label_ids).pluck(:title)
|
||||
process_audience(audience_labels)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
delegate :inbox, to: :campaign
|
||||
delegate :channel, to: :inbox
|
||||
|
||||
def process_audience(audience_labels)
|
||||
campaign.account.contacts.tagged_with(audience_labels, any: true).each do |contact|
|
||||
next if contact.phone_number.blank?
|
||||
|
||||
content = Liquid::CampaignTemplateService.new(campaign: campaign, contact: contact).call(campaign.message)
|
||||
send_message(to: contact.phone_number, content: content)
|
||||
end
|
||||
end
|
||||
|
||||
def send_message(to:, content:)
|
||||
channel.send_text_message(to, content)
|
||||
rescue StandardError => e
|
||||
Rails.logger.error("[SMS Campaign #{campaign.id}] Failed to send to #{to}: #{e.message}")
|
||||
end
|
||||
end
|
||||
16
research/chatwoot/app/services/sms/send_on_sms_service.rb
Normal file
16
research/chatwoot/app/services/sms/send_on_sms_service.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class Sms::SendOnSmsService < Base::SendOnChannelService
|
||||
private
|
||||
|
||||
def channel_class
|
||||
Channel::Sms
|
||||
end
|
||||
|
||||
def perform_reply
|
||||
send_on_sms
|
||||
end
|
||||
|
||||
def send_on_sms
|
||||
message_id = channel.send_message(message.conversation.contact_inbox.source_id, message)
|
||||
message.update!(source_id: message_id) if message_id.present?
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user