Initial Placetel integration.

This commit is contained in:
Martin Edenhofer 2018-10-15 11:47:59 +02:00
parent 402b28b3e7
commit 271102057d
12 changed files with 1085 additions and 4 deletions

View file

@ -0,0 +1,158 @@
class Index extends App.ControllerIntegrationBase
featureIntegration: 'placetel_integration'
featureName: 'Placetel'
featureConfig: 'placetel_config'
description: [
['This service shows you contacts of incoming calls and a caller list in realtime.']
['Also caller id of outbound calls can be changed.']
]
events:
'click .js-select': 'selectAll'
'change .js-switch input': 'switch'
render: =>
super
new Form(
el: @$('.js-form')
)
new App.HttpLog(
el: @$('.js-log')
facility: 'placetel'
)
class Form extends App.Controller
events:
'submit form': 'update'
'click .js-inboundBlockCallerId .js-add': 'addInboundBlockCallerId'
'click .js-outboundRouting .js-add': 'addOutboundRouting'
'click .js-inboundBlockCallerId .js-remove': 'removeInboundBlockCallerId'
'click .js-outboundRouting .js-remove': 'removeOutboundRouting'
constructor: ->
super
@render()
currentConfig: ->
config = App.Setting.get('placetel_config')
if !config.outbound
config.outbound = {}
if !config.outbound.routing_table
config.outbound.routing_table = []
if !config.inbound
config.inbound = {}
if !config.inbound.block_caller_ids
config.inbound.block_caller_ids = []
config
setConfig: (value) ->
App.Setting.set('placetel_config', value, {notify: true})
render: =>
@config = @currentConfig()
@html App.view('integration/placetel')(
config: @config
placetel_token: App.Setting.get('placetel_token')
)
updateCurrentConfig: =>
config = @config
cleanupInput = @cleanupInput
# default caller_id
default_caller_id = @$('input[name=default_caller_id]').val()
config.outbound.default_caller_id = cleanupInput(default_caller_id)
# routing table
config.outbound.routing_table = []
@$('.js-outboundRouting .js-row').each(->
dest = cleanupInput($(@).find('input[name="dest"]').val())
caller_id = cleanupInput($(@).find('input[name="caller_id"]').val())
note = $(@).find('input[name="note"]').val()
config.outbound.routing_table.push {
dest: dest
caller_id: caller_id
note: note
}
)
# blocked caller ids
config.inbound.block_caller_ids = []
@$('.js-inboundBlockCallerId .js-row').each(->
caller_id = $(@).find('input[name="caller_id"]').val()
note = $(@).find('input[name="note"]').val()
config.inbound.block_caller_ids.push {
caller_id: cleanupInput(caller_id)
note: note
}
)
@config = config
update: (e) =>
e.preventDefault()
@updateCurrentConfig()
@setConfig(@config)
cleanupInput: (value) ->
return value if !value
value.replace(/\s/g, '').trim()
addInboundBlockCallerId: (e) =>
e.preventDefault()
@updateCurrentConfig()
element = $(e.currentTarget).closest('tr')
caller_id = element.find('input[name="caller_id"]').val()
note = element.find('input[name="note"]').val()
return if _.isEmpty(caller_id) || _.isEmpty(note)
@config.inbound.block_caller_ids.push {
caller_id: @cleanupInput(caller_id)
note: note
}
@render()
addOutboundRouting: (e) =>
e.preventDefault()
@updateCurrentConfig()
element = $(e.currentTarget).closest('tr')
dest = @cleanupInput(element.find('input[name="dest"]').val())
caller_id = @cleanupInput(element.find('input[name="caller_id"]').val())
note = element.find('input[name="note"]').val()
return if _.isEmpty(caller_id) || _.isEmpty(dest) || _.isEmpty(note)
@config.outbound.routing_table.push {
dest: dest
caller_id: caller_id
note: note
}
@render()
removeInboundBlockCallerId: (e) =>
e.preventDefault()
@updateCurrentConfig()
element = $(e.currentTarget).closest('tr')
element.remove()
@updateCurrentConfig()
removeOutboundRouting: (e) =>
e.preventDefault()
@updateCurrentConfig()
element = $(e.currentTarget).closest('tr')
element.remove()
@updateCurrentConfig()
class State
@current: ->
App.Setting.get('placetel_integration')
App.Config.set(
'IntegrationPlacetel'
{
name: 'Placetel'
target: '#system/integration/placetel'
description: 'VoIP service provider with realtime push.'
controller: Index
state: State
}
'NavBarIntegrations'
)

View file

@ -99,6 +99,7 @@ class App.CTI extends App.Controller
featureActive: => featureActive: =>
return true if @Config.get('sipgate_integration') return true if @Config.get('sipgate_integration')
return true if @Config.get('cti_integration') return true if @Config.get('cti_integration')
return true if @Config.get('placetel_integration')
false false
render: -> render: ->

