Feature: Twilio Whatsapp Integration (#779)

Twilio Whatsapp Integration

Co-authored-by: Sojan <sojan@pepalo.com>
This commit is contained in:
Pranav Raj S
2020-04-30 01:41:13 +05:30
committed by GitHub
parent 168042f9a4
commit 0cb7333977
23 changed files with 238 additions and 81 deletions

View File

@@ -17,6 +17,7 @@ gem 'jbuilder'
gem 'kaminari' gem 'kaminari'
gem 'responders' gem 'responders'
gem 'rest-client' gem 'rest-client'
gem 'telephone_number'
gem 'time_diff' gem 'time_diff'
gem 'tzinfo-data' gem 'tzinfo-data'
gem 'valid_email2' gem 'valid_email2'

View File

@@ -448,6 +448,7 @@ GEM
faraday faraday
inflecto inflecto
virtus virtus
telephone_number (1.4.6)
thor (0.20.3) thor (0.20.3)
thread_safe (0.3.6) thread_safe (0.3.6)
time_diff (0.3.0) time_diff (0.3.0)
@@ -560,6 +561,7 @@ DEPENDENCIES
spring spring
spring-watcher-listen spring-watcher-listen
telegram-bot-ruby telegram-bot-ruby
telephone_number
time_diff time_diff
twilio-ruby (~> 5.32.0) twilio-ruby (~> 5.32.0)
twitty! twitty!

View File

@@ -21,13 +21,14 @@ class ContactBuilder
phone_number: contact_attributes[:phone_number], phone_number: contact_attributes[:phone_number],
email: contact_attributes[:email], email: contact_attributes[:email],
identifier: contact_attributes[:identifier], identifier: contact_attributes[:identifier],
additional_attributes: contact_attributes[:identifier] additional_attributes: contact_attributes[:additional_attributes]
) )
contact_inbox = ::ContactInbox.create!( contact_inbox = ::ContactInbox.create!(
contact_id: contact.id, contact_id: contact.id,
inbox_id: inbox.id, inbox_id: inbox.id,
source_id: source_id source_id: source_id
) )
::ContactAvatarJob.perform_later(contact, contact_attributes[:avatar_url]) if contact_attributes[:avatar_url] ::ContactAvatarJob.perform_later(contact, contact_attributes[:avatar_url]) if contact_attributes[:avatar_url]
contact_inbox contact_inbox
rescue StandardError => e rescue StandardError => e

View File

@@ -15,7 +15,6 @@ class Messages::Outgoing::NormalBuilder
def perform def perform
@message = @conversation.messages.build(message_params) @message = @conversation.messages.build(message_params)
@message.save
if @attachments.present? if @attachments.present?
@attachments.each do |uploaded_attachment| @attachments.each do |uploaded_attachment|
attachment = @message.attachments.new( attachment = @message.attachments.new(
@@ -24,8 +23,8 @@ class Messages::Outgoing::NormalBuilder
) )
attachment.file.attach(uploaded_attachment) attachment.file.attach(uploaded_attachment)
end end
@message.save
end end
@message.save
@message @message
end end

View File

