Files
leadchat/app/services/twilio/incoming_message_service.rb
Sojan Jose d107d0adec fix: Twilio channel attachment issues (#10167)
We received customer reports that attachments in Twilio messages
required page reloads to appear. This issue occurred because in the old
Twilio builder, we saved the message and attachment in two stages. The
new builders follow a streamlined approach, where both are saved in a
single transaction. This update aligns the Twilio channel with the new
builder format and resolves the issue.

### Testing:

Tests cover the attachment cases, ensuring that all original tests pass
with these changes.
2024-09-25 18:03:28 -07:00

149 lines
4.0 KiB
Ruby

class Twilio::IncomingMessageService
include ::FileTypeHelper
pattr_initialize [:params!]
def perform
return if twilio_channel.blank?
set_contact
set_conversation
@message = @conversation.messages.build(
content: message_body,
account_id: @inbox.account_id,
inbox_id: @inbox.id,
message_type: :incoming,
sender: @contact,
source_id: params[:SmsSid]
)
attach_files
@message.save!
end
private
def twilio_channel
@twilio_channel ||= ::Channel::TwilioSms.find_by(messaging_service_sid: params[:MessagingServiceSid]) if params[:MessagingServiceSid].present?
if params[:AccountSid].present? && params[:To].present?
@twilio_channel ||= ::Channel::TwilioSms.find_by!(account_sid: params[:AccountSid],
phone_number: params[:To])
end
@twilio_channel
end
def inbox
@inbox ||= twilio_channel.inbox
end
def account
@account ||= inbox.account
end
def phone_number
twilio_channel.sms? ? params[:From] : params[:From].gsub('whatsapp:', '')
end
def formatted_phone_number
TelephoneNumber.parse(phone_number).international_number
end
def message_body
params[:Body]&.delete("\u0000")
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,
additional_attributes: additional_attributes
}
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,
additional_attributes: additional_attributes
}
end
def additional_attributes
if twilio_channel.sms?
{
from_zip_code: params[:FromZip],
from_country: params[:FromCountry],
from_state: params[:FromState]
}
else
{}
end
end
def attach_files
return if params[:MediaUrl0].blank?
attachment_file = download_attachment_file
return if attachment_file.blank?
@message.attachments.new(
account_id: @message.account_id,
file_type: file_type(params[:MediaContentType0]),
file: {
io: attachment_file,
filename: attachment_file.original_filename,
content_type: attachment_file.content_type
}
)
end
def download_attachment_file
download_with_auth
rescue Down::Error, Down::ClientError => e
handle_download_attachment_error(e)
end
def download_with_auth
Down.download(
params[:MediaUrl0],
# https://support.twilio.com/hc/en-us/articles/223183748-Protect-Media-Access-with-HTTP-Basic-Authentication-for-Programmable-Messaging
http_basic_authentication: [twilio_channel.account_sid, twilio_channel.auth_token || twilio_channel.api_key_sid]
)
end
# This is just a temporary workaround since some users have not yet enabled media protection. We will remove this in the future.
def handle_download_attachment_error(error)
Rails.logger.info "Error downloading attachment from Twilio: #{error.message}: Retrying"
Down.download(params[:MediaUrl0])
rescue StandardError => e
Rails.logger.info "Error downloading attachment from Twilio: #{e.message}: Skipping"
nil
end
end