Fixes #3672 - MessageBird integration

This commit is contained in:
Rolf Schmidt 2021-08-16 16:52:36 +02:00 committed by Thorsten Eckel
parent 0402c755de
commit 1cf8ec8969
15 changed files with 562 additions and 81 deletions

View file

@ -284,6 +284,7 @@ RSpec/ExampleLength:
- 'spec/requests/integration/smime_spec.rb' - 'spec/requests/integration/smime_spec.rb'
- 'spec/requests/integration/telegram_spec.rb' - 'spec/requests/integration/telegram_spec.rb'
- 'spec/requests/integration/twilio_sms_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/integration/user_device_spec.rb'
- 'spec/requests/knowledge_base/answer_attachments_cloning_spec.rb' - 'spec/requests/knowledge_base/answer_attachments_cloning_spec.rb'
- 'spec/requests/links_spec.rb' - 'spec/requests/links_spec.rb'
@ -315,6 +316,7 @@ RSpec/ExpectActual:
- 'spec/requests/integration/monitoring_spec.rb' - 'spec/requests/integration/monitoring_spec.rb'
- 'spec/requests/integration/object_manager_attributes_spec.rb' - 'spec/requests/integration/object_manager_attributes_spec.rb'
- 'spec/requests/integration/twilio_sms_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/integration/user_device_spec.rb'
- 'spec/requests/organization_spec.rb' - 'spec/requests/organization_spec.rb'
- 'spec/requests/ticket/article_attachments_spec.rb' - 'spec/requests/ticket/article_attachments_spec.rb'
@ -565,6 +567,7 @@ RSpec/MultipleExpectations:
- 'spec/requests/integration/smime_spec.rb' - 'spec/requests/integration/smime_spec.rb'
- 'spec/requests/integration/telegram_spec.rb' - 'spec/requests/integration/telegram_spec.rb'
- 'spec/requests/integration/twilio_sms_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/integration/user_device_spec.rb'
- 'spec/requests/links_spec.rb' - 'spec/requests/links_spec.rb'
- 'spec/requests/long_polling_spec.rb' - 'spec/requests/long_polling_spec.rb'

View file

@ -105,8 +105,6 @@ Metrics/AbcSize:
- 'app/models/channel/driver/imap.rb' - 'app/models/channel/driver/imap.rb'
- 'app/models/channel/driver/pop3.rb' - 'app/models/channel/driver/pop3.rb'
- 'app/models/channel/driver/sendmail.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/smtp.rb'
- 'app/models/channel/driver/twitter.rb' - 'app/models/channel/driver/twitter.rb'
- 'app/models/channel/email_build.rb' - 'app/models/channel/email_build.rb'
@ -509,7 +507,6 @@ Metrics/CyclomaticComplexity:
- 'app/models/channel/driver/facebook.rb' - 'app/models/channel/driver/facebook.rb'
- 'app/models/channel/driver/imap.rb' - 'app/models/channel/driver/imap.rb'
- 'app/models/channel/driver/pop3.rb' - 'app/models/channel/driver/pop3.rb'
- 'app/models/channel/driver/sms/twilio.rb'
- 'app/models/channel/driver/smtp.rb' - 'app/models/channel/driver/smtp.rb'
- 'app/models/channel/driver/twitter.rb' - 'app/models/channel/driver/twitter.rb'
- 'app/models/channel/email_build.rb' - 'app/models/channel/email_build.rb'
@ -743,7 +740,6 @@ Metrics/PerceivedComplexity:
- 'app/models/channel/driver/facebook.rb' - 'app/models/channel/driver/facebook.rb'
- 'app/models/channel/driver/imap.rb' - 'app/models/channel/driver/imap.rb'
- 'app/models/channel/driver/pop3.rb' - 'app/models/channel/driver/pop3.rb'
- 'app/models/channel/driver/sms/twilio.rb'
- 'app/models/channel/driver/smtp.rb' - 'app/models/channel/driver/smtp.rb'
- 'app/models/channel/driver/twitter.rb' - 'app/models/channel/driver/twitter.rb'
- 'app/models/channel/email_build.rb' - 'app/models/channel/email_build.rb'
@ -921,6 +917,7 @@ Style/OptionalBooleanParameter:
- 'app/models/channel/driver/facebook.rb' - 'app/models/channel/driver/facebook.rb'
- 'app/models/channel/driver/sendmail.rb' - 'app/models/channel/driver/sendmail.rb'
- 'app/models/channel/driver/sms/massenversand.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/sms/twilio.rb'
- 'app/models/channel/driver/smtp.rb' - 'app/models/channel/driver/smtp.rb'
- 'app/models/channel/driver/telegram.rb' - 'app/models/channel/driver/telegram.rb'

View file

@ -127,6 +127,7 @@ gem 'icalendar-recurrence'
gem 'telephone_number' gem 'telephone_number'
# feature - SMS # feature - SMS
gem 'messagebird-rest'
gem 'twilio-ruby', require: false gem 'twilio-ruby', require: false
# feature - ordering # feature - ordering

View file

@ -313,6 +313,7 @@ GEM
marcel (1.0.1) marcel (1.0.1)
memoizable (0.4.2) memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
messagebird-rest (2.0.0)
method_source (1.0.0) method_source (1.0.0)
mime-types (3.3.1) mime-types (3.3.1)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
@ -692,6 +693,7 @@ DEPENDENCIES
koala koala
libv8 libv8
mail! mail!
messagebird-rest
mime-types mime-types
mini_racer (= 0.2.9) mini_racer (= 0.2.9)
mysql2 mysql2

View file

@ -98,6 +98,8 @@ class ChannelsSmsController < ApplicationController
list = [] list = []
Dir.glob(Rails.root.join('app/models/channel/driver/sms/*.rb')).each do |path| Dir.glob(Rails.root.join('app/models/channel/driver/sms/*.rb')).each do |path|
filename = File.basename(path) filename = File.basename(path)
next if !Channel.driver_class("sms/#{filename}").const_defined?(:NAME)
list.push Channel.driver_class("sms/#{filename}").definition list.push Channel.driver_class("sms/#{filename}").definition
end end
list list

View 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

View file

@ -10,17 +10,7 @@ class Channel::Driver::Sms::Massenversand
Rails.logger.info "Backend sending Massenversand SMS to #{attr[:recipient]}" Rails.logger.info "Backend sending Massenversand SMS to #{attr[:recipient]}"
begin begin
url = build_url(options, attr) send_create(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
true true
rescue => e rescue => e
@ -31,6 +21,19 @@ class Channel::Driver::Sms::Massenversand
end end
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 def self.definition
{ {
name: 'Massenversand', name: 'Massenversand',

View 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

View file

@ -1,6 +1,6 @@
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/ # 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 NAME = 'sms/twilio'.freeze
def fetchable?(_channel) def fetchable?(_channel)
@ -14,15 +14,7 @@ class Channel::Driver::Sms::Twilio
Rails.logger.info "Backend sending Twilio SMS to #{attr[:recipient]}" Rails.logger.info "Backend sending Twilio SMS to #{attr[:recipient]}"
begin begin
if Setting.get('developer_mode') != true send_create(options, attr)
result = api(options).messages.create(
from: options[:sender],
to: attr[:recipient],
body: attr[:message],
)
raise result.error_message if result&.error_code&.positive?
end
true true
rescue => e rescue => e
@ -31,6 +23,18 @@ class Channel::Driver::Sms::Twilio
end end
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) def process(_options, attr, channel)
Rails.logger.info "Receiving SMS frim recipient #{attr[:From]}" 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] return ['application/xml; charset=UTF-8;', Twilio::TwiML::MessagingResponse.new.to_s]
end end
# find sender user = user_by_mobile(attr[:From])
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
UserInfo.current_user_id = user.id UserInfo.current_user_id = user.id
# find ticket process_ticket(attr, channel, user)
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
group = Group.find_by(id: channel.group_id) require 'twilio-ruby' # Only load this gem when it is really used
if !group ['application/xml; charset=UTF-8;', Twilio::TwiML::MessagingResponse.new.to_s]
raise Exceptions::UnprocessableEntity, 'Group is invalid!' end
end
title = attr[:Body] def create_ticket(attr, channel, user)
if title.length > 40 title = cut_title(attr[:Body])
title = "#{title[0, 40]}..." ticket = Ticket.new(
end group_id: channel.group_id,
ticket = Ticket.new( title: title,
group_id: channel.group_id, state_id: Ticket::State.find_by(default_create: true).id,
title: title, priority_id: Ticket::Priority.find_by(default_create: true).id,
state_id: Ticket::State.find_by(default_create: true).id, customer_id: user.id,
priority_id: Ticket::Priority.find_by(default_create: true).id, preferences: {
customer_id: user.id, channel_id: channel.id,
preferences: { sms: {
channel_id: channel.id, AccountSid: attr['AccountSid'],
sms: { From: attr['From'],
AccountSid: attr['AccountSid'], To: attr['To'],
From: attr['From'],
To: attr['To'],
}
} }
) }
ticket.save! )
end ticket.save!
ticket
end
def create_article(attr, channel, ticket)
Ticket::Article.create!( Ticket::Article.create!(
ticket_id: ticket.id, ticket_id: ticket.id,
type: article_type_sms, 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 end
def self.definition def self.definition

6
script/messagebird.rb Normal file
View 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')

View 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

View 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

View 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"
}

View 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"
}

View 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"
}