Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add delivery status for Twilio Channel (#8082)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
- Loading branch information
1 parent
88de335
commit 0bc2087
Showing
10 changed files
with
224 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
class Twilio::DeliveryStatusController < ApplicationController | ||
def create | ||
::Twilio::DeliveryStatusService.new(params: permitted_params).perform | ||
|
||
head :no_content | ||
end | ||
|
||
private | ||
|
||
def permitted_params | ||
params.permit( | ||
:AccountSid, | ||
:From, | ||
:MessageSid, | ||
:MessagingServiceSid, | ||
:MessageStatus, | ||
:ErrorCode, | ||
:ErrorMessage | ||
) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
class Twilio::DeliveryStatusService | ||
pattr_initialize [:params!] | ||
# Reference: https://www.twilio.com/docs/messaging/api/message-resource#message-status-values | ||
|
||
def perform | ||
return if twilio_channel.blank? | ||
|
||
return unless supported_status? | ||
|
||
process_statuses if message.present? | ||
end | ||
|
||
private | ||
|
||
def process_statuses | ||
@message.status = params[:MessageStatus] | ||
@message.external_error = external_error if error_occurred? | ||
@message.save! | ||
end | ||
|
||
def supported_status? | ||
%w[sent delivered read failed undelivered].include?(params[:MessageStatus]) | ||
end | ||
|
||
def external_error | ||
return nil unless error_occurred? | ||
|
||
error_message = params[:ErrorMessage].presence | ||
error_code = params[:ErrorCode] | ||
|
||
if error_message.present? | ||
"#{error_code} - #{error_message}" | ||
elsif error_code.present? | ||
I18n.t('conversations.messages.delivery_status.error_code', error_code: error_code) | ||
end | ||
end | ||
|
||
def error_occurred? | ||
params[:ErrorCode].present? && %w[failed undelivered].include?(params[:MessageStatus]) | ||
end | ||
|
||
def twilio_channel | ||
@twilio_channel ||= if params[:MessagingServiceSid].present? | ||
::Channel::TwilioSms.find_by(messaging_service_sid: params[:MessagingServiceSid]) | ||
elsif params[:AccountSid].present? && params[:From].present? | ||
::Channel::TwilioSms.find_by!(account_sid: params[:AccountSid], phone_number: params[:From]) | ||
end | ||
end | ||
|
||
def message | ||
return unless params[:MessageSid] | ||
|
||
@message ||= twilio_channel.inbox.messages.find_by(source_id: params[:MessageSid]) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
spec/controllers/twilio/delivery_status_controller_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
require 'rails_helper' | ||
|
||
RSpec.describe 'Twilio::DeliveryStatusController', type: :request do | ||
include Rails.application.routes.url_helpers | ||
let(:twilio_service) { instance_double(Twilio::DeliveryStatusService) } | ||
|
||
before do | ||
allow(Twilio::DeliveryStatusService).to receive(:new).and_return(twilio_service) | ||
allow(twilio_service).to receive(:perform) | ||
end | ||
|
||
describe 'POST /twilio/delivery' do | ||
it 'calls incoming message service' do | ||
post twilio_delivery_status_index_url, params: {} | ||
expect(twilio_service).to have_received(:perform) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
require 'rails_helper' | ||
|
||
describe Twilio::DeliveryStatusService do | ||
let!(:account) { create(:account) } | ||
let!(:twilio_channel) do | ||
create(:channel_twilio_sms, account: account, account_sid: 'ACxxx', | ||
inbox: create(:inbox, account: account, greeting_enabled: false)) | ||
end | ||
let!(:contact) { create(:contact, account: account, phone_number: '+12345') } | ||
let(:contact_inbox) { create(:contact_inbox, source_id: '+12345', contact: contact, inbox: twilio_channel.inbox) } | ||
let!(:conversation) { create(:conversation, contact: contact, inbox: twilio_channel.inbox, contact_inbox: contact_inbox) } | ||
|
||
describe '#perform' do | ||
context 'when message delivery status is fired' do | ||
before do | ||
create(:message, account: account, inbox: twilio_channel.inbox, conversation: conversation, status: :sent, | ||
source_id: 'SMd560ac79e4a4d36b3ce59f1f50471986') | ||
end | ||
|
||
it 'updates the message if the status is delivered' do | ||
params = { | ||
SmsSid: 'SMxx', | ||
From: '+12345', | ||
AccountSid: 'ACxxx', | ||
MessagingServiceSid: twilio_channel.messaging_service_sid, | ||
MessageSid: conversation.messages.last.source_id, | ||
MessageStatus: 'delivered' | ||
} | ||
|
||
described_class.new(params: params).perform | ||
expect(conversation.reload.messages.last.status).to eq('delivered') | ||
end | ||
|
||
it 'updates the message if the status is read' do | ||
params = { | ||
SmsSid: 'SMxx', | ||
From: '+12345', | ||
AccountSid: 'ACxxx', | ||
MessagingServiceSid: twilio_channel.messaging_service_sid, | ||
MessageSid: conversation.messages.last.source_id, | ||
MessageStatus: 'read' | ||
} | ||
|
||
described_class.new(params: params).perform | ||
expect(conversation.reload.messages.last.status).to eq('read') | ||
end | ||
|
||
it 'does not update the message if the status is not a support status' do | ||
params = { | ||
SmsSid: 'SMxx', | ||
From: '+12345', | ||
AccountSid: 'ACxxx', | ||
MessagingServiceSid: twilio_channel.messaging_service_sid, | ||
MessageSid: conversation.messages.last.source_id, | ||
MessageStatus: 'queued' | ||
} | ||
|
||
described_class.new(params: params).perform | ||
expect(conversation.reload.messages.last.status).to eq('sent') | ||
end | ||
|
||
it 'updates message status to failed if message status is failed' do | ||
params = { | ||
SmsSid: 'SMxx', | ||
From: '+12345', | ||
AccountSid: 'ACxxx', | ||
MessagingServiceSid: twilio_channel.messaging_service_sid, | ||
MessageSid: conversation.messages.last.source_id, | ||
MessageStatus: 'failed' | ||
} | ||
|
||
described_class.new(params: params).perform | ||
expect(conversation.reload.messages.last.status).to eq('failed') | ||
end | ||
|
||
it 'updates message status to failed and updates the error message if message status is undelivered' do | ||
params = { | ||
SmsSid: 'SMxx', | ||
From: '+12345', | ||
AccountSid: 'ACxxx', | ||
MessagingServiceSid: twilio_channel.messaging_service_sid, | ||
MessageSid: conversation.messages.last.source_id, | ||
MessageStatus: 'failed', | ||
ErrorCode: '30008', | ||
ErrorMessage: 'Unknown error' | ||
} | ||
|
||
described_class.new(params: params).perform | ||
expect(conversation.reload.messages.last.status).to eq('failed') | ||
expect(conversation.reload.messages.last.external_error).to eq('30008 - Unknown error') | ||
end | ||
|
||
it 'updates the error message if message status is undelivered and error message is not present' do | ||
params = { | ||
SmsSid: 'SMxx', | ||
From: '+12345', | ||
AccountSid: 'ACxxx', | ||
MessagingServiceSid: twilio_channel.messaging_service_sid, | ||
MessageSid: conversation.messages.last.source_id, | ||
MessageStatus: 'failed', | ||
ErrorCode: '30008' | ||
} | ||
|
||
described_class.new(params: params).perform | ||
expect(conversation.reload.messages.last.status).to eq('failed') | ||
expect(conversation.reload.messages.last.external_error).to eq('Error code: 30008') | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters