Feat: Support MMS in SMS Channel ( Bandwidth ) (#4046)

Ability to send and receive MMS to bandwidth channel

fixes: #3961
This commit is contained in:
Sojan Jose 2022-03-02 15:09:56 +05:30 committed by GitHub
parent 4d458c2184
commit 7b9e4982cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 133 additions and 26 deletions

View file

@ -3,7 +3,7 @@ module FileTypeHelper
def file_type(content_type)
return :image if image_file?(content_type)
return :video if video_file?(content_type)
return :audio if content_type.include?('audio/')
return :audio if content_type&.include?('audio/')
:file
end

View file

@ -293,7 +293,7 @@ export default {
if (this.isAWhatsappChannel) {
return MESSAGE_MAX_LENGTH.TWILIO_WHATSAPP;
}
if (this.isATwilioSMSChannel) {
if (this.isASmsInbox) {
return MESSAGE_MAX_LENGTH.TWILIO_SMS;
}
if (this.isATwitterInbox) {
@ -310,7 +310,7 @@ export default {
this.isAWhatsappChannel ||
this.isAPIInbox ||
this.isAnEmailChannel ||
this.isATwilioSMSChannel ||
this.isASmsInbox ||
this.isATelegramChannel
);
},

View file

@ -44,6 +44,9 @@ export default {
const { medium: medium = '' } = this.inbox;
return this.isATwilioChannel && medium === 'sms';
},
isASmsInbox() {
return this.channelType === INBOX_TYPES.SMS || this.isATwilioSMSChannel;
},
isATwilioWhatsappChannel() {
const { medium: medium = '' } = this.inbox;
return this.isATwilioChannel && medium === 'whatsapp';

View file

@ -62,6 +62,18 @@ describe('inboxMixin', () => {
expect(wrapper.vm.isAWebWidgetInbox).toBe(true);
});
it('isASmsInbox returns true if channel type is sms', () => {
const Component = {
render() {},
mixins: [inboxMixin],
data() {
return { inbox: { channel_type: 'Channel::Sms' } };
},
};
const wrapper = shallowMount(Component);
expect(wrapper.vm.isASmsInbox).toBe(true);
});
it('isATwilioChannel returns true if channel type is Twilio', () => {
const Component = {
render() {},
@ -94,6 +106,7 @@ describe('inboxMixin', () => {
const wrapper = shallowMount(Component);
expect(wrapper.vm.isATwilioChannel).toBe(true);
expect(wrapper.vm.isATwilioSMSChannel).toBe(true);
expect(wrapper.vm.isASmsInbox).toBe(true);
});
it('isATwilioWhatsappChannel returns true if channel type is Twilio and medium is whatsapp', () => {

View file

@ -48,6 +48,10 @@ class Attachment < ApplicationRecord
file.attached? ? url_for(file) : ''
end
def download_url
file.attached? ? rails_storage_proxy_url(file) : ''
end
def thumb_url
if file.attached? && file.representable?
url_for(file.representation(resize: '250x250'))

View file

@ -33,35 +33,38 @@ class Channel::Sms < ApplicationRecord
'https://messaging.bandwidth.com/api/v2'
end
# Extract later into provider Service
def send_message(phone_number, message)
if message.attachments.present?
send_attachment_message(phone_number, message)
else
send_text_message(phone_number, message.content)
end
def send_message(contact_number, message)
body = message_body(contact_number, message.content)
body['media'] = message.attachments.map(&:download_url) if message.attachments.present?
send_to_bandwidth(body)
end
def send_text_message(contact_number, message)
response = HTTParty.post(
"#{api_base_path}/users/#{provider_config['account_id']}/messages",
basic_auth: bandwidth_auth,
headers: { 'Content-Type' => 'application/json' },
body: {
'to' => contact_number,
'from' => phone_number,
'text' => message,
'applicationId' => provider_config['application_id']
}.to_json
)
response.success? ? response.parsed_response['id'] : nil
def send_text_message(contact_number, message_content)
body = message_body(contact_number, message_content)
send_to_bandwidth(body)
end
private
def send_attachment_message(phone_number, message)
# fix me
def message_body(contact_number, message_content)
{
'to' => contact_number,
'from' => phone_number,
'text' => message_content,
'applicationId' => provider_config['application_id']
}
end
def send_to_bandwidth(body)
response = HTTParty.post(
"#{api_base_path}/users/#{provider_config['account_id']}/messages",
basic_auth: bandwidth_auth,
headers: { 'Content-Type' => 'application/json' },
body: body.to_json
)
response.success? ? response.parsed_response['id'] : nil
end
def bandwidth_auth

View file

@ -14,6 +14,8 @@ class Sms::IncomingMessageService
sender: @contact,
source_id: params[:id]
)
attach_files
@message.save!
end
private
@ -22,6 +24,10 @@ class Sms::IncomingMessageService
@account ||= @inbox.account
end
def channel
@channel ||= @inbox.channel
end
def phone_number
params[:from]
end
@ -63,4 +69,28 @@ class Sms::IncomingMessageService
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'
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,
content_type: attachment_file.content_type
}
)
end
end
end

View file

@ -26,6 +26,37 @@ describe Sms::IncomingMessageService do
expect(Contact.all.first.name).to eq('+1 423-423-4234')
expect(sms_channel.inbox.messages.first.content).to eq('test message')
end
it 'creates attachment messages and ignores .smil files' do
stub_request(:get, 'http://test.com/test.png').to_return(status: 200, body: File.read('spec/assets/sample.png'), headers: {})
stub_request(:get, 'http://test.com/test2.png').to_return(status: 200, body: File.read('spec/assets/sample.png'), headers: {})
params = {
'id': '3232420-2323-234324',
'owner': sms_channel.phone_number,
'applicationId': '2342349-324234d-32432432',
'time': '2022-02-02T23:14:05.262Z',
'segmentCount': 1,
'direction': 'in',
'to': [
sms_channel.phone_number
],
'media': [
'http://test.com/test.smil',
'http://test.com/test.png',
'http://test.com/test2.png'
],
'from': '+14234234234',
'text': 'test message'
}.with_indifferent_access
described_class.new(inbox: sms_channel.inbox, params: params).perform
expect(sms_channel.inbox.conversations.count).not_to eq(0)
expect(Contact.all.first.name).to eq('+1 423-423-4234')
expect(sms_channel.inbox.messages.first.content).to eq('test message')
expect(sms_channel.inbox.messages.first.attachments.present?).to eq true
end
end
end
end

View file

@ -23,6 +23,29 @@ describe Sms::SendOnSmsService do
described_class.new(message: message).perform
expect(message.reload.source_id).to eq('123456789')
end
it 'calls channel.send_message with attachments' do
message = build(:message, message_type: :outgoing, content: 'test',
conversation: conversation)
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png')
attachment2 = message.attachments.new(account_id: message.account_id, file_type: :image)
attachment2.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png')
message.save!
allow(HTTParty).to receive(:post).and_return(sms_request)
allow(sms_request).to receive(:success?).and_return(true)
allow(sms_request).to receive(:parsed_response).and_return({ 'id' => '123456789' })
expect(HTTParty).to receive(:post).with(
'https://messaging.bandwidth.com/api/v2/users/1/messages',
basic_auth: { username: '1', password: '1' },
headers: { 'Content-Type' => 'application/json' },
body: { 'to' => '+123456789', 'from' => sms_channel.phone_number, 'text' => 'test', 'applicationId' => '1',
'media' => [attachment.download_url, attachment2.download_url] }.to_json
)
described_class.new(message: message).perform
expect(message.reload.source_id).to eq('123456789')
end
end
end
end