View file

@ -40,7 +40,7 @@
<% end %> <% end %>
<tr> <tr>
<td class="settings-list-control-cell"><input name="caller_id" value="" placeholder="4930609854189" class="form-control form-control--small js-summary"> <td class="settings-list-control-cell"><input name="caller_id" value="" placeholder="4930609854189" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="note" value="" placeholder="<%- @Ti('my onw note') %>" class="form-control form-control--small js-summary"> <td class="settings-list-control-cell"><input name="note" value="" placeholder="<%- @Ti('my own note') %>" class="form-control form-control--small js-summary">
<td class="settings-list-row-control"><div class="btn btn--text btn--create js-add"><%- @Icon('plus-small') %> <%- @T('Add') %></div> <td class="settings-list-row-control"><div class="btn btn--text btn--create js-add"><%- @Icon('plus-small') %> <%- @T('Add') %></div>
</tbody> </tbody>
</table> </table>
@ -70,7 +70,7 @@
<tr> <tr>
<td class="settings-list-control-cell"><input name="dest" value="" placeholder="49* or 3230123456789" class="form-control form-control--small js-summary"> <td class="settings-list-control-cell"><input name="dest" value="" placeholder="49* or 3230123456789" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="caller_id" value="" placeholder="4930609854189" class="form-control form-control--small js-summary"> <td class="settings-list-control-cell"><input name="caller_id" value="" placeholder="4930609854189" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="note" value="" placeholder="<%- @Ti('my onw note') %>" class="form-control form-control--small js-summary"> <td class="settings-list-control-cell"><input name="note" value="" placeholder="<%- @Ti('my own note') %>" class="form-control form-control--small js-summary">
<td class="settings-list-row-control"><div class="btn btn--text btn--create js-add"><%- @Icon('plus-small') %> <%- @T('Add') %></div> <td class="settings-list-row-control"><div class="btn btn--text btn--create js-add"><%- @Icon('plus-small') %> <%- @T('Add') %></div>
</tbody> </tbody>
</table> </table>

View file

@ -0,0 +1,97 @@
<form>
<h2>Placetel <%- @T('Settings') %></h2>
<p><%- @T('You need to configure the Zammad endpoints in the %s', 'Placetel') %>:<p>
<div class="settings-entry">
<table class="settings-list" style="width: 100%;">
<thead>
<tr>
<th width="20%"><%- @T('Type') %>
<th width="80%"><%- @T('URL') %>
</thead>
<tbody>
<tr>
<td class="settings-list-row-control"><%- @T('Endpoint') %>
<td class="settings-list-control-cell"><input type="url" class="form-control form-control--small js-select" readonly value="<%- @C('http_type') %>://<%- @C('fqdn') %>/api/v1/placetel/<%= @placetel_token %>">
</tbody>
</table>
</div>
<h2><%- @T('Inbound') %></h2>
<p><%- @T('Blocked caller ids based on sender caller id.') %>
<div class="settings-entry">
<table class="settings-list js-inboundBlockCallerId" style="width: 100%;">
<thead>
<tr>
<th width="50%"><%- @T('Caller id to block') %>
<th width="40%"><%- @T('Note') %>
<th width="10%"><%- @T('Action') %>
</thead>
<tbody>
<% for row in @config.inbound.block_caller_ids: %>
<tr class="js-row">
<td class="settings-list-control-cell"><input name="caller_id" value="<%= row.caller_id %>" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="note" value="<%= row.note %>" class="form-control form-control--small js-summary">
<td class="settings-list-row-control"><div class="btn btn--text js-remove"><%- @Icon('trash') %> <%- @T('Remove') %></div>
<% end %>
<tr>
<td class="settings-list-control-cell"><input name="caller_id" value="" placeholder="4930609854189" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="note" value="" placeholder="<%- @Ti('my own note') %>" class="form-control form-control--small js-summary">
<td class="settings-list-row-control"><div class="btn btn--text btn--create js-add"><%- @Icon('plus-small') %> <%- @T('Add') %></div>
</tbody>
</table>
</div>
<!-- disabled until api is supporting it
<h2><%- @T('Outbound') %></h2>
<p><%- @T('Set caller id of outbound calls based on destination caller id.') %>
<div class="settings-entry js-outboundRouting">
<table class="settings-list" style="width: 100%;">
<thead>
<tr>
<th width="30%"><%- @T('Destination caller id') %>
<th width="30%"><%- @T('Set outbound caller id') %>
<th width="30%"><%- @T('Note') %>
<th width="10%"><%- @T('Action') %>
</thead>
<tbody>
<% for row in @config.outbound.routing_table: %>
<tr class="js-row">
<td class="settings-list-control-cell"><input name="dest" value="<%= row.dest %>" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="caller_id" value="<%= row.caller_id %>" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="note" value="<%= row.note %>" class="form-control form-control--small js-summary">
<td class="settings-list-row-control"><div class="btn btn--text js-remove"><%- @Icon('trash') %> <%- @T('Remove') %></div>
<% end %>
<tr>
<td class="settings-list-control-cell"><input name="dest" value="" placeholder="49* or 3230123456789" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="caller_id" value="" placeholder="4930609854189" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="note" value="" placeholder="<%- @Ti('my own note') %>" class="form-control form-control--small js-summary">
<td class="settings-list-row-control"><div class="btn btn--text btn--create js-add"><%- @Icon('plus-small') %> <%- @T('Add') %></div>
</tbody>
</table>
</div>
<p><%- @T('Default caller id.') %>
<div class="settings-entry">
<table class="settings-list" style="width: 100%;">
<thead>
<tr>
<th width="50%"><%- @T('Default caller id') %>
<th width="50%"><%- @T('Note') %>
</thead>
<tbody>
<tr>
<td class="settings-list-control-cell"><input name="default_caller_id" value="<%= @config.outbound.default_caller_id %>" placeholder="4930609854189" class="form-control form-control--small js-summary">
<td class="settings-list-row-control"><%- @T('Default caller id for outbound calls.') %>
</tbody>
</table>
</div>
-->
<button type="submit" class="btn btn--primary js-submit"><%- @T('Save') %></button>
</form>