@@ -2,14 +2,16 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::BaseControlle
before_action :authorize_request before_action :authorize_request
def create def create
ActiveRecord::Base.transaction do
authenticate_twilio authenticate_twilio
build_inbox build_inbox
setup_webhooks setup_webhooks if @twilio_channel.sms?
rescue Twilio::REST::TwilioError => e rescue Twilio::REST::TwilioError => e
render_could_not_create_error(e.message) render_could_not_create_error(e.message)
rescue StandardError => e rescue StandardError => e
render_could_not_create_error(e.message) render_could_not_create_error(e.message)
end end
end
private private
@@ -26,25 +28,30 @@ class Api::V1::Accounts::Channels::TwilioChannelsController < Api::BaseControlle
::Twilio::WebhookSetupService.new(inbox: @inbox).perform ::Twilio::WebhookSetupService.new(inbox: @inbox).perform
end end
def phone_number
medium == 'sms' ? permitted_params[:phone_number] : "whatsapp:#{permitted_params[:phone_number]}"
end
def medium
permitted_params[:medium]
end
def build_inbox def build_inbox
ActiveRecord::Base.transaction do @twilio_channel = current_account.twilio_sms.create!(
twilio_sms = current_account.twilio_sms.create(
account_sid: permitted_params[:account_sid], account_sid: permitted_params[:account_sid],
auth_token: permitted_params[:auth_token], auth_token: permitted_params[:auth_token],
phone_number: permitted_params[:phone_number] phone_number: phone_number,
medium: medium
) )
@inbox = current_account.inboxes.create( @inbox = current_account.inboxes.create(
name: permitted_params[:name], name: permitted_params[:name],
channel: twilio_sms channel: @twilio_channel
) )
rescue StandardError => e
render_could_not_create_error(e.message)
end
end end
def permitted_params def permitted_params
params.require(:twilio_channel).permit( params.require(:twilio_channel).permit(
:account_id, :phone_number, :account_sid, :auth_token, :name :account_id, :phone_number, :account_sid, :auth_token, :name, :medium
) )
end end
end end

View File

@@ -23,7 +23,9 @@ class Twilio::CallbackController < ApplicationController
:FromZip, :FromZip,
:Body, :Body,
:ToCountry, :ToCountry,
:FromState :FromState,
:MediaUrl0,
:MediaContentType0
) )
end end
end end

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -33,6 +33,14 @@
:style="badgeStyle" :style="badgeStyle"
src="~dashboard/assets/images/twitter-badge.png" src="~dashboard/assets/images/twitter-badge.png"
/> />
<img
v-if="badge === 'Channel::TwilioSms'"
id="badge"
class="source-badge"
:style="badgeStyle"
src="~dashboard/assets/images/channels/whatsapp.png"
/>
</div> </div>
</template> </template>
<script> <script>

View File

