Fixes #3672 - MessageBird integration
This commit is contained in:
parent
0402c755de
commit
1cf8ec8969
15 changed files with 562 additions and 81 deletions
|
@ -284,6 +284,7 @@ RSpec/ExampleLength:
|
|||
- 'spec/requests/integration/smime_spec.rb'
|
||||
- 'spec/requests/integration/telegram_spec.rb'
|
||||
- 'spec/requests/integration/twilio_sms_spec.rb'
|
||||
- 'spec/requests/integration/message_bird_sms_spec.rb'
|
||||
- 'spec/requests/integration/user_device_spec.rb'
|
||||
- 'spec/requests/knowledge_base/answer_attachments_cloning_spec.rb'
|
||||
- 'spec/requests/links_spec.rb'
|
||||
|
@ -315,6 +316,7 @@ RSpec/ExpectActual:
|
|||
- 'spec/requests/integration/monitoring_spec.rb'
|
||||
- 'spec/requests/integration/object_manager_attributes_spec.rb'
|
||||
- 'spec/requests/integration/twilio_sms_spec.rb'
|
||||
- 'spec/requests/integration/message_bird_sms_spec.rb'
|
||||
- 'spec/requests/integration/user_device_spec.rb'
|
||||
- 'spec/requests/organization_spec.rb'
|
||||
- 'spec/requests/ticket/article_attachments_spec.rb'
|
||||
|
@ -565,6 +567,7 @@ RSpec/MultipleExpectations:
|
|||
- 'spec/requests/integration/smime_spec.rb'
|
||||
- 'spec/requests/integration/telegram_spec.rb'
|
||||
- 'spec/requests/integration/twilio_sms_spec.rb'
|
||||
- 'spec/requests/integration/message_bird_sms_spec.rb'
|
||||
- 'spec/requests/integration/user_device_spec.rb'
|
||||
- 'spec/requests/links_spec.rb'
|
||||
- 'spec/requests/long_polling_spec.rb'
|
||||
|
|
|
@ -105,8 +105,6 @@ Metrics/AbcSize:
|
|||
- 'app/models/channel/driver/imap.rb'
|
||||
- 'app/models/channel/driver/pop3.rb'
|
||||
- 'app/models/channel/driver/sendmail.rb'
|
||||
- 'app/models/channel/driver/sms/massenversand.rb'
|
||||
- 'app/models/channel/driver/sms/twilio.rb'
|
||||
- 'app/models/channel/driver/smtp.rb'
|
||||
- 'app/models/channel/driver/twitter.rb'
|
||||
- 'app/models/channel/email_build.rb'
|
||||
|
@ -509,7 +507,6 @@ Metrics/CyclomaticComplexity:
|
|||
- 'app/models/channel/driver/facebook.rb'
|
||||
- 'app/models/channel/driver/imap.rb'
|
||||
- 'app/models/channel/driver/pop3.rb'
|
||||
- 'app/models/channel/driver/sms/twilio.rb'
|
||||
- 'app/models/channel/driver/smtp.rb'
|
||||
- 'app/models/channel/driver/twitter.rb'
|
||||
- 'app/models/channel/email_build.rb'
|
||||
|
@ -743,7 +740,6 @@ Metrics/PerceivedComplexity:
|
|||
- 'app/models/channel/driver/facebook.rb'
|
||||
- 'app/models/channel/driver/imap.rb'
|
||||
- 'app/models/channel/driver/pop3.rb'
|
||||
- 'app/models/channel/driver/sms/twilio.rb'
|
||||
- 'app/models/channel/driver/smtp.rb'
|
||||
- 'app/models/channel/driver/twitter.rb'
|
||||
- 'app/models/channel/email_build.rb'
|
||||
|
@ -921,6 +917,7 @@ Style/OptionalBooleanParameter:
|
|||
- 'app/models/channel/driver/facebook.rb'
|
||||
- 'app/models/channel/driver/sendmail.rb'
|
||||
- 'app/models/channel/driver/sms/massenversand.rb'
|
||||
- 'app/models/channel/driver/sms/message_bird.rb'
|
||||
- 'app/models/channel/driver/sms/twilio.rb'
|
||||
- 'app/models/channel/driver/smtp.rb'
|
||||
- 'app/models/channel/driver/telegram.rb'
|
||||
|
|
1
Gemfile
1
Gemfile
|
@ -127,6 +127,7 @@ gem 'icalendar-recurrence'
|
|||
gem 'telephone_number'
|
||||
|
||||
# feature - SMS
|
||||
gem 'messagebird-rest'
|
||||
gem 'twilio-ruby', require: false
|
||||
|
||||
# feature - ordering
|
||||
|
|
|
@ -313,6 +313,7 @@ GEM
|
|||
marcel (1.0.1)
|
||||
memoizable (0.4.2)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
messagebird-rest (2.0.0)
|
||||
method_source (1.0.0)
|
||||
mime-types (3.3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
|
@ -692,6 +693,7 @@ DEPENDENCIES
|
|||
koala
|
||||
libv8
|
||||
mail!
|
||||
messagebird-rest
|
||||
mime-types
|
||||
mini_racer (= 0.2.9)
|
||||
mysql2
|
||||
|
|
|
@ -98,6 +98,8 @@ class ChannelsSmsController < ApplicationController
|
|||
list = []
|
||||
Dir.glob(Rails.root.join('app/models/channel/driver/sms/*.rb')).each do |path|
|
||||
filename = File.basename(path)
|
||||
next if !Channel.driver_class("sms/#{filename}").const_defined?(:NAME)
|
||||
|
||||
list.push Channel.driver_class("sms/#{filename}").definition
|
||||
end
|
||||
list
|
||||
|
|
68
app/models/channel/driver/sms/base.rb
Normal file
68
app/models/channel/driver/sms/base.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||
|
||||
class Channel::Driver::Sms::Base
|
||||
def user_by_cti(mobile)
|
||||
_from_comment, preferences = Cti::CallerId.get_comment_preferences(mobile, 'from')
|
||||
user = nil
|
||||
if preferences && preferences['from'] && preferences['from'][0] && preferences['from'][0]['level'] == 'known' && preferences['from'][0]['object'] == 'User'
|
||||
user = User.find_by(id: preferences['from'][0]['o_id'])
|
||||
end
|
||||
user
|
||||
end
|
||||
|
||||
def user_by_mobile(mobile)
|
||||
User.where(mobile: mobile).order(:updated_at).first || user_by_cti(mobile) || User.create!(
|
||||
firstname: mobile,
|
||||
mobile: mobile,
|
||||
)
|
||||
end
|
||||
|
||||
def article_type_sms
|
||||
Ticket::Article::Type.find_by(name: 'sms')
|
||||
end
|
||||
|
||||
def closed_ids
|
||||
Ticket::State.where(name: %w[closed merged removed]).pluck(:id)
|
||||
end
|
||||
|
||||
def ensure_ticket_followup_state(ticket)
|
||||
return if !ticket
|
||||
|
||||
new_state = Ticket::State.find_by(default_create: true)
|
||||
return if ticket.state_id == new_state.id
|
||||
|
||||
ticket.state = Ticket::State.find_by(default_follow_up: true)
|
||||
ticket.save!
|
||||
end
|
||||
|
||||
def find_open_sms_ticket(user)
|
||||
ticket = Ticket.where(customer_id: user.id, create_article_type_id: article_type_sms.id).where.not(state_id: closed_ids).order(:updated_at).first
|
||||
ensure_ticket_followup_state(ticket)
|
||||
ticket
|
||||
end
|
||||
|
||||
def ensure_group!(channel)
|
||||
raise Exceptions::UnprocessableEntity, 'Group needed in channel definition!' if channel.group_id.blank?
|
||||
|
||||
group = Group.find_by(id: channel.group_id)
|
||||
raise Exceptions::UnprocessableEntity, 'Group is invalid!' if !group
|
||||
end
|
||||
|
||||
def cut_title(title)
|
||||
if title.length > 40
|
||||
title = "#{title[0, 40]}..."
|
||||
end
|
||||
title
|
||||
end
|
||||
|
||||
def process_ticket(attr, channel, user)
|
||||
ticket = find_open_sms_ticket(user)
|
||||
if !ticket
|
||||
ensure_group!(channel)
|
||||
|
||||
ticket = create_ticket(attr, channel, user)
|
||||
end
|
||||
|
||||
create_article(attr, channel, ticket)
|
||||
end
|
||||
end
|
|
@ -10,17 +10,7 @@ class Channel::Driver::Sms::Massenversand
|
|||
|
||||
Rails.logger.info "Backend sending Massenversand SMS to #{attr[:recipient]}"
|
||||
begin
|
||||
url = build_url(options, attr)
|
||||
|
||||
if Setting.get('developer_mode') != true
|
||||
response = Faraday.get(url).body
|
||||
|
||||
if !response.match?('OK')
|
||||
message = "Received non-OK response from gateway URL '#{url}'"
|
||||
Rails.logger.error "#{message}: #{response.inspect}"
|
||||
raise message
|
||||
end
|
||||
end
|
||||
send_create(options, attr)
|
||||
|
||||
true
|
||||
rescue => e
|
||||
|
@ -31,6 +21,19 @@ class Channel::Driver::Sms::Massenversand
|
|||
end
|
||||
end
|
||||
|
||||
def send_create(options, attr)
|
||||
url = build_url(options, attr)
|
||||
|
||||
return if Setting.get('developer_mode')
|
||||
|
||||
response = Faraday.get(url).body
|
||||
return if response.match?('OK')
|
||||
|
||||
message = "Received non-OK response from gateway URL '#{url}'"
|
||||
Rails.logger.error "#{message}: #{response.inspect}"
|
||||
raise message
|
||||
end
|
||||
|
||||
def self.definition
|
||||
{
|
||||
name: 'Massenversand',
|
||||
|
|
110
app/models/channel/driver/sms/message_bird.rb
Normal file
110
app/models/channel/driver/sms/message_bird.rb
Normal file
|
@ -0,0 +1,110 @@
|
|||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||
|
||||
class Channel::Driver::Sms::MessageBird < Channel::Driver::Sms::Base
|
||||
NAME = 'sms/message_bird'.freeze
|
||||
|
||||
def fetchable?(_channel)
|
||||
false
|
||||
end
|
||||
|
||||
def send(options, attr, _notification = false)
|
||||
Rails.logger.info "Sending SMS to recipient #{attr[:recipient]}"
|
||||
|
||||
return true if Setting.get('import_mode')
|
||||
|
||||
Rails.logger.info "Backend sending MessageBird SMS to #{attr[:recipient]}"
|
||||
begin
|
||||
send_create(options, attr)
|
||||
true
|
||||
rescue => e
|
||||
Rails.logger.debug { "MessageBird error: #{e.inspect}" }
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
def send_create(options, attr)
|
||||
return if Setting.get('developer_mode')
|
||||
|
||||
api(options).message_create(options[:sender], attr[:recipient], attr[:message])
|
||||
end
|
||||
|
||||
def process(_options, attr, channel)
|
||||
Rails.logger.info "Receiving SMS frim recipient #{attr['originator']}"
|
||||
|
||||
# prevent already created articles
|
||||
if attr['message_id'].present? && Ticket::Article.exists?(message_id: attr['message_id'])
|
||||
return [:json, {}]
|
||||
end
|
||||
|
||||
# find sender
|
||||
user = user_by_mobile(attr['originator'])
|
||||
UserInfo.current_user_id = user.id
|
||||
|
||||
process_ticket(attr, channel, user)
|
||||
|
||||
[:json, {}]
|
||||
end
|
||||
|
||||
def create_ticket(attr, channel, user)
|
||||
title = cut_title(attr['incomingMessage'])
|
||||
ticket = Ticket.new(
|
||||
group_id: channel.group_id,
|
||||
title: title,
|
||||
state_id: Ticket::State.find_by(default_create: true).id,
|
||||
priority_id: Ticket::Priority.find_by(default_create: true).id,
|
||||
customer_id: user.id,
|
||||
preferences: {
|
||||
channel_id: channel.id,
|
||||
sms: {
|
||||
originator: attr['originator'],
|
||||
recipient: attr['recipient'],
|
||||
}
|
||||
}
|
||||
)
|
||||
ticket.save!
|
||||
ticket
|
||||
end
|
||||
|
||||
def create_article(attr, channel, ticket)
|
||||
Ticket::Article.create!(
|
||||
ticket_id: ticket.id,
|
||||
type: article_type_sms,
|
||||
sender: Ticket::Article::Sender.find_by(name: 'Customer'),
|
||||
body: attr['incomingMessage'],
|
||||
from: attr['originator'],
|
||||
to: attr['recipient'],
|
||||
message_id: attr['message_id'],
|
||||
content_type: 'text/plain',
|
||||
preferences: {
|
||||
channel_id: channel.id,
|
||||
sms: {
|
||||
reference: attr['reference'],
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def self.definition
|
||||
{
|
||||
name: 'message_bird',
|
||||
adapter: 'sms/message_bird',
|
||||
account: [
|
||||
{ name: 'options::webhook_token', display: 'Webhook Token', tag: 'input', type: 'text', limit: 200, null: false, default: Digest::MD5.hexdigest(rand(999_999_999_999).to_s), disabled: true, readonly: true },
|
||||
{ name: 'options::token', display: 'Token', tag: 'input', type: 'text', limit: 255, null: false },
|
||||
{ name: 'options::sender', display: 'Sender', tag: 'input', type: 'text', limit: 200, null: false, placeholder: '+491710000000' },
|
||||
{ name: 'group_id', display: 'Destination Group', tag: 'select', null: false, relation: 'Group', nulloption: true, filter: { active: true } },
|
||||
],
|
||||
notification: [
|
||||
{ name: 'options::token', display: 'Token', tag: 'input', type: 'text', limit: 255, null: false },
|
||||
{ name: 'options::sender', display: 'Sender', tag: 'input', type: 'text', limit: 200, null: false, placeholder: '+491710000000' },
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def api(options)
|
||||
require 'messagebird'
|
||||
@api ||= ::MessageBird::Client.new(options[:token])
|
||||
end
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||
|
||||
class Channel::Driver::Sms::Twilio
|
||||
class Channel::Driver::Sms::Twilio < Channel::Driver::Sms::Base
|
||||
NAME = 'sms/twilio'.freeze
|
||||
|
||||
def fetchable?(_channel)
|
||||
|
@ -14,15 +14,7 @@ class Channel::Driver::Sms::Twilio
|
|||
|
||||
Rails.logger.info "Backend sending Twilio SMS to #{attr[:recipient]}"
|
||||
begin
|
||||
if Setting.get('developer_mode') != true
|
||||
result = api(options).messages.create(
|
||||
from: options[:sender],
|
||||
to: attr[:recipient],
|
||||
body: attr[:message],
|
||||
)
|
||||
|
||||
raise result.error_message if result&.error_code&.positive?
|
||||
end
|
||||
send_create(options, attr)
|
||||
|
||||
true
|
||||
rescue => e
|
||||
|
@ -31,6 +23,18 @@ class Channel::Driver::Sms::Twilio
|
|||
end
|
||||
end
|
||||
|
||||
def send_create(options, attr)
|
||||
return if Setting.get('developer_mode')
|
||||
|
||||
result = api(options).messages.create(
|
||||
from: options[:sender],
|
||||
to: attr[:recipient],
|
||||
body: attr[:message],
|
||||
)
|
||||
|
||||
raise result.error_message if result&.error_code&.positive?
|
||||
end
|
||||
|
||||
def process(_options, attr, channel)
|
||||
Rails.logger.info "Receiving SMS frim recipient #{attr[:From]}"
|
||||
|
||||
|
@ -40,65 +44,37 @@ class Channel::Driver::Sms::Twilio
|
|||
return ['application/xml; charset=UTF-8;', Twilio::TwiML::MessagingResponse.new.to_s]
|
||||
end
|
||||
|
||||
# find sender
|
||||
user = User.where(mobile: attr[:From]).order(:updated_at).first
|
||||
if !user
|
||||
_from_comment, preferences = Cti::CallerId.get_comment_preferences(attr[:From], 'from')
|
||||
if preferences && preferences['from'] && preferences['from'][0] && preferences['from'][0]['level'] == 'known' && preferences['from'][0]['object'] == 'User'
|
||||
user = User.find_by(id: preferences['from'][0]['o_id'])
|
||||
end
|
||||
end
|
||||
if !user
|
||||
user = User.create!(
|
||||
firstname: attr[:From],
|
||||
mobile: attr[:From],
|
||||
)
|
||||
end
|
||||
|
||||
user = user_by_mobile(attr[:From])
|
||||
UserInfo.current_user_id = user.id
|
||||
|
||||
# find ticket
|
||||
article_type_sms = Ticket::Article::Type.find_by(name: 'sms')
|
||||
state_ids = Ticket::State.where(name: %w[closed merged removed]).pluck(:id)
|
||||
ticket = Ticket.where(customer_id: user.id, create_article_type_id: article_type_sms.id).where.not(state_id: state_ids).order(:updated_at).first
|
||||
if ticket
|
||||
new_state = Ticket::State.find_by(default_create: true)
|
||||
if ticket.state_id != new_state.id
|
||||
ticket.state = Ticket::State.find_by(default_follow_up: true)
|
||||
ticket.save!
|
||||
end
|
||||
else
|
||||
if channel.group_id.blank?
|
||||
raise Exceptions::UnprocessableEntity, 'Group needed in channel definition!'
|
||||
end
|
||||
process_ticket(attr, channel, user)
|
||||
|
||||
group = Group.find_by(id: channel.group_id)
|
||||
if !group
|
||||
raise Exceptions::UnprocessableEntity, 'Group is invalid!'
|
||||
end
|
||||
require 'twilio-ruby' # Only load this gem when it is really used
|
||||
['application/xml; charset=UTF-8;', Twilio::TwiML::MessagingResponse.new.to_s]
|
||||
end
|
||||
|
||||
title = attr[:Body]
|
||||
if title.length > 40
|
||||
title = "#{title[0, 40]}..."
|
||||
end
|
||||
ticket = Ticket.new(
|
||||
group_id: channel.group_id,
|
||||
title: title,
|
||||
state_id: Ticket::State.find_by(default_create: true).id,
|
||||
priority_id: Ticket::Priority.find_by(default_create: true).id,
|
||||
customer_id: user.id,
|
||||
preferences: {
|
||||
channel_id: channel.id,
|
||||
sms: {
|
||||
AccountSid: attr['AccountSid'],
|
||||
From: attr['From'],
|
||||
To: attr['To'],
|
||||
}
|
||||
def create_ticket(attr, channel, user)
|
||||
title = cut_title(attr[:Body])
|
||||
ticket = Ticket.new(
|
||||
group_id: channel.group_id,
|
||||
title: title,
|
||||
state_id: Ticket::State.find_by(default_create: true).id,
|
||||
priority_id: Ticket::Priority.find_by(default_create: true).id,
|
||||
customer_id: user.id,
|
||||
preferences: {
|
||||
channel_id: channel.id,
|
||||
sms: {
|
||||
AccountSid: attr['AccountSid'],
|
||||
From: attr['From'],
|
||||
To: attr['To'],
|
||||
}
|
||||
)
|
||||
ticket.save!
|
||||
end
|
||||
}
|
||||
)
|
||||
ticket.save!
|
||||
ticket
|
||||
end
|
||||
|
||||
def create_article(attr, channel, ticket)
|
||||
Ticket::Article.create!(
|
||||
ticket_id: ticket.id,
|
||||
type: article_type_sms,
|
||||
|
@ -117,9 +93,6 @@ class Channel::Driver::Sms::Twilio
|
|||
}
|
||||
}
|
||||
)
|
||||
|
||||
require 'twilio-ruby' # Only load this gem when it is really used
|
||||
['application/xml; charset=UTF-8;', Twilio::TwiML::MessagingResponse.new.to_s]
|
||||
end
|
||||
|
||||
def self.definition
|
||||
|
|
6
script/messagebird.rb
Normal file
6
script/messagebird.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||
|
||||
require 'messagebird'
|
||||
|
||||
client = MessageBird::Client.new('OSR2zUFd14Nd5snb8zmEQYoBx')
|
||||
client.message_create('Zammad GmbH', '+4917670333748', 'This is a test messageNEW', reference: 'Foobar')
|
68
spec/models/channel/driver/sms/message_bird_spec.rb
Normal file
68
spec/models/channel/driver/sms/message_bird_spec.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||
|
||||
require 'rails_helper'
|
||||
require 'messagebird'
|
||||
|
||||
RSpec.describe Channel::Driver::Sms::MessageBird do
|
||||
it 'passes' do
|
||||
channel = create_channel
|
||||
|
||||
stub_request(:post, url_to_mock)
|
||||
.to_return(body: mocked_response_success)
|
||||
|
||||
api = channel.driver_instance.new
|
||||
expect(api.send(channel.options, { recipient: '+37060010000', message: message_body })).to be true
|
||||
end
|
||||
|
||||
it 'fails' do
|
||||
channel = create_channel
|
||||
|
||||
stub_request(:post, url_to_mock)
|
||||
.to_return(status: 400, body: mocked_response_failure)
|
||||
|
||||
api = channel.driver_instance.new
|
||||
|
||||
expect { api.send(channel.options, { recipient: 'asd', message: message_body }) }.to raise_exception(::MessageBird::ServerException)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_channel
|
||||
FactoryBot.create(:channel,
|
||||
options: {
|
||||
adapter: 'sms/message_bird',
|
||||
sender: sender_number,
|
||||
token: token
|
||||
},
|
||||
created_by_id: 1,
|
||||
updated_by_id: 1)
|
||||
end
|
||||
|
||||
# api parameters
|
||||
|
||||
def url_to_mock
|
||||
'https://rest.messagebird.com/messages'
|
||||
end
|
||||
|
||||
def message_body
|
||||
'Test'
|
||||
end
|
||||
|
||||
def sender_number
|
||||
'+15005550006'
|
||||
end
|
||||
|
||||
def token
|
||||
'2345r4erfdvc4wedxv3efds'
|
||||
end
|
||||
|
||||
# mocked responses
|
||||
|
||||
def mocked_response_success
|
||||
'{"id":"1e8cc35873d14fe4ab18bd97a412121","href":"https://rest.messagebird.com/messages/1e8cc35873d14fe4ab18bd121212f971a","direction":"mt","type":"sms","originator":"Zammad GmbH","body":"This is a test messageNEW","reference":"Foobar","validity":null,"gateway":10,"typeDetails":{},"datacoding":"plain","mclass":1,"scheduledDatetime":null,"createdDatetime":"2021-07-22T13:25:03+00:00","recipients":{"totalCount":1,"totalSentCount":1,"totalDeliveredCount":0,"totalDeliveryFailedCount":0,"items":[{"recipient":491234,"status":"sent","statusDatetime":"2021-07-22T13:25:03+00:00","messagePartCount":1}]}}'
|
||||
end
|
||||
|
||||
def mocked_response_failure
|
||||
'{"errors":[{"code":9,"description":"no (correct) recipients found","parameter":"recipient"}]}'
|
||||
end
|
||||
end
|
188
spec/requests/integration/message_bird_sms_spec.rb
Normal file
188
spec/requests/integration/message_bird_sms_spec.rb
Normal file
|
@ -0,0 +1,188 @@
|
|||
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Message Bird SMS', type: :request do
|
||||
|
||||
describe 'request handling' do
|
||||
|
||||
let(:agent) do
|
||||
create(:agent, groups: Group.all)
|
||||
end
|
||||
|
||||
it 'does basic call' do
|
||||
|
||||
# configure twilio channel
|
||||
group_id = Group.find_by(name: 'Users').id
|
||||
|
||||
UserInfo.current_user_id = 1
|
||||
channel = create(
|
||||
:channel,
|
||||
area: 'Sms::Account',
|
||||
options: {
|
||||
adapter: 'sms/message_bird',
|
||||
webhook_token: 'f409460e50f76d331fdac8ba7b7963b6',
|
||||
token: '223',
|
||||
sender: '333',
|
||||
},
|
||||
group_id: nil,
|
||||
)
|
||||
|
||||
# process inbound sms
|
||||
post '/api/v1/sms_webhook', params: read_message('inbound_sms1'), as: :json
|
||||
expect(response).to have_http_status(:not_found)
|
||||
|
||||
post '/api/v1/sms_webhook/not_existing', params: read_message('inbound_sms1'), as: :json
|
||||
expect(response).to have_http_status(:not_found)
|
||||
|
||||
post '/api/v1/sms_webhook/f409460e50f76d331fdac8ba7b7963b6', params: read_message('inbound_sms1'), as: :json
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(json_response['error']).to eq('Can\'t use Channel::Driver::Sms::MessageBird: #<Exceptions::UnprocessableEntity: Group needed in channel definition!>')
|
||||
|
||||
channel.group_id = Group.first.id
|
||||
channel.save!
|
||||
|
||||
post '/api/v1/sms_webhook/f409460e50f76d331fdac8ba7b7963b6', params: read_message('inbound_sms1'), as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
ticket = Ticket.last
|
||||
article = Ticket::Article.last
|
||||
customer = User.last
|
||||
expect(ticket.articles.count).to eq(1)
|
||||
expect(ticket.title).to eq('Ldfhxhcuffufuf. Fifififig. Fifififiif F...')
|
||||
expect(ticket.state.name).to eq('new')
|
||||
expect(ticket.group_id).to eq(group_id)
|
||||
expect(ticket.customer_id).to eq(customer.id)
|
||||
expect(ticket.created_by_id).to eq(customer.id)
|
||||
expect(article.from).to eq('+491710000000')
|
||||
expect(article.to).to eq('+4915700000000')
|
||||
expect(article.cc).to be_nil
|
||||
expect(article.subject).to be_nil
|
||||
expect(article.body).to eq('Ldfhxhcuffufuf. Fifififig. Fifififiif Fifififiif Fifififiif Fifififiif Fifififiif')
|
||||
expect(article.created_by_id).to eq(customer.id)
|
||||
expect(article.sender.name).to eq('Customer')
|
||||
expect(article.type.name).to eq('sms')
|
||||
|
||||
post '/api/v1/sms_webhook/f409460e50f76d331fdac8ba7b7963b6', params: read_message('inbound_sms2'), as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
ticket.reload
|
||||
expect(ticket.articles.count).to eq(2)
|
||||
expect(ticket.state.name).to eq('new')
|
||||
|
||||
article = Ticket::Article.last
|
||||
expect(article.from).to eq('+491710000000')
|
||||
expect(article.to).to eq('+4915700000000')
|
||||
expect(article.cc).to be_nil
|
||||
expect(article.subject).to be_nil
|
||||
expect(article.body).to eq('Follow-up')
|
||||
expect(article.sender.name).to eq('Customer')
|
||||
expect(article.type.name).to eq('sms')
|
||||
expect(article.created_by_id).to eq(customer.id)
|
||||
|
||||
# check duplicate callbacks
|
||||
post '/api/v1/sms_webhook/f409460e50f76d331fdac8ba7b7963b6', params: read_message('inbound_sms2'), as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
ticket.reload
|
||||
expect(ticket.articles.count).to eq(2)
|
||||
expect(ticket.state.name).to eq('new')
|
||||
expect(article.id).to eq(Ticket::Article.last.id)
|
||||
|
||||
# new ticket need to be create
|
||||
ticket.state = Ticket::State.find_by(name: 'closed')
|
||||
ticket.save!
|
||||
|
||||
post '/api/v1/sms_webhook/f409460e50f76d331fdac8ba7b7963b6', params: read_message('inbound_sms3'), as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
ticket.reload
|
||||
expect(ticket.articles.count).to eq(2)
|
||||
expect(ticket.id).not_to eq(Ticket.last.id)
|
||||
expect(ticket.state.name).to eq('closed')
|
||||
|
||||
ticket = Ticket.last
|
||||
article = Ticket::Article.last
|
||||
customer = User.last
|
||||
expect(ticket.articles.count).to eq(1)
|
||||
expect(ticket.title).to eq('new 2')
|
||||
expect(ticket.group_id).to eq(group_id)
|
||||
expect(ticket.customer_id).to eq(customer.id)
|
||||
expect(ticket.created_by_id).to eq(customer.id)
|
||||
expect(article.from).to eq('+491710000000')
|
||||
expect(article.to).to eq('+4915700000000')
|
||||
expect(article.cc).to be_nil
|
||||
expect(article.subject).to be_nil
|
||||
expect(article.body).to eq('new 2')
|
||||
expect(article.created_by_id).to eq(customer.id)
|
||||
expect(article.sender.name).to eq('Customer')
|
||||
expect(article.type.name).to eq('sms')
|
||||
|
||||
# reply by agent
|
||||
params = {
|
||||
ticket_id: ticket.id,
|
||||
body: 'some test',
|
||||
type: 'sms',
|
||||
}
|
||||
authenticated_as(agent)
|
||||
post '/api/v1/ticket_articles', params: params, as: :json
|
||||
expect(response).to have_http_status(:created)
|
||||
expect(json_response).to be_a_kind_of(Hash)
|
||||
expect(json_response['subject']).to be_nil
|
||||
expect(json_response['body']).to eq('some test')
|
||||
expect(json_response['content_type']).to eq('text/plain')
|
||||
expect(json_response['updated_by_id']).to eq(agent.id)
|
||||
expect(json_response['created_by_id']).to eq(agent.id)
|
||||
|
||||
stub_request(:post, 'https://rest.messagebird.com/messages')
|
||||
.to_return(status: 200, body: mocked_response_success, headers: {})
|
||||
|
||||
expect(article.preferences[:delivery_retry]).to be_nil
|
||||
expect(article.preferences[:delivery_status]).to be_nil
|
||||
|
||||
TransactionDispatcher.commit
|
||||
Scheduler.worker(true)
|
||||
|
||||
article = Ticket::Article.find(json_response['id'])
|
||||
expect(article.preferences[:delivery_retry]).to eq(1)
|
||||
expect(article.preferences[:delivery_status]).to eq('success')
|
||||
end
|
||||
|
||||
it 'does customer based on already existing mobile attibute' do
|
||||
|
||||
customer = create(
|
||||
:customer,
|
||||
email: 'me@example.com',
|
||||
mobile: '01710000000',
|
||||
)
|
||||
TransactionDispatcher.commit
|
||||
Scheduler.worker(true)
|
||||
|
||||
UserInfo.current_user_id = 1
|
||||
create(
|
||||
:channel,
|
||||
area: 'Sms::Account',
|
||||
options: {
|
||||
adapter: 'sms/twilio',
|
||||
webhook_token: 'f409460e50f76d331fdac8ba7b7963b6',
|
||||
account_id: '111',
|
||||
token: '223',
|
||||
sender: '333',
|
||||
},
|
||||
)
|
||||
|
||||
post '/api/v1/sms_webhook/f409460e50f76d331fdac8ba7b7963b6', params: read_message('inbound_sms1'), as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
expect(customer.id).to eq(User.last.id)
|
||||
end
|
||||
|
||||
def read_message(file)
|
||||
JSON.parse(File.read(Rails.root.join('test', 'data', 'message_bird', "#{file}.json")))
|
||||
end
|
||||
|
||||
def mocked_response_success
|
||||
'{"id":"1e8cc35873d14fe4ab18bd97a412121","href":"https://rest.messagebird.com/messages/1e8cc35873d14fe4ab18bd121212f971a","direction":"mt","type":"sms","originator":"Zammad GmbH","body":"some test","reference":"Foobar","validity":null,"gateway":10,"typeDetails":{},"datacoding":"plain","mclass":1,"scheduledDatetime":null,"createdDatetime":"2021-07-22T13:25:03+00:00","recipients":{"totalCount":1,"totalSentCount":1,"totalDeliveredCount":0,"totalDeliveryFailedCount":0,"items":[{"recipient":491234,"status":"sent","statusDatetime":"2021-07-22T13:25:03+00:00","messagePartCount":1}]}}'
|
||||
end
|
||||
end
|
||||
end
|
20
test/data/message_bird/inbound_sms1.json
Normal file
20
test/data/message_bird/inbound_sms1.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"body":"Ldfhxhcuffufuf. Fifififig. Fifififiif Fifififiif Fifififiif Fifififiif Fifififiif",
|
||||
"createdDatetime":"",
|
||||
"currentTime":"2021-07-22T10:06:00+02:00",
|
||||
"date":"0",
|
||||
"date_utc":"0",
|
||||
"id":"",
|
||||
"incomingMessage":"Ldfhxhcuffufuf. Fifififig. Fifififiif Fifififiif Fifififiif Fifififiif Fifififiif",
|
||||
"message":"",
|
||||
"message_id":"",
|
||||
"originator":"+491710000000",
|
||||
"payload":"Ldfhxhcuffufuf. Fifififig. Fifififiif Fifififiif Fifififiif Fifififiif Fifififiif",
|
||||
"receiver":"",
|
||||
"recipient":"+4915700000000",
|
||||
"reference":"",
|
||||
"sender":"",
|
||||
"controller":"channels_sms",
|
||||
"action":"webhook",
|
||||
"token":"xxx"
|
||||
}
|
20
test/data/message_bird/inbound_sms2.json
Normal file
20
test/data/message_bird/inbound_sms2.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"body":"Follow-up",
|
||||
"createdDatetime":"",
|
||||
"currentTime":"2021-07-22T10:06:00+02:00",
|
||||
"date":"0",
|
||||
"date_utc":"0",
|
||||
"id":"",
|
||||
"incomingMessage":"Follow-up",
|
||||
"message":"",
|
||||
"message_id":"123",
|
||||
"originator":"+491710000000",
|
||||
"payload":"Follow-up",
|
||||
"receiver":"",
|
||||
"recipient":"+4915700000000",
|
||||
"reference":"",
|
||||
"sender":"",
|
||||
"controller":"channels_sms",
|
||||
"action":"webhook",
|
||||
"token":"xxx"
|
||||
}
|
20
test/data/message_bird/inbound_sms3.json
Normal file
20
test/data/message_bird/inbound_sms3.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"body":"new 2",
|
||||
"createdDatetime":"",
|
||||
"currentTime":"2021-07-22T10:06:00+02:00",
|
||||
"date":"0",
|
||||
"date_utc":"0",
|
||||
"id":"",
|
||||
"incomingMessage":"new 2",
|
||||
"message":"",
|
||||
"message_id":"",
|
||||
"originator":"+491710000000",
|
||||
"payload":"new 2",
|
||||
"receiver":"",
|
||||
"recipient":"+4915700000000",
|
||||
"reference":"",
|
||||
"sender":"",
|
||||
"controller":"channels_sms",
|
||||
"action":"webhook",
|
||||
"token":"xxx"
|
||||
}
|
Loading…
Reference in a new issue