View file

@ -43,7 +43,7 @@
<% end %> <% end %>
<tr> <tr>
<td class="settings-list-control-cell"><input name="caller_id" value="" placeholder="4930609854189" class="form-control form-control--small js-summary"> <td class="settings-list-control-cell"><input name="caller_id" value="" placeholder="4930609854189" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="note" value="" placeholder="<%- @Ti('my onw note') %>" class="form-control form-control--small js-summary"> <td class="settings-list-control-cell"><input name="note" value="" placeholder="<%- @Ti('my own note') %>" class="form-control form-control--small js-summary">
<td class="settings-list-row-control"><div class="btn btn--text btn--create js-add"><%- @Icon('plus-small') %> <%- @T('Add') %></div> <td class="settings-list-row-control"><div class="btn btn--text btn--create js-add"><%- @Icon('plus-small') %> <%- @T('Add') %></div>
</tbody> </tbody>
</table> </table>
@ -73,7 +73,7 @@
<tr> <tr>
<td class="settings-list-control-cell"><input name="dest" value="" placeholder="49* or 3230123456789" class="form-control form-control--small js-summary"> <td class="settings-list-control-cell"><input name="dest" value="" placeholder="49* or 3230123456789" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="caller_id" value="" placeholder="4930609854189" class="form-control form-control--small js-summary"> <td class="settings-list-control-cell"><input name="caller_id" value="" placeholder="4930609854189" class="form-control form-control--small js-summary">
<td class="settings-list-control-cell"><input name="note" value="" placeholder="<%- @Ti('my onw note') %>" class="form-control form-control--small js-summary"> <td class="settings-list-control-cell"><input name="note" value="" placeholder="<%- @Ti('my own note') %>" class="form-control form-control--small js-summary">
<td class="settings-list-row-control"><div class="btn btn--text btn--create js-add"><%- @Icon('plus-small') %> <%- @T('Add') %></div> <td class="settings-list-row-control"><div class="btn btn--text btn--create js-add"><%- @Icon('plus-small') %> <%- @T('Add') %></div>
</tbody> </tbody>
</table> </table>

View file

@ -15,6 +15,11 @@ class CtiController < ApplicationController
name: 'sipgate.io', name: 'sipgate.io',
enabled: Setting.get('sipgate_integration'), enabled: Setting.get('sipgate_integration'),
url: '#system/integration/sipgate', url: '#system/integration/sipgate',
},
{
name: 'Placetel',
enabled: Setting.get('placetel_integration'),
url: '#system/integration/placetel',
} }
] ]

View file