@@ -26,6 +26,7 @@
<file-upload <file-upload
v-if="showFileUpload" v-if="showFileUpload"
:size="4096 * 4096" :size="4096 * 4096"
accept="jpg,jpeg,png,mp3,ogg,amr,pdf,mp4"
@input-file="onFileUpload" @input-file="onFileUpload"
> >
<i <i
@@ -142,7 +143,10 @@ export default {
return 10000; return 10000;
}, },
showFileUpload() { showFileUpload() {
return this.channelType === 'Channel::WebWidget'; return (
this.channelType === 'Channel::WebWidget' ||
this.channelType === 'Channel::TwilioSms'
);
}, },
replyButtonLabel() { replyButtonLabel() {
if (this.isPrivate) { if (this.isPrivate) {
@@ -295,6 +299,9 @@ export default {
}, },
onFileUpload(file) { onFileUpload(file) {
if (!file) {
return;
}
this.isUploading.image = true; this.isUploading.image = true;
this.$store this.$store
.dispatch('sendAttachment', [this.currentChat.id, { file: file.file }]) .dispatch('sendAttachment', [this.currentChat.id, { file: file.file }])

View File

@@ -6,10 +6,26 @@
"404": "Diesem Konto sind keine Posteingänge zugeordnet." "404": "Diesem Konto sind keine Posteingänge zugeordnet."
}, },
"CREATE_FLOW": [ "CREATE_FLOW": [
{ "title": "Wählen Sie Kanal", "route": "settings_inbox_new", "body": "Wählen Sie den Anbieter, den Sie in Chatwoot integrieren möchten." }, {
{ "title": "Posteingang erstellen", "route": "settings_inboxes_page_channel", "body": "Authentifizieren Sie Ihr Konto und erstellen Sie einen Posteingang." }, "title": "Wählen Sie Kanal",
{ "title": "Agenten hinzufügen", "route": "settings_inboxes_add_agents", "body": "Fügen Sie dem erstellten Posteingang Agenten hinzu." }, "route": "settings_inbox_new",
{ "title": "Voila!", "route": "settings_inbox_finish", "body": "Sie sind bereit zu gehen!" } "body": "Wählen Sie den Anbieter, den Sie in Chatwoot integrieren möchten."
},
{
"title": "Posteingang erstellen",
"route": "settings_inboxes_page_channel",
"body": "Authentifizieren Sie Ihr Konto und erstellen Sie einen Posteingang."
},
{
"title": "Agenten hinzufügen",
"route": "settings_inboxes_add_agents",
"body": "Fügen Sie dem erstellten Posteingang Agenten hinzu."
},
{
"title": "Voila!",
"route": "settings_inbox_finish",
"body": "Sie sind bereit zu gehen!"
}
], ],
"ADD": { "ADD": {
"FB": { "FB": {
@@ -35,10 +51,9 @@
}, },
"SUBMIT_BUTTON": "Posteingang erstellen" "SUBMIT_BUTTON": "Posteingang erstellen"
}, },
"TWILIO": { "TWILIO": {
"TITLE": "Twilio SMS Channel", "TITLE": "Twilio SMS/Whatsapp Channel",
"DESC": "Integrieren Sie Twilio und unterstützen Sie Ihre Kunden per SMS.", "DESC": "Integrieren Sie Twilio und unterstützen Sie Ihre Kunden per SMS/Whatsapp.",
"ACCOUNT_SID": { "ACCOUNT_SID": {
"LABEL": "Account SID", "LABEL": "Account SID",
"PLACEHOLDER": "Bitte geben Sie Ihre Twilio Account SID ein", "PLACEHOLDER": "Bitte geben Sie Ihre Twilio Account SID ein",

View File

@@ -6,10 +6,26 @@
"404": "There are no inboxes attached to this account." "404": "There are no inboxes attached to this account."
}, },
"CREATE_FLOW": [ "CREATE_FLOW": [
{ "title": "Choose Channel", "route": "settings_inbox_new", "body": "Choose the provider you want to integrate with Chatwoot." }, {
{ "title": "Create Inbox", "route": "settings_inboxes_page_channel", "body": "Authenticate your account and create an inbox." }, "title": "Choose Channel",
{ "title": "Add Agents", "route": "settings_inboxes_add_agents", "body": "Add agents to the created inbox." }, "route": "settings_inbox_new",
{ "title": "Voila!", "route": "settings_inbox_finish", "body": "You are all set to go!" } "body": "Choose the provider you want to integrate with Chatwoot."
},
{
"title": "Create Inbox",
"route": "settings_inboxes_page_channel",
"body": "Authenticate your account and create an inbox."
},
{
"title": "Add Agents",
"route": "settings_inboxes_add_agents",
"body": "Add agents to the created inbox."
},
{
"title": "Voila!",
"route": "settings_inbox_finish",
"body": "You are all set to go!"
}
], ],
"ADD": { "ADD": {
"FB": { "FB": {
@@ -49,13 +65,17 @@
"SUBMIT_BUTTON": "Create inbox" "SUBMIT_BUTTON": "Create inbox"
}, },
"TWILIO": { "TWILIO": {
"TITLE": "Twilio SMS Channel", "TITLE": "Twilio SMS/Whatsapp Channel",
"DESC": "Integrate Twilio and start supporting your customers via SMS.", "DESC": "Integrate Twilio and start supporting your customers via SMS or Whatsapp.",
"ACCOUNT_SID": { "ACCOUNT_SID": {
"LABEL": "Account SID", "LABEL": "Account SID",
"PLACEHOLDER": "Please enter your Twilio Account SID", "PLACEHOLDER": "Please enter your Twilio Account SID",
"ERROR": "This field is required" "ERROR": "This field is required"
}, },
"CHANNEL_TYPE": {
"LABEL": "Channel Type",
"ERROR": "Please select your Channel Type"
},
"AUTH_TOKEN": { "AUTH_TOKEN": {
"LABEL": "Auth Token", "LABEL": "Auth Token",
"PLACEHOLDER": "Please enter your Twilio Auth Token", "PLACEHOLDER": "Please enter your Twilio Auth Token",

View File

@@ -23,6 +23,13 @@
> >
{{ contact.email }} {{ contact.email }}
</a> </a>
<a
v-if="contact.phone_number"
:href="`tel:${contact.phone_number}`"
class="contact--email"
>
{{ contact.phone_number }}
</a>
<div <div
v-if=" v-if="
@@ -211,7 +218,7 @@ export default {
text-transform: capitalize; text-transform: capitalize;
font-weight: $font-weight-bold; font-weight: $font-weight-bold;
font-size: $font-size-medium; font-size: $font-size-default;
} }
.contact--email { .contact--email {

View File

@@ -14,9 +14,22 @@
:placeholder="$t('INBOX_MGMT.ADD.TWILIO.CHANNEL_NAME.PLACEHOLDER')" :placeholder="$t('INBOX_MGMT.ADD.TWILIO.CHANNEL_NAME.PLACEHOLDER')"
@blur="$v.channelName.$touch" @blur="$v.channelName.$touch"
/> />
<span v-if="$v.channelName.$error" class="message"> <span v-if="$v.channelName.$error" class="message">{{
{{ $t('INBOX_MGMT.ADD.TWILIO.CHANNEL_NAME.ERROR') }} $t('INBOX_MGMT.ADD.TWILIO.CHANNEL_NAME.ERROR')
</span> }}</span>
</label>
</div>
<div class="medium-8 columns">
<label :class="{ error: $v.medium.$error }">
{{ $t('INBOX_MGMT.ADD.TWILIO.CHANNEL_TYPE.LABEL') }}
<select v-model="medium">
<option value="sms">SMS</option>
<option value="whatsapp">Whatsapp</option>
</select>
<span v-if="$v.medium.$error" class="message">{{
$t('INBOX_MGMT.ADD.TWILIO.CHANNEL_TYPE.ERROR')
}}</span>
</label> </label>
</div> </div>
@@ -29,9 +42,9 @@
:placeholder="$t('INBOX_MGMT.ADD.TWILIO.PHONE_NUMBER.PLACEHOLDER')" :placeholder="$t('INBOX_MGMT.ADD.TWILIO.PHONE_NUMBER.PLACEHOLDER')"
@blur="$v.phoneNumber.$touch" @blur="$v.phoneNumber.$touch"
/> />
<span v-if="$v.phoneNumber.$error" class="message"> <span v-if="$v.phoneNumber.$error" class="message">{{
{{ $t('INBOX_MGMT.ADD.TWILIO.PHONE_NUMBER.ERROR') }} $t('INBOX_MGMT.ADD.TWILIO.PHONE_NUMBER.ERROR')
</span> }}</span>
</label> </label>
</div> </div>
@@ -44,9 +57,9 @@
:placeholder="$t('INBOX_MGMT.ADD.TWILIO.ACCOUNT_SID.PLACEHOLDER')" :placeholder="$t('INBOX_MGMT.ADD.TWILIO.ACCOUNT_SID.PLACEHOLDER')"
@blur="$v.accountSID.$touch" @blur="$v.accountSID.$touch"
/> />
<span v-if="$v.accountSID.$error" class="message"> <span v-if="$v.accountSID.$error" class="message">{{
{{ $t('INBOX_MGMT.ADD.TWILIO.ACCOUNT_SID.ERROR') }} $t('INBOX_MGMT.ADD.TWILIO.ACCOUNT_SID.ERROR')
</span> }}</span>
</label> </label>
</div> </div>
<div class="medium-8 columns"> <div class="medium-8 columns">
@@ -58,9 +71,9 @@
:placeholder="$t('INBOX_MGMT.ADD.TWILIO.AUTH_TOKEN.PLACEHOLDER')" :placeholder="$t('INBOX_MGMT.ADD.TWILIO.AUTH_TOKEN.PLACEHOLDER')"
@blur="$v.authToken.$touch" @blur="$v.authToken.$touch"
/> />
<span v-if="$v.authToken.$error" class="message"> <span v-if="$v.authToken.$error" class="message">{{
{{ $t('INBOX_MGMT.ADD.TWILIO.AUTH_TOKEN.ERROR') }} $t('INBOX_MGMT.ADD.TWILIO.AUTH_TOKEN.ERROR')
</span> }}</span>
</label> </label>
</div> </div>
@@ -92,6 +105,7 @@ export default {
return { return {
accountSID: '', accountSID: '',
authToken: '', authToken: '',
medium: '',
channelName: '', channelName: '',
phoneNumber: '', phoneNumber: '',
}; };
@@ -106,6 +120,7 @@ export default {
phoneNumber: { required, shouldStartWithPlusSign }, phoneNumber: { required, shouldStartWithPlusSign },
authToken: { required }, authToken: { required },
accountSID: { required }, accountSID: { required },
medium: { required },
}, },
methods: { methods: {
async createChannel() { async createChannel() {
@@ -120,6 +135,7 @@ export default {
{ {
twilio_channel: { twilio_channel: {
name: this.channelName, name: this.channelName,
medium: this.medium,
account_sid: this.accountSID, account_sid: this.accountSID,
auth_token: this.authToken, auth_token: this.authToken,
phone_number: this.phoneNumber, phone_number: this.phoneNumber,

View File

@@ -5,6 +5,7 @@
# id :bigint not null, primary key # id :bigint not null, primary key
# account_sid :string not null # account_sid :string not null
# auth_token :string not null # auth_token :string not null
# medium :integer default("sms")
# phone_number :string not null # phone_number :string not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
@@ -23,6 +24,8 @@ class Channel::TwilioSms < ApplicationRecord
validates :auth_token, presence: true validates :auth_token, presence: true
validates :phone_number, uniqueness: { scope: :account_id }, presence: true validates :phone_number, uniqueness: { scope: :account_id }, presence: true
enum medium: { sms: 0, whatsapp: 1 }
belongs_to :account belongs_to :account
has_one :inbox, as: :channel, dependent: :destroy has_one :inbox, as: :channel, dependent: :destroy

View File

@@ -70,11 +70,12 @@ class Message < ApplicationRecord
has_many :attachments, dependent: :destroy, autosave: true, before_add: :validate_attachments_limit has_many :attachments, dependent: :destroy, autosave: true, before_add: :validate_attachments_limit
after_create :reopen_conversation, after_create :reopen_conversation,
:dispatch_event,
:send_reply,
:execute_message_template_hooks, :execute_message_template_hooks,
:notify_via_mail :notify_via_mail
# we need to wait for the active storage attachments to be available
after_create_commit :dispatch_event, :send_reply
after_update :dispatch_update_event after_update :dispatch_update_event
def channel_token def channel_token

View File

@@ -1,10 +1,12 @@
class Twilio::IncomingMessageService class Twilio::IncomingMessageService
include ::FileTypeHelper
pattr_initialize [:params!] pattr_initialize [:params!]
def perform def perform
set_contact set_contact
set_conversation set_conversation
@conversation.messages.create( @message = @conversation.messages.create(
content: params[:Body], content: params[:Body],
account_id: @inbox.account_id, account_id: @inbox.account_id,
inbox_id: @inbox.id, inbox_id: @inbox.id,
@@ -12,6 +14,7 @@ class Twilio::IncomingMessageService
contact_id: @contact.id, contact_id: @contact.id,
source_id: params[:SmsSid] source_id: params[:SmsSid]
) )
attach_files
end end
private private
@@ -31,6 +34,14 @@ class Twilio::IncomingMessageService
@account ||= inbox.account @account ||= inbox.account
end end
def phone_number
twilio_inbox.sms? ? params[:From] : params[:From].gsub('whatsapp:', '')
end
def formatted_phone_number
TelephoneNumber.parse(phone_number).international_number
end
def set_contact def set_contact
contact_inbox = ::ContactBuilder.new( contact_inbox = ::ContactBuilder.new(
source_id: params[:From], source_id: params[:From],
@@ -61,17 +72,40 @@ class Twilio::IncomingMessageService
def contact_attributes def contact_attributes
{ {
name: params[:From], name: formatted_phone_number,
phone_number: params[:From], phone_number: phone_number,
contact_attributes: additional_attributes additional_attributes: additional_attributes
} }
end end
def additional_attributes def additional_attributes
if twilio_inbox.sms?
{ {
from_zip_code: params[:FromZip], from_zip_code: params[:FromZip],
from_country: params[:FromCountry], from_country: params[:FromCountry],
from_state: params[:FromState] from_state: params[:FromState]
} }
else
{}
end
end
def attach_files
return if params[:MediaUrl0].blank?
file_resource = LocalResource.new(params[:MediaUrl0], params[:MediaContentType0])
attachment = @message.attachments.new(
account_id: @message.account_id,
file_type: file_type(params[:MediaContentType0])
)
attachment.file.attach(
io: file_resource.file,
filename: file_resource.tmp_filename,
content_type: file_resource.encoding
)
@message.save!
end end
end end

View File

@@ -7,11 +7,7 @@ class Twilio::OutgoingMessageService
return if inbox.channel.class.to_s != 'Channel::TwilioSms' return if inbox.channel.class.to_s != 'Channel::TwilioSms'
return unless message.outgoing? return unless message.outgoing?
twilio_message = client.messages.create( twilio_message = client.messages.create(message_params)
body: message.content,
from: channel.phone_number,
to: contact.phone_number
)
message.update!(source_id: twilio_message.sid) message.update!(source_id: twilio_message.sid)
end end
@@ -19,6 +15,21 @@ class Twilio::OutgoingMessageService
delegate :conversation, to: :message delegate :conversation, to: :message
delegate :contact, to: :conversation delegate :contact, to: :conversation
delegate :contact_inbox, to: :conversation
def message_params
params = {
body: message.content,
from: channel.phone_number,
to: contact_inbox.source_id
}
params[:media_url] = attachments if channel.whatsapp? && message.attachments.present?
params
end
def attachments
message.attachments.map(&:file_url)
end
def inbox def inbox
@inbox ||= message.inbox @inbox ||= message.inbox

View File

@@ -0,0 +1,5 @@
class AddMediumToTwilioSms < ActiveRecord::Migration[6.0]
def change
add_column :channel_twilio_sms, :medium, :integer, index: true, default: 0
end
end

View File

@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_04_17_093432) do ActiveRecord::Schema.define(version: 2020_04_29_082655) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@@ -121,6 +121,7 @@ ActiveRecord::Schema.define(version: 2020_04_17_093432) do
t.integer "account_id", null: false t.integer "account_id", null: false
t.datetime "created_at", precision: 6, null: false t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false
t.integer "medium", default: 0
t.index ["account_id", "phone_number"], name: "index_channel_twilio_sms_on_account_id_and_phone_number", unique: true t.index ["account_id", "phone_number"], name: "index_channel_twilio_sms_on_account_id_and_phone_number", unique: true
end end
@@ -287,11 +288,9 @@ ActiveRecord::Schema.define(version: 2020_04_17_093432) do
t.index ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context" t.index ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
t.index ["taggable_id", "taggable_type", "tagger_id", "context"], name: "taggings_idy" t.index ["taggable_id", "taggable_type", "tagger_id", "context"], name: "taggings_idy"
t.index ["taggable_id"], name: "index_taggings_on_taggable_id" t.index ["taggable_id"], name: "index_taggings_on_taggable_id"
t.index ["taggable_type", "taggable_id"], name: "index_taggings_on_taggable_type_and_taggable_id"
t.index ["taggable_type"], name: "index_taggings_on_taggable_type" t.index ["taggable_type"], name: "index_taggings_on_taggable_type"
t.index ["tagger_id", "tagger_type"], name: "index_taggings_on_tagger_id_and_tagger_type" t.index ["tagger_id", "tagger_type"], name: "index_taggings_on_tagger_id_and_tagger_type"
t.index ["tagger_id"], name: "index_taggings_on_tagger_id" t.index ["tagger_id"], name: "index_taggings_on_tagger_id"
t.index ["tagger_type", "tagger_id"], name: "index_taggings_on_tagger_type_and_tagger_id"
end end
create_table "tags", id: :serial, force: :cascade do |t| create_table "tags", id: :serial, force: :cascade do |t|

View File

@@ -1,8 +1,9 @@
class LocalResource class LocalResource
attr_reader :uri attr_reader :uri
def initialize(uri) def initialize(uri, file_type = nil)
@uri = URI(uri) @uri = URI(uri)
@file_type = file_type
end end
def file def file
@@ -23,11 +24,12 @@ class LocalResource
io.read.encoding io.read.encoding
end end
def find_file_type
@file_type ? @file_type.split('/').last : Pathname.new(uri.path).extname
end
def tmp_filename def tmp_filename
[ [Time.now.to_i.to_s, find_file_type].join('.')
Time.now.to_i.to_s,
Pathname.new(uri.path).extname
]
end end
def tmp_folder def tmp_folder

View File

@@ -21,7 +21,8 @@ RSpec.describe '/api/v1/accounts/{account.id}/channels/twilio_channel', type: :r
account_sid: 'sid', account_sid: 'sid',
auth_token: 'token', auth_token: 'token',
phone_number: '+1234567890', phone_number: '+1234567890',
name: 'SMS Channel' name: 'SMS Channel',
medium: 'sms'
} }
} }
end end

View File

@@ -3,6 +3,7 @@ FactoryBot.define do
auth_token { SecureRandom.uuid } auth_token { SecureRandom.uuid }
account_sid { SecureRandom.uuid } account_sid { SecureRandom.uuid }
sequence(:phone_number) { |n| "+123456789#{n}1" } sequence(:phone_number) { |n| "+123456789#{n}1" }
medium { :sms }
inbox inbox
account account
end end

View File

@@ -10,7 +10,9 @@ describe Twilio::OutgoingMessageService do
let!(:account) { create(:account) } let!(:account) { create(:account) }
let!(:widget_inbox) { create(:inbox, account: account) } let!(:widget_inbox) { create(:inbox, account: account) }
let!(:twilio_sms) { create(:channel_twilio_sms, account: account) } let!(:twilio_sms) { create(:channel_twilio_sms, account: account) }
let!(:twilio_whatsapp) { create(:channel_twilio_sms, medium: :whatsapp, account: account) }
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms, account: account) } let!(:twilio_inbox) { create(:inbox, channel: twilio_sms, account: account) }
let!(:twilio_whatsapp_inbox) { create(:inbox, channel: twilio_whatsapp, account: account) }
let!(:contact) { create(:contact, account: account) } let!(:contact) { create(:contact, account: account) }
let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: twilio_inbox) } let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: twilio_inbox) }
let(:conversation) { create(:conversation, contact: contact, inbox: twilio_inbox, contact_inbox: contact_inbox) } let(:conversation) { create(:conversation, contact: contact, inbox: twilio_inbox, contact_inbox: contact_inbox) }
@@ -55,5 +57,18 @@ describe Twilio::OutgoingMessageService do
expect(outgoing_message.reload.source_id).to eq('1234') expect(outgoing_message.reload.source_id).to eq('1234')
end end
end end
it 'if outgoing message has attachment and is for whatsapp' do
# check for message attachment url
allow(messages_double).to receive(:create).with(hash_including(media_url: [anything])).and_return(message_record_double)
allow(message_record_double).to receive(:sid).and_return('1234')
message = build(
:message, message_type: 'outgoing', inbox: twilio_whatsapp_inbox, account: account, 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')
message.save!
end
end end
end end