@ -0,0 +1,167 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
require 'builder'
class Integration::PlacetelController < ApplicationController
skip_before_action :verify_csrf_token
before_action :check_configured, :check_token
# notify about inbound call / block inbound call
def event
local_params = ActiveSupport::HashWithIndifferentAccess.new(params.permit!.to_h)
# do placetel event mapping
if local_params['event'] == 'IncomingCall'
local_params['direction'] = 'in'
local_params['event'] = 'newCall'
elsif local_params['event'] == 'HungUp'
local_params['event'] = 'hangup'
elsif local_params['event'] == 'CallAccepted'
local_params['event'] = 'answer'
end
if local_params['direction'].blank?
entry = Cti::Log.find_by(call_id: params[:call_id])
if entry
local_params['direction'] = entry.direction
end
end
if local_params['type'] == 'missed'
local_params['cause'] = 'cancel'
elsif local_params['type'] == 'voicemail'
local_params['cause'] = 'voicemail'
elsif local_params['type'] == 'blocked'
local_params['cause'] = 'blocked'
elsif local_params['type'] == 'accepted'
local_params['cause'] = 'normalClearing'
end
if local_params['direction'] == 'in'
if local_params['event'] == 'newCall'
config_inbound = config_integration[:inbound] || {}
block_caller_ids = config_inbound[:block_caller_ids] || []
# check if call need to be blocked
block_caller_ids.each do |item|
next unless item[:caller_id] == local_params['from']
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!
content = xml.Response() do
xml.Reject('reason' => 'busy')
end
send_data content, type: 'application/xml; charset=UTF-8;'
#local_params['Reject'] = 'busy'
local_params['comment'] = 'reject, busy'
if local_params['user']
local_params['comment'] = "#{local_params['user']} -> reject, busy"
end
Cti::Log.process(local_params)
return true
end
end
Cti::Log.process(local_params)
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!
content = xml.Response()
send_data content, type: 'application/xml; charset=UTF-8;'
return true
elsif local_params['direction'] == 'out'
config_outbound = config_integration[:outbound]
routing_table = nil
default_caller_id = nil
if config_outbound.present?
routing_table = config_outbound[:routing_table]
default_caller_id = config_outbound[:default_caller_id]
end
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!
# set callerId
content = nil
to = local_params[:to]
from = nil
if to && routing_table.present?
routing_table.each do |row|
dest = row[:dest].gsub(/\*/, '.+?')
next if to !~ /^#{dest}$/
from = row[:caller_id]
content = xml.Response() do
xml.Dial(callerId: from) { xml.Number(params[:to]) }
end
break
end
if !content && default_caller_id.present?
from = default_caller_id
content = xml.Response() do
xml.Dial(callerId: default_caller_id) { xml.Number(params[:to]) }
end
end
else
content = xml.Response()
end
send_data(content, type: 'application/xml; charset=UTF-8;')
if from.present?
local_params['from'] = from
end
Cti::Log.process(local_params)
return true
end
response_error('Invalid direction!')
end
private
def check_token
if Setting.get('placetel_token') != params[:token]
response_unauthorized('Invalid token, please contact your admin!')
return
end
true
end
def check_configured
http_log_config facility: 'placetel'
if !Setting.get('placetel_integration')
response_error('Feature is disable, please contact your admin to enable it!')
return
end
if config_integration.blank? || config_integration[:inbound].blank? || config_integration[:outbound].blank?
response_error('Feature not configured, please contact your admin!')
return
end
true
end
def xml_error(error, code)
xml = Builder::XmlMarkup.new(indent: 2)
xml.instruct!
content = xml.Response() do
xml.Error(error)
end
send_data content, type: 'application/xml; charset=UTF-8;', status: code
end
def config_integration
@config_integration ||= Setting.get('placetel_config')
end
def response_error(error)
xml_error(error, 422)
end
def response_unauthorized(error)
xml_error(error, 401)
end
end

View file

@ -0,0 +1,5 @@
Zammad::Application.routes.draw do
match '/api/v1/placetel/:token', to: 'integration/placetel#event', via: :post
end

View file

@ -0,0 +1,71 @@
class SettingAddPlacetel < ActiveRecord::Migration[5.1]
def change
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
Setting.create_if_not_exists(
title: 'Placetel integration',
name: 'placetel_integration',
area: 'Integration::Switch',
description: 'Defines if Placetel (http://www.placetel.de) is enabled or not.',
options: {
form: [
{
display: '',
null: true,
name: 'placetel_integration',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
state: false,
preferences: {
prio: 1,
trigger: ['menu:render', 'cti:reload'],
authentication: true,
permission: ['admin.integration'],
},
frontend: true
)
Setting.create_if_not_exists(
title: 'Placetel config',
name: 'placetel_config',
area: 'Integration::Placetel',
description: 'Defines the Placetel config.',
options: {},
state: { 'outbound' => { 'routing_table' => [], 'default_caller_id' => '' }, 'inbound' => { 'block_caller_ids' => [] } },
preferences: {
prio: 2,
permission: ['admin.integration'],
},
frontend: false,
)
Setting.create_if_not_exists(
title: 'PLACETEL Token',
name: 'placetel_token',
area: 'Integration::Placetel',
description: 'Token for placetel.',
options: {
form: [
{
display: '',
null: false,
name: 'placetel_token',
tag: 'input',
},
],
},
state: ENV['PLACETEL_TOKEN'] || SecureRandom.urlsafe_base64(20),
preferences: {
permission: ['admin.integration'],
},
frontend: false
)
end
end

View file

@ -3996,6 +3996,68 @@ Setting.create_if_not_exists(
}, },
frontend: false frontend: false
) )
Setting.create_if_not_exists(
title: 'Placetel integration',
name: 'placetel_integration',
area: 'Integration::Switch',
description: 'Defines if Placetel (http://www.placetel.de) is enabled or not.',
options: {
form: [
{
display: '',
null: true,
name: 'placetel_integration',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
state: false,
preferences: {
prio: 1,
trigger: ['menu:render', 'cti:reload'],
authentication: true,
permission: ['admin.integration'],
},
frontend: true
)
Setting.create_if_not_exists(
title: 'Placetel config',
name: 'placetel_config',
area: 'Integration::Placetel',
description: 'Defines the Placetel config.',
options: {},
state: { 'outbound' => { 'routing_table' => [], 'default_caller_id' => '' }, 'inbound' => { 'block_caller_ids' => [] } },
preferences: {
prio: 2,
permission: ['admin.integration'],
},
frontend: false,
)
Setting.create_if_not_exists(
title: 'PLACETEL Token',
name: 'placetel_token',
area: 'Integration::Placetel',
description: 'Token for placetel.',
options: {
form: [
{
display: '',
null: false,
name: 'placetel_token',
tag: 'input',
},
],
},
state: ENV['PLACETEL_TOKEN'] || SecureRandom.urlsafe_base64(20),
preferences: {
permission: ['admin.integration'],
},
frontend: false
)
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Clearbit integration', title: 'Clearbit integration',
name: 'clearbit_integration', name: 'clearbit_integration',

View file

@ -5,3 +5,4 @@ set -ex
rm app/assets/javascripts/app/controllers/layout_ref.coffee rm app/assets/javascripts/app/controllers/layout_ref.coffee
rm -rf app/assets/javascripts/app/views/layout_ref/ rm -rf app/assets/javascripts/app/views/layout_ref/
rm app/assets/javascripts/app/controllers/karma.coffee rm app/assets/javascripts/app/controllers/karma.coffee
rm app/assets/javascripts/app/controllers/_integration/placetel.coffee

View file

@ -0,0 +1,514 @@
require 'rails_helper'
RSpec.describe 'Integration Placetel', type: :request do
let(:agent_user) do
create(:agent_user)
end
let!(:customer_user1) do
create(
:customer_user,
login: 'ticket-caller_id_cti-customer1@example.com',
firstname: 'CallerId',
lastname: 'Customer1',
phone: '+49 99999 222222',
fax: '+49 99999 222223',
mobile: '+01114100300',
note: 'Phone at home: +49 99999 222224',
)
end
let!(:customer_user2) do
create(
:customer_user,
login: 'ticket-caller_id_cti-customer2@example.com',
firstname: 'CallerId',
lastname: 'Customer2',
phone: '+49 99999 222222 2',
)
end
let!(:customer_user3) do
create(
:customer_user,
login: 'ticket-caller_id_cti-customer3@example.com',
firstname: 'CallerId',
lastname: 'Customer3',
phone: '+49 99999 222222 2',
)
end
before(:each) do
Cti::Log.destroy_all
Setting.set('placetel_integration', true)
Setting.set('placetel_config', {
outbound: {
routing_table: [
{
dest: '41*',
caller_id: '41715880339000',
},
{
dest: '491714000000',
caller_id: '41715880339000',
},
],
default_caller_id: '4930777000000',
},
inbound: {
block_caller_ids: [
{
caller_id: '491715000000',
note: 'some note',
}
],
notify_user_ids: {
2 => true,
4 => false,
},
}
})
Cti::CallerId.rebuild
end
describe 'request handling' do
it 'does token check' do
params = 'event=IncomingCall&from=01114100300&to=030600000000&call_id=4991155921769858278-1'
post '/api/v1/placetel/not_existing_token', params: params
expect(response).to have_http_status(401)
error = nil
local_response = REXML::Document.new(response.body)
local_response.elements.each('Response/Error') do |element|
error = element.text
end
expect(error).to eq('Invalid token, please contact your admin!')
end
it 'does basic call' do
token = Setting.get('placetel_token')
# inbound - I
params = 'event=IncomingCall&from=01114100300&to=030600000000&call_id=4991155921769858278-1'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
local_response = REXML::Document.new(response.body)
expect(local_response.elements.count).to eq(1)
expect(local_response.elements.first.to_s).to eq('<Response/>')
# inbound - II - block caller
params = 'event=IncomingCall&from=491715000000&to=030600000000&call_id=4991155921769858278-2'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
local_response = REXML::Document.new(response.body)
reason = nil
local_response.elements.each('Response/Reject') do |element|
reason = element.attributes['reason']
end
expect(reason).to eq('busy')
# outbound - I - set default_caller_id
params = 'event=newCall&direction=out&from=030600000000&to=01114100300&call_id=8621106404543334274-3'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
caller_id = nil
number_to_dail = nil
lcoal_response = REXML::Document.new(response.body)
lcoal_response.elements.each('Response/Dial') do |element|
caller_id = element.attributes['callerId']
end
lcoal_response.elements.each('Response/Dial/Number') do |element|
number_to_dail = element.text
end
expect(caller_id).to eq('4930777000000')
expect(number_to_dail).to eq('01114100300')
# outbound - II - set caller_id based on routing_table by explicite number
params = 'event=newCall&direction=out&from=030600000000&to=491714000000&call_id=8621106404543334274-4'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
caller_id = nil
number_to_dail = nil
lcoal_response = REXML::Document.new(response.body)
lcoal_response.elements.each('Response/Dial') do |element|
caller_id = element.attributes['callerId']
end
lcoal_response.elements.each('Response/Dial/Number') do |element|
number_to_dail = element.text
end
expect(caller_id).to eq('41715880339000')
expect(number_to_dail).to eq('491714000000')
# outbound - III - set caller_id based on routing_table by 41*
params = 'event=newCall&direction=out&from=030600000000&to=4147110000000&call_id=8621106404543334274-5'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
caller_id = nil
number_to_dail = nil
lcoal_response = REXML::Document.new(response.body)
lcoal_response.elements.each('Response/Dial') do |element|
caller_id = element.attributes['callerId']
end
lcoal_response.elements.each('Response/Dial/Number') do |element|
number_to_dail = element.text
end
expect(caller_id).to eq('41715880339000')
expect(number_to_dail).to eq('4147110000000')
# no config
Setting.set('placetel_config', {})
params = 'event=IncomingCall&from=01114100300&to=030600000000&call_id=4991155921769858278-6'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(422)
error = nil
local_response = REXML::Document.new(response.body)
local_response.elements.each('Response/Error') do |element|
error = element.text
end
expect(error).to eq('Feature not configured, please contact your admin!')
end
it 'does log call' do
token = Setting.get('placetel_token')
# outbound - I - new call
params = 'event=newCall&direction=out&from=030600000000&to=01114100300&call_id=1234567890-1'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-1')
expect(log).to be_truthy
expect(log.from).to eq('4930777000000')
expect(log.to).to eq('01114100300')
expect(log.direction).to eq('out')
#expect(log.from_comment).to eq('user 1')
expect(log.to_comment).to eq('CallerId Customer1')
expect(log.comment).to be_nil
expect(log.state).to eq('newCall')
expect(log.done).to eq(true)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_nil
expect(log.end_at).to be_nil
expect(log.duration_waiting_time).to be_nil
expect(log.duration_talking_time).to be_nil
# outbound - I - hangup by agent
params = 'event=HungUp&call_id=1234567890-1&type=missed'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-1')
expect(log).to be_truthy
expect(log.from).to eq('4930777000000')
expect(log.to).to eq('01114100300')
expect(log.direction).to eq('out')
#expect(log.from_comment).to eq('user 1')
expect(log.to_comment).to eq('CallerId Customer1')
expect(log.comment).to eq('cancel')
expect(log.state).to eq('hangup')
expect(log.done).to eq(true)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_nil
expect(log.end_at).to be_truthy
expect(log.duration_waiting_time).to be_truthy
expect(log.duration_talking_time).to be_nil
# outbound - II - new call
params = 'event=newCall&direction=out&from=030600000000&to=01114100300&call_id=1234567890-2'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-2')
expect(log).to be_truthy
expect(log.from).to eq('4930777000000')
expect(log.to).to eq('01114100300')
expect(log.direction).to eq('out')
# expect(log.from_comment).to eq('user 1')
expect(log.to_comment).to eq('CallerId Customer1')
expect(log.comment).to be_nil
expect(log.state).to eq('newCall')
expect(log.done).to eq(true)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_nil
expect(log.end_at).to be_nil
expect(log.duration_waiting_time).to be_nil
expect(log.duration_talking_time).to be_nil
# outbound - II - answer by customer
params = 'event=CallAccepted&call_id=1234567890-2&from=030600000000&to=01114100300'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-2')
expect(log).to be_truthy
expect(log.from).to eq('4930777000000')
expect(log.to).to eq('01114100300')
expect(log.direction).to eq('out')
# expect(log.from_comment).to eq('user 1')
expect(log.to_comment).to eq('CallerId Customer1')
expect(log.comment).to be_nil
expect(log.state).to eq('answer')
expect(log.done).to eq(true)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_truthy
expect(log.end_at).to be_nil
expect(log.duration_waiting_time).to be_truthy
expect(log.duration_talking_time).to be_nil
# outbound - II - hangup by customer
params = 'event=HungUp&call_id=1234567890-2&type=accepted&from=030600000000&to=01114100300'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-2')
expect(log).to be_truthy
expect(log.from).to eq('4930777000000')
expect(log.to).to eq('01114100300')
expect(log.direction).to eq('out')
# expect(log.from_comment).to eq('user 1')
expect(log.to_comment).to eq('CallerId Customer1')
expect(log.comment).to eq('normalClearing')
expect(log.state).to eq('hangup')
expect(log.done).to eq(true)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_truthy
expect(log.end_at).to be_truthy
expect(log.duration_waiting_time).to be_truthy
expect(log.duration_talking_time).to be_truthy
# inbound - I - new call
params = 'event=IncomingCall&to=030600000000&from=01114100300&call_id=1234567890-3'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-3')
expect(log).to be_truthy
expect(log.to).to eq('030600000000')
expect(log.from).to eq('01114100300')
expect(log.direction).to eq('in')
#expect(log.to_comment).to eq('user 1')
expect(log.from_comment).to eq('CallerId Customer1')
expect(log.comment).to be_nil
expect(log.state).to eq('newCall')
expect(log.done).to eq(false)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_nil
expect(log.end_at).to be_nil
expect(log.duration_waiting_time).to be_nil
expect(log.duration_talking_time).to be_nil
# inbound - I - answer by customer
params = 'event=CallAccepted&call_id=1234567890-3&to=030600000000&from=01114100300'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-3')
expect(log).to be_truthy
expect(log.to).to eq('030600000000')
expect(log.from).to eq('01114100300')
expect(log.direction).to eq('in')
#expect(log.to_comment).to eq('user 1')
expect(log.from_comment).to eq('CallerId Customer1')
expect(log.comment).to be_nil
expect(log.state).to eq('answer')
expect(log.done).to eq(true)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_truthy
expect(log.end_at).to be_nil
expect(log.duration_waiting_time).to be_truthy
expect(log.duration_talking_time).to be_nil
# inbound - I - hangup by customer
params = 'event=HungUp&call_id=1234567890-3&type=accepted&to=030600000000&from=01114100300'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-3')
expect(log).to be_truthy
expect(log.to).to eq('030600000000')
expect(log.from).to eq('01114100300')
expect(log.direction).to eq('in')
#expect(log.to_comment).to eq('user 1')
expect(log.from_comment).to eq('CallerId Customer1')
expect(log.comment).to eq('normalClearing')
expect(log.state).to eq('hangup')
expect(log.done).to eq(true)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_truthy
expect(log.end_at).to be_truthy
expect(log.duration_waiting_time).to be_truthy
expect(log.duration_talking_time).to be_truthy
# inbound - II - new call
params = 'event=IncomingCall&to=030600000000&from=01114100300&call_id=1234567890-4'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-4')
expect(log).to be_truthy
expect(log.to).to eq('030600000000')
expect(log.from).to eq('01114100300')
expect(log.direction).to eq('in')
#expect(log.to_comment).to eq('user 1,user 2')
expect(log.from_comment).to eq('CallerId Customer1')
expect(log.comment).to be_nil
expect(log.state).to eq('newCall')
expect(log.done).to eq(false)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_nil
expect(log.end_at).to be_nil
expect(log.duration_waiting_time).to be_nil
expect(log.duration_talking_time).to be_nil
# inbound - II - answer by voicemail
params = 'event=CallAccepted&call_id=1234567890-4&to=030600000000&from=01114100300&user=voicemail'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-4')
expect(log).to be_truthy
expect(log.to).to eq('030600000000')
expect(log.from).to eq('01114100300')
expect(log.direction).to eq('in')
expect(log.to_comment).to eq('voicemail')
expect(log.from_comment).to eq('CallerId Customer1')
expect(log.comment).to be_nil
expect(log.state).to eq('answer')
expect(log.done).to eq(true)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_truthy
expect(log.end_at).to be_nil
expect(log.duration_waiting_time).to be_truthy
expect(log.duration_talking_time).to be_nil
# inbound - II - hangup by customer
params = 'event=HungUp&call_id=1234567890-4&type=accepted&to=030600000000&from=01114100300'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-4')
expect(log).to be_truthy
expect(log.to).to eq('030600000000')
expect(log.from).to eq('01114100300')
expect(log.direction).to eq('in')
expect(log.to_comment).to eq('voicemail')
expect(log.from_comment).to eq('CallerId Customer1')
expect(log.comment).to eq('normalClearing')
expect(log.state).to eq('hangup')
expect(log.done).to eq(false)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_truthy
expect(log.end_at).to be_truthy
expect(log.duration_waiting_time).to be_truthy
expect(log.duration_talking_time).to be_truthy
# inbound - III - new call
params = 'event=IncomingCall&to=030600000000&from=01114100300&call_id=1234567890-5'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-5')
expect(log).to be_truthy
expect(log.to).to eq('030600000000')
expect(log.from).to eq('01114100300')
expect(log.direction).to eq('in')
#expect(log.to_comment).to eq('user 1,user 2')
expect(log.from_comment).to eq('CallerId Customer1')
expect(log.comment).to be_nil
expect(log.state).to eq('newCall')
expect(log.done).to eq(false)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_nil
expect(log.end_at).to be_nil
expect(log.duration_waiting_time).to be_nil
expect(log.duration_talking_time).to be_nil
# inbound - III - hangup by customer
params = 'event=HungUp&call_id=1234567890-5&type=accepted&to=030600000000&from=01114100300'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-5')
expect(log).to be_truthy
expect(log.to).to eq('030600000000')
expect(log.from).to eq('01114100300')
expect(log.direction).to eq('in')
#expect(log.to_comment).to eq('user 1,user 2')
expect(log.from_comment).to eq('CallerId Customer1')
expect(log.comment).to eq('normalClearing')
expect(log.state).to eq('hangup')
expect(log.done).to eq(false)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_nil
expect(log.end_at).to be_truthy
expect(log.duration_waiting_time).to be_truthy
expect(log.duration_talking_time).to be_nil
# inbound - IV - new call
params = 'event=IncomingCall&to=030600000000&from=49999992222222&call_id=1234567890-6'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-6')
expect(log).to be_truthy
expect(log.to).to eq('030600000000')
expect(log.from).to eq('49999992222222')
expect(log.direction).to eq('in')
#expect(log.to_comment).to eq('user 1,user 2')
expect(log.from_comment).to eq('CallerId Customer3,CallerId Customer2')
expect(log.preferences['to']).to be_falsey
expect(log.preferences['from']).to be_truthy
expect(log.comment).to be_nil
expect(log.state).to eq('newCall')
expect(log.done).to eq(false)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_nil
expect(log.end_at).to be_nil
expect(log.duration_waiting_time).to be_nil
expect(log.duration_talking_time).to be_nil
# inbound - IV - new call
params = 'event=IncomingCall&to=030600000000&from=anonymous&call_id=1234567890-7'
post "/api/v1/placetel/#{token}", params: params
expect(response).to have_http_status(200)
log = Cti::Log.find_by(call_id: '1234567890-7')
expect(log).to be_truthy
expect(log.to).to eq('030600000000')
expect(log.from).to eq('anonymous')
expect(log.direction).to eq('in')
#expect(log.to_comment).to eq('user 1,user 2')
expect(log.from_comment).to be_nil
expect(log.preferences['to']).to be_falsey
expect(log.preferences['from']).to be_falsey
expect(log.comment).to be_nil
expect(log.state).to eq('newCall')
expect(log.done).to eq(false)
expect(log.initialized_at).to be_truthy
expect(log.start_at).to be_nil
expect(log.end_at).to be_nil
expect(log.duration_waiting_time).to be_nil
expect(log.duration_talking_time).to be_nil
# get caller list
get '/api/v1/cti/log'
expect(response).to have_http_status(401)
authenticated_as(agent_user)
get '/api/v1/cti/log', as: :json
expect(response).to have_http_status(200)
expect(json_response['list']).to be_a_kind_of(Array)
expect(json_response['list'].count).to eq(7)
expect(json_response['assets']).to be_truthy
expect(json_response['assets']['User']).to be_truthy
expect(json_response['assets']['User'][customer_user2.id.to_s]).to be_truthy
expect(json_response['assets']['User'][customer_user3.id.to_s]).to be_truthy
expect(json_response['list'][0]['call_id']).to eq('1234567890-7')
expect(json_response['list'][1]['call_id']).to eq('1234567890-6')
expect(json_response['list'][2]['call_id']).to eq('1234567890-5')
expect(json_response['list'][3]['call_id']).to eq('1234567890-4')
expect(json_response['list'][4]['call_id']).to eq('1234567890-3')
expect(json_response['list'][5]['call_id']).to eq('1234567890-2')
expect(json_response['list'][5]['state']).to eq('hangup')
expect(json_response['list'][5]['from']).to eq('4930777000000')
#expect(json_response['list'][5]['from_comment']).to eq('user 1')
expect(json_response['list'][5]['to']).to eq('01114100300')
expect(json_response['list'][5]['to_comment']).to eq('CallerId Customer1')
expect(json_response['list'][5]['comment']).to eq('normalClearing')
expect(json_response['list'][5]['state']).to eq('hangup')
expect(json_response['list'][6]['call_id']).to eq('1234567890-1')
end
end
end