Implemented CTI ticket create screen popup on answering call.
This commit is contained in:
parent
9503ff20ce
commit
7faa4c310d
31 changed files with 1840 additions and 463 deletions
|
@ -57,7 +57,7 @@ License: MIT license
|
||||||
highlight.pack.js
|
highlight.pack.js
|
||||||
Source: https://highlightjs.org
|
Source: https://highlightjs.org
|
||||||
Copyright: 2006 Ivan Sagalaev (https://github.com/isagalaev)
|
Copyright: 2006 Ivan Sagalaev (https://github.com/isagalaev)
|
||||||
License: BSD License
|
License: BSD License (BSD-3-Clause)
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
jquery.flot.js
|
jquery.flot.js
|
||||||
Source: https://github.com/dnschnur/flot
|
Source: https://github.com/dnschnur/flot
|
||||||
|
@ -74,7 +74,7 @@ jquery.fineuploader-3.0.js
|
||||||
Source: http://github.com/Valums-File-Uploader/file-uploader
|
Source: http://github.com/Valums-File-Uploader/file-uploader
|
||||||
Copyright: 2010 Andrew Valums <andrew(at)valums.com>
|
Copyright: 2010 Andrew Valums <andrew(at)valums.com>
|
||||||
2012 Ray Nicholus <fineuploader(at)garstasio.com>
|
2012 Ray Nicholus <fineuploader(at)garstasio.com>
|
||||||
License: MIT license, GNU GPL 2 or later, GNU LGPL 2 or later
|
License: GNU GPL 2 or later, GNU LGPL 2 or later
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
jquery.noty.js
|
jquery.noty.js
|
||||||
Source: https://github.com/needim/noty/
|
Source: https://github.com/needim/noty/
|
||||||
|
@ -171,7 +171,7 @@ License: MIT license
|
||||||
Font Awesome icon font
|
Font Awesome icon font
|
||||||
Source: http://fontawesome.io/
|
Source: http://fontawesome.io/
|
||||||
Copyright: Font Awesome by Dave Gandy - http://fontawesome.io
|
Copyright: Font Awesome by Dave Gandy - http://fontawesome.io
|
||||||
License: SIL OFL 1.1
|
License: MIT License
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
Simple line icons font
|
Simple line icons font
|
||||||
Source: https://github.com/thesabbir/simple-line-icons
|
Source: https://github.com/thesabbir/simple-line-icons
|
||||||
|
|
|
@ -26,8 +26,10 @@ class Form extends App.Controller
|
||||||
'submit form': 'update'
|
'submit form': 'update'
|
||||||
'click .js-inboundBlockCallerId .js-add': 'addInboundBlockCallerId'
|
'click .js-inboundBlockCallerId .js-add': 'addInboundBlockCallerId'
|
||||||
'click .js-outboundRouting .js-add': 'addOutboundRouting'
|
'click .js-outboundRouting .js-add': 'addOutboundRouting'
|
||||||
|
'click .js-notifyMap .js-addMap': 'addNotifyMap'
|
||||||
'click .js-inboundBlockCallerId .js-remove': 'removeInboundBlockCallerId'
|
'click .js-inboundBlockCallerId .js-remove': 'removeInboundBlockCallerId'
|
||||||
'click .js-outboundRouting .js-remove': 'removeOutboundRouting'
|
'click .js-outboundRouting .js-remove': 'removeOutboundRouting'
|
||||||
|
'click .js-notifyMap .js-removeMap': 'removeNotifyMap'
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
@ -43,6 +45,8 @@ class Form extends App.Controller
|
||||||
config.inbound = {}
|
config.inbound = {}
|
||||||
if !config.inbound.block_caller_ids
|
if !config.inbound.block_caller_ids
|
||||||
config.inbound.block_caller_ids = []
|
config.inbound.block_caller_ids = []
|
||||||
|
if !config.notify_map
|
||||||
|
config.notify_map = []
|
||||||
config
|
config
|
||||||
|
|
||||||
setConfig: (value) ->
|
setConfig: (value) ->
|
||||||
|
@ -56,6 +60,32 @@ class Form extends App.Controller
|
||||||
cti_token: App.Setting.get('cti_token')
|
cti_token: App.Setting.get('cti_token')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# placeholder
|
||||||
|
configure_attributes = [
|
||||||
|
{ name: 'user_ids', display: '', tag: 'column_select', multiple: true, null: true, relation: 'User', sortBy: 'firstname' },
|
||||||
|
]
|
||||||
|
new App.ControllerForm(
|
||||||
|
el: @$('.js-userSelectorBlank')
|
||||||
|
model:
|
||||||
|
configure_attributes: configure_attributes,
|
||||||
|
params:
|
||||||
|
user_ids: []
|
||||||
|
autofocus: false
|
||||||
|
)
|
||||||
|
|
||||||
|
for row in @config.notify_map
|
||||||
|
configure_attributes = [
|
||||||
|
{ name: 'user_ids', display: '', tag: 'column_select', multiple: true, null: true, relation: 'User', sortBy: 'firstname' },
|
||||||
|
]
|
||||||
|
new App.ControllerForm(
|
||||||
|
el: @$("[name=queue][value='#{row.queue}']").closest('tr').find('.js-userSelector')
|
||||||
|
model:
|
||||||
|
configure_attributes: configure_attributes,
|
||||||
|
params:
|
||||||
|
user_ids: row.user_ids
|
||||||
|
autofocus: false
|
||||||
|
)
|
||||||
|
|
||||||
updateCurrentConfig: =>
|
updateCurrentConfig: =>
|
||||||
config = @config
|
config = @config
|
||||||
cleanupInput = @cleanupInput
|
cleanupInput = @cleanupInput
|
||||||
|
@ -88,6 +118,17 @@ class Form extends App.Controller
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# notify map
|
||||||
|
config.notify_map = []
|
||||||
|
@$('.js-notifyMap .js-row').each(->
|
||||||
|
queue = $(@).find('input[name="queue"]').val()
|
||||||
|
user_ids = $(@).find('select[name="user_ids"]').val()
|
||||||
|
config.notify_map.push {
|
||||||
|
queue: cleanupInput(queue)
|
||||||
|
user_ids: user_ids
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
@config = config
|
@config = config
|
||||||
|
|
||||||
update: (e) =>
|
update: (e) =>
|
||||||
|
@ -127,6 +168,41 @@ class Form extends App.Controller
|
||||||
}
|
}
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
|
addNotifyMap: (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
@updateCurrentConfig()
|
||||||
|
element = $(e.currentTarget).closest('tr')
|
||||||
|
queue = @cleanupInput(element.find('input[name="queue"]').val())
|
||||||
|
user_ids = element.find('select[name="user_ids"]').val()
|
||||||
|
if _.isEmpty(queue)
|
||||||
|
@notify(
|
||||||
|
type: 'error'
|
||||||
|
msg: App.i18n.translateContent('A queue is required!')
|
||||||
|
timeout: 6000
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if _.isEmpty(user_ids)
|
||||||
|
@notify(
|
||||||
|
type: 'error'
|
||||||
|
msg: App.i18n.translateContent('A user is required!')
|
||||||
|
timeout: 6000
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
for row in @config.notify_map
|
||||||
|
if row.queue is queue
|
||||||
|
@notify(
|
||||||
|
type: 'error'
|
||||||
|
msg: App.i18n.translateContent('Queue already exists!')
|
||||||
|
timeout: 6000
|
||||||
|
)
|
||||||
|
return
|
||||||
|
@config.notify_map.push {
|
||||||
|
queue: queue
|
||||||
|
user_ids: user_ids
|
||||||
|
}
|
||||||
|
@render()
|
||||||
|
|
||||||
removeInboundBlockCallerId: (e) =>
|
removeInboundBlockCallerId: (e) =>
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@updateCurrentConfig()
|
@updateCurrentConfig()
|
||||||
|
@ -141,6 +217,13 @@ class Form extends App.Controller
|
||||||
element.remove()
|
element.remove()
|
||||||
@updateCurrentConfig()
|
@updateCurrentConfig()
|
||||||
|
|
||||||
|
removeNotifyMap: (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
@updateCurrentConfig()
|
||||||
|
element = $(e.currentTarget).closest('tr')
|
||||||
|
element.remove()
|
||||||
|
@updateCurrentConfig()
|
||||||
|
|
||||||
class State
|
class State
|
||||||
@current: ->
|
@current: ->
|
||||||
App.Setting.get('cti_integration')
|
App.Setting.get('cti_integration')
|
||||||
|
|
|
@ -25,9 +25,11 @@ class Form extends App.Controller
|
||||||
events:
|
events:
|
||||||
'submit form': 'update'
|
'submit form': 'update'
|
||||||
'click .js-inboundBlockCallerId .js-add': 'addInboundBlockCallerId'
|
'click .js-inboundBlockCallerId .js-add': 'addInboundBlockCallerId'
|
||||||
'click .js-outboundRouting .js-add': 'addOutboundRouting'
|
|
||||||
'click .js-inboundBlockCallerId .js-remove': 'removeInboundBlockCallerId'
|
'click .js-inboundBlockCallerId .js-remove': 'removeInboundBlockCallerId'
|
||||||
|
'click .js-outboundRouting .js-add': 'addOutboundRouting'
|
||||||
'click .js-outboundRouting .js-remove': 'removeOutboundRouting'
|
'click .js-outboundRouting .js-remove': 'removeOutboundRouting'
|
||||||
|
'click .js-userDeviceMap .js-add': 'addUserDeviceMap'
|
||||||
|
'click .js-userDeviceMap .js-remove': 'removeUserDeviceMap'
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
@ -43,6 +45,8 @@ class Form extends App.Controller
|
||||||
config.inbound = {}
|
config.inbound = {}
|
||||||
if !config.inbound.block_caller_ids
|
if !config.inbound.block_caller_ids
|
||||||
config.inbound.block_caller_ids = []
|
config.inbound.block_caller_ids = []
|
||||||
|
if !config.user_device_map
|
||||||
|
config.user_device_map = []
|
||||||
config
|
config
|
||||||
|
|
||||||
setConfig: (value) ->
|
setConfig: (value) ->
|
||||||
|
@ -60,7 +64,7 @@ class Form extends App.Controller
|
||||||
config = @config
|
config = @config
|
||||||
cleanupInput = @cleanupInput
|
cleanupInput = @cleanupInput
|
||||||
|
|
||||||
config.api_token = @$('input[name=api_token]').val()
|
config.api_token = cleanupInput(@$('input[name=api_token]').val())
|
||||||
|
|
||||||
# default caller_id
|
# default caller_id
|
||||||
default_caller_id = @$('input[name=default_caller_id]').val()
|
default_caller_id = @$('input[name=default_caller_id]').val()
|
||||||
|
@ -90,6 +94,17 @@ class Form extends App.Controller
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# user device map
|
||||||
|
config.user_device_map = []
|
||||||
|
@$('.js-userDeviceMap .js-row').each(->
|
||||||
|
device_id = $(@).find('input[name="device_id"]').val()
|
||||||
|
user_id = $(@).find('input[name="user_id"]').val()
|
||||||
|
config.user_device_map.push {
|
||||||
|
device_id: device_id
|
||||||
|
user_id: user_id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
@config = config
|
@config = config
|
||||||
|
|
||||||
update: (e) =>
|
update: (e) =>
|
||||||
|
@ -114,6 +129,13 @@ class Form extends App.Controller
|
||||||
}
|
}
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
|
removeInboundBlockCallerId: (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
@updateCurrentConfig()
|
||||||
|
element = $(e.currentTarget).closest('tr')
|
||||||
|
element.remove()
|
||||||
|
@updateCurrentConfig()
|
||||||
|
|
||||||
addOutboundRouting: (e) =>
|
addOutboundRouting: (e) =>
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@updateCurrentConfig()
|
@updateCurrentConfig()
|
||||||
|
@ -129,14 +151,27 @@ class Form extends App.Controller
|
||||||
}
|
}
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
removeInboundBlockCallerId: (e) =>
|
removeOutboundRouting: (e) =>
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@updateCurrentConfig()
|
@updateCurrentConfig()
|
||||||
element = $(e.currentTarget).closest('tr')
|
element = $(e.currentTarget).closest('tr')
|
||||||
element.remove()
|
element.remove()
|
||||||
@updateCurrentConfig()
|
@updateCurrentConfig()
|
||||||
|
|
||||||
removeOutboundRouting: (e) =>
|
addUserDeviceMap: (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
@updateCurrentConfig()
|
||||||
|
element = $(e.currentTarget).closest('tr')
|
||||||
|
user_id = @cleanupInput(element.find('input[name="user_id"]').val())
|
||||||
|
device_id = @cleanupInput(element.find('input[name="device_id"]').val())
|
||||||
|
return if _.isEmpty(user_id) || _.isEmpty(device_id)
|
||||||
|
@config.user_device_map.push {
|
||||||
|
user_id: user_id
|
||||||
|
device_id: device_id
|
||||||
|
}
|
||||||
|
@render()
|
||||||
|
|
||||||
|
removeUserDeviceMap: (e) =>
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@updateCurrentConfig()
|
@updateCurrentConfig()
|
||||||
element = $(e.currentTarget).closest('tr')
|
element = $(e.currentTarget).closest('tr')
|
||||||
|
|
|
@ -28,6 +28,8 @@ class Form extends App.Controller
|
||||||
'click .js-outboundRouting .js-add': 'addOutboundRouting'
|
'click .js-outboundRouting .js-add': 'addOutboundRouting'
|
||||||
'click .js-inboundBlockCallerId .js-remove': 'removeInboundBlockCallerId'
|
'click .js-inboundBlockCallerId .js-remove': 'removeInboundBlockCallerId'
|
||||||
'click .js-outboundRouting .js-remove': 'removeOutboundRouting'
|
'click .js-outboundRouting .js-remove': 'removeOutboundRouting'
|
||||||
|
'click .js-userRemoteMap .js-add': 'addUserRemoteMap'
|
||||||
|
'click .js-userRemoteMap .js-remove': 'removeUserRemoteMap'
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
|
@ -43,6 +45,8 @@ class Form extends App.Controller
|
||||||
config.inbound = {}
|
config.inbound = {}
|
||||||
if !config.inbound.block_caller_ids
|
if !config.inbound.block_caller_ids
|
||||||
config.inbound.block_caller_ids = []
|
config.inbound.block_caller_ids = []
|
||||||
|
if !config.user_remote_map
|
||||||
|
config.user_remote_map = []
|
||||||
config
|
config
|
||||||
|
|
||||||
setConfig: (value) ->
|
setConfig: (value) ->
|
||||||
|
@ -59,6 +63,9 @@ class Form extends App.Controller
|
||||||
config = @config
|
config = @config
|
||||||
cleanupInput = @cleanupInput
|
cleanupInput = @cleanupInput
|
||||||
|
|
||||||
|
config.api_user = cleanupInput(@$('input[name=api_user]').val())
|
||||||
|
config.api_password = cleanupInput(@$('input[name=api_password]').val())
|
||||||
|
|
||||||
# default caller_id
|
# default caller_id
|
||||||
default_caller_id = @$('input[name=default_caller_id]').val()
|
default_caller_id = @$('input[name=default_caller_id]').val()
|
||||||
config.outbound.default_caller_id = cleanupInput(default_caller_id)
|
config.outbound.default_caller_id = cleanupInput(default_caller_id)
|
||||||
|
@ -87,6 +94,17 @@ class Form extends App.Controller
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# user device map
|
||||||
|
config.user_remote_map = []
|
||||||
|
@$('.js-userRemoteMap .js-row').each(->
|
||||||
|
remote_user_id = $(@).find('input[name="remote_user_id"]').val()
|
||||||
|
user_id = $(@).find('input[name="user_id"]').val()
|
||||||
|
config.user_remote_map.push {
|
||||||
|
remote_user_id: remote_user_id
|
||||||
|
user_id: user_id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
@config = config
|
@config = config
|
||||||
|
|
||||||
update: (e) =>
|
update: (e) =>
|
||||||
|
@ -140,6 +158,26 @@ class Form extends App.Controller
|
||||||
element.remove()
|
element.remove()
|
||||||
@updateCurrentConfig()
|
@updateCurrentConfig()
|
||||||
|
|
||||||
|
addUserRemoteMap: (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
@updateCurrentConfig()
|
||||||
|
element = $(e.currentTarget).closest('tr')
|
||||||
|
user_id = @cleanupInput(element.find('input[name="user_id"]').val())
|
||||||
|
remote_user_id = @cleanupInput(element.find('input[name="remote_user_id"]').val())
|
||||||
|
return if _.isEmpty(user_id) || _.isEmpty(remote_user_id)
|
||||||
|
@config.user_remote_map.push {
|
||||||
|
user_id: user_id
|
||||||
|
remote_user_id: remote_user_id
|
||||||
|
}
|
||||||
|
@render()
|
||||||
|
|
||||||
|
removeUserRemoteMap: (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
@updateCurrentConfig()
|
||||||
|
element = $(e.currentTarget).closest('tr')
|
||||||
|
element.remove()
|
||||||
|
@updateCurrentConfig()
|
||||||
|
|
||||||
class State
|
class State
|
||||||
@current: ->
|
@current: ->
|
||||||
App.Setting.get('sipgate_integration')
|
App.Setting.get('sipgate_integration')
|
||||||
|
|
|
@ -199,7 +199,7 @@ class App.TicketCreate extends App.Controller
|
||||||
|
|
||||||
if _.isEmpty(params.ticket_id) && _.isEmpty(params.article_id)
|
if _.isEmpty(params.ticket_id) && _.isEmpty(params.article_id)
|
||||||
if !_.isEmpty(params.customer_id)
|
if !_.isEmpty(params.customer_id)
|
||||||
@renderQueue(options: { customer_id: params.customer_id })
|
@renderQueue(options: params)
|
||||||
return
|
return
|
||||||
@renderQueue()
|
@renderQueue()
|
||||||
return
|
return
|
||||||
|
|
|
@ -6,7 +6,7 @@ class App.CTI extends App.Controller
|
||||||
'.js-callerLog': 'callerLog'
|
'.js-callerLog': 'callerLog'
|
||||||
events:
|
events:
|
||||||
'click .js-check': 'done'
|
'click .js-check': 'done'
|
||||||
'click .js-userNew': 'userNew'
|
'click .js-newUser': 'newUser'
|
||||||
list: []
|
list: []
|
||||||
backends: []
|
backends: []
|
||||||
meta:
|
meta:
|
||||||
|
@ -32,9 +32,30 @@ class App.CTI extends App.Controller
|
||||||
return if data.state isnt 'newCall'
|
return if data.state isnt 'newCall'
|
||||||
return if data.direction isnt 'in'
|
return if data.direction isnt 'in'
|
||||||
return if @switch() isnt true
|
return if @switch() isnt true
|
||||||
@notify(data)
|
if !document.hasFocus()
|
||||||
|
@notify(data)
|
||||||
'cti_event'
|
'cti_event'
|
||||||
)
|
)
|
||||||
|
@bind('menu:render', (data) =>
|
||||||
|
return if @switch() isnt true
|
||||||
|
localHtml = ''
|
||||||
|
for item in @ringingCalls()
|
||||||
|
localHtml += App.view('navigation/menu_cti_ringing')(
|
||||||
|
item: item
|
||||||
|
)
|
||||||
|
$('.js-phoneMenuItem').after(localHtml)
|
||||||
|
$('.call-widget').find('.js-newUser').bind('click', (e) =>
|
||||||
|
@newUser(e)
|
||||||
|
)
|
||||||
|
$('.call-widget').find('.js-newTicket').bind('click', (e) =>
|
||||||
|
user = undefined
|
||||||
|
user_id = $(e.currentTarget).data('user-id')
|
||||||
|
if user_id
|
||||||
|
user = App.User.find(user_id)
|
||||||
|
console.log('user_id', user_id, user)
|
||||||
|
@newTicket(user)
|
||||||
|
)
|
||||||
|
)
|
||||||
@bind('auth', (data) =>
|
@bind('auth', (data) =>
|
||||||
@meta.counter = 0
|
@meta.counter = 0
|
||||||
)
|
)
|
||||||
|
@ -57,6 +78,13 @@ class App.CTI extends App.Controller
|
||||||
@initSpoolSent = true
|
@initSpoolSent = true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ringingCalls: =>
|
||||||
|
ringing = []
|
||||||
|
for row in @list
|
||||||
|
if row.state is 'newCall' && row.done is false
|
||||||
|
ringing.push row
|
||||||
|
ringing
|
||||||
|
|
||||||
# fetch data, render view
|
# fetch data, render view
|
||||||
load: ->
|
load: ->
|
||||||
@ajax(
|
@ajax(
|
||||||
|
@ -148,8 +176,18 @@ class App.CTI extends App.Controller
|
||||||
item.disabled = false
|
item.disabled = false
|
||||||
|
|
||||||
@removePopovers()
|
@removePopovers()
|
||||||
@callerLog.html(App.view('cti/caller_log')(list: @list))
|
|
||||||
@renderPopovers()
|
list = $(App.view('cti/caller_log')(list: @list))
|
||||||
|
list.find('.js-avatar').each( ->
|
||||||
|
$element = $(@)
|
||||||
|
new WidgetAvatar(
|
||||||
|
el: $element
|
||||||
|
object_id: $element.attr('data-id')
|
||||||
|
level: $element.attr('data-level')
|
||||||
|
size: 40
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@callerLog.html(list)
|
||||||
|
|
||||||
@updateNavMenu()
|
@updateNavMenu()
|
||||||
|
|
||||||
|
@ -163,9 +201,15 @@ class App.CTI extends App.Controller
|
||||||
data: JSON.stringify(done: done)
|
data: JSON.stringify(done: done)
|
||||||
)
|
)
|
||||||
|
|
||||||
userNew: (e) ->
|
newTicket: (user) =>
|
||||||
|
if user
|
||||||
|
@navigate("ticket/create/customer/#{user.id}")
|
||||||
|
return
|
||||||
|
@navigate('ticket/create')
|
||||||
|
|
||||||
|
newUser: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
phone = $(e.currentTarget).text()
|
phone = $(e.currentTarget).data('phone')
|
||||||
new App.ControllerGenericNew(
|
new App.ControllerGenericNew(
|
||||||
pageData:
|
pageData:
|
||||||
title: 'Users'
|
title: 'Users'
|
||||||
|
@ -176,7 +220,7 @@ class App.CTI extends App.Controller
|
||||||
genericObject: 'User'
|
genericObject: 'User'
|
||||||
item:
|
item:
|
||||||
phone: phone
|
phone: phone
|
||||||
container: @el.closest('.content')
|
#container: @el.closest('.content')
|
||||||
callback: @ticketNew
|
callback: @ticketNew
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -221,6 +265,33 @@ class App.CTI extends App.Controller
|
||||||
currentPosition: =>
|
currentPosition: =>
|
||||||
@$('.main').scrollTop()
|
@$('.main').scrollTop()
|
||||||
|
|
||||||
|
class WidgetAvatar extends App.ObserverController
|
||||||
|
@extend App.PopoverProvidable
|
||||||
|
@registerPopovers 'User'
|
||||||
|
|
||||||
|
model: 'User'
|
||||||
|
observe:
|
||||||
|
login: true
|
||||||
|
firstname: true
|
||||||
|
lastname: true
|
||||||
|
organization_id: true
|
||||||
|
email: true
|
||||||
|
image: true
|
||||||
|
vip: true
|
||||||
|
out_of_office: true,
|
||||||
|
out_of_office_start_at: true,
|
||||||
|
out_of_office_end_at: true,
|
||||||
|
out_of_office_replacement_id: true,
|
||||||
|
active: true
|
||||||
|
|
||||||
|
globalRerender: false
|
||||||
|
|
||||||
|
render: (user) =>
|
||||||
|
classes = ['user-popover', 'u-textTruncate']
|
||||||
|
classes.push('is-inactive') if !user.active
|
||||||
|
@html(App.view('cti/caller_log_avatar')(user: user, classes: classes, level: @level))
|
||||||
|
@renderPopovers()
|
||||||
|
|
||||||
class CTIRouter extends App.ControllerPermanent
|
class CTIRouter extends App.ControllerPermanent
|
||||||
requiredPermission: 'cti.agent'
|
requiredPermission: 'cti.agent'
|
||||||
constructor: (params) ->
|
constructor: (params) ->
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
class Widget extends App.Controller
|
||||||
|
serverRestarted: false
|
||||||
|
constructor: ->
|
||||||
|
super
|
||||||
|
|
||||||
|
App.Event.bind(
|
||||||
|
'remote_task'
|
||||||
|
(data) =>
|
||||||
|
console.log('remote_task', data)
|
||||||
|
App.TaskManager.execute(data)
|
||||||
|
@navigate(data.url)
|
||||||
|
'remote_task'
|
||||||
|
)
|
||||||
|
|
||||||
|
App.Config.set('remote_task', Widget, 'Widgets')
|
|
@ -200,6 +200,20 @@ App.ViewHelpers =
|
||||||
return true if contentType.match(/image\/(png|jpg|jpeg|gif)/i)
|
return true if contentType.match(/image\/(png|jpg|jpeg|gif)/i)
|
||||||
false
|
false
|
||||||
|
|
||||||
|
unique_avatar: (seed, text, size = 40) ->
|
||||||
|
baseSize = 40
|
||||||
|
width = 300 * size/baseSize
|
||||||
|
height = 226 * size/baseSize
|
||||||
|
|
||||||
|
rng = new Math.seedrandom(seed)
|
||||||
|
x = rng() * (width - size)
|
||||||
|
y = rng() * (height - size)
|
||||||
|
|
||||||
|
return App.view('avatar_unique')
|
||||||
|
x: x
|
||||||
|
y: y
|
||||||
|
initials: text
|
||||||
|
|
||||||
# icon with modifier based on visibility state
|
# icon with modifier based on visibility state
|
||||||
# params: className, iconset, addStateClass
|
# params: className, iconset, addStateClass
|
||||||
iconWithModifier: (item, params) ->
|
iconWithModifier: (item, params) ->
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% for item in @list: %>
|
<% for item in @list: %>
|
||||||
<tr <% if item.done: %>class="is-grayed-out"<% end %> data-id="<%- item.id %>">
|
<tr class="u-center<% if item.done: %> is-grayed-out<% end %>" data-id="<%- item.id %>">
|
||||||
<td class="table-checkbox" style="vertical-align: middle">
|
<td class="table-checkbox u-positionOrigin">
|
||||||
<label class="checkbox-replacement<% if item.disabled is true: %> is-disabled<% end %>">
|
<label class="checkbox-replacement checkbox-replacement--fullscreen<% if item.disabled is true: %> is-disabled<% end %>">
|
||||||
<input type="checkbox" class="js-check"<% if item.done: %> checked<% end %><% if item.disabled is true: %> disabled<% end %>>
|
<input type="checkbox" class="js-check"<% if item.done: %> checked<% end %><% if item.disabled is true: %> disabled<% end %>>
|
||||||
<%- @Icon('checkbox', 'icon-unchecked') %>
|
<%- @Icon('checkbox', 'icon-unchecked') %>
|
||||||
<%- @Icon('checkbox-checked', 'icon-checked') %>
|
<%- @Icon('checkbox-checked', 'icon-checked') %>
|
||||||
|
@ -26,31 +26,42 @@
|
||||||
<% if item.preferences.from && !_.isEmpty(item.preferences.from): %>
|
<% if item.preferences.from && !_.isEmpty(item.preferences.from): %>
|
||||||
<% for caller_id in item.preferences.from: %>
|
<% for caller_id in item.preferences.from: %>
|
||||||
<% if caller_id.user_id && App.User.exists(caller_id.user_id): %>
|
<% if caller_id.user_id && App.User.exists(caller_id.user_id): %>
|
||||||
|
<% if shown: %><div class="spacer"></div><% end %>
|
||||||
|
<div class="user-card">
|
||||||
|
<div class="js-avatar" data-id="<%- caller_id.user_id %>" data-level="<%= caller_id.level %>"></div>
|
||||||
|
<a class="text-muted" href="<%- App.Utils.phoneify(item.from_pretty) %>"><%= item.from_pretty %></a>
|
||||||
|
</div>
|
||||||
<% shown = true %>
|
<% shown = true %>
|
||||||
<% user = App.User.fullLocal(caller_id.user_id) %>
|
|
||||||
<% classes = ['user-popover'] %>
|
|
||||||
<% classes.push('is-inactive') if !user.active %>
|
|
||||||
<% if caller_id.level isnt 'known': %><%- @T('maybe') %> <% end %>
|
|
||||||
<span class="<%= classes.join(' ') %>" data-id="<%- user.id %>"><%= user.displayNameLong() %></span><br>
|
|
||||||
<% else if !_.isEmpty(caller_id.comment): %>
|
<% else if !_.isEmpty(caller_id.comment): %>
|
||||||
<% shown = true %>
|
<% shown = true %>
|
||||||
<%- @T('maybe') %> <%= caller_id.comment %><br>
|
<div class="user-card">
|
||||||
|
<%- @unique_avatar(caller_id.comment, caller_id.comment.split(" ").map((name) -> name[0]).join("")) %>
|
||||||
|
<%- @T('maybe') %>: <%= caller_id.comment %><br>
|
||||||
|
<a class="text-muted" href="<%- App.Utils.phoneify(item.from_pretty) %>"><%= item.from_pretty %></a>
|
||||||
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if !shown && !_.isEmpty(item.from_comment): %>
|
<% if !shown && !_.isEmpty(item.from_comment): %>
|
||||||
<% shown = true %>
|
<% shown = true %>
|
||||||
<%= item.from_comment %>
|
<div class="user-card">
|
||||||
<br>
|
<%- @unique_avatar(item.from_comment, item.from_comment.split(" ").map((name) -> name[0]).join("")) %>
|
||||||
|
<%= item.from_comment %><br>
|
||||||
|
<a class="text-muted" href="<%- App.Utils.phoneify(item.from_pretty) %>"><%= item.from_pretty %></a>
|
||||||
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if shown: %>
|
<% if !shown: %>
|
||||||
<small><%= item.from_pretty %></small>
|
<div class="user-card">
|
||||||
<% else: %>
|
<%- @unique_avatar(item.from_pretty || item.from, '??') %>
|
||||||
<% if !_.isEmpty(item.from_pretty): %>
|
<% if !_.isEmpty(item.from_pretty): %>
|
||||||
<span class="js-userNew u-clickable" href="#"><%= item.from_pretty %></span>
|
<a class="inherit-color" href="<%- App.Utils.phoneify(item.from_pretty) %>"><%= item.from_pretty %></a>
|
||||||
|
<% if item.direction is 'in': %>
|
||||||
|
<div class="btn btn--text btn--create no-padding js-newUser" href="#" data-phone="<%= item.from_pretty %>"><%- @Icon('plus-small') %> <span><%- @T('Create User') %></div>
|
||||||
|
<% end %>
|
||||||
<% else: %>
|
<% else: %>
|
||||||
<span><%= item.from %></span>
|
<a class="inherit-color" href="<%- App.Utils.phoneify(item.from) %>"><%= item.from %></a>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -59,41 +70,53 @@
|
||||||
<% for caller_id in item.preferences.to: %>
|
<% for caller_id in item.preferences.to: %>
|
||||||
<% if caller_id.user_id && App.User.exists(caller_id.user_id): %>
|
<% if caller_id.user_id && App.User.exists(caller_id.user_id): %>
|
||||||
<% shown = true %>
|
<% shown = true %>
|
||||||
<% user = App.User.fullLocal(caller_id.user_id) %>
|
<div class="user-card">
|
||||||
<% classes = ['user-popover'] %>
|
<div class="js-avatar" data-id="<%- caller_id.user_id %>" data-level="<%= caller_id.level %>"></div>
|
||||||
<% classes.push('is-inactive') if !user.active %>
|
<a class="text-muted" href="<%- App.Utils.phoneify(item.to_pretty) %>"><%= item.to_pretty %></a>
|
||||||
<% if caller_id.level isnt 'known': %><%- @T('maybe') %> <% end %>
|
</div>
|
||||||
<span class="<%= classes.join(' ') %>" data-id="<%- user.id %>"><%= user.displayNameLong() %></span><br>
|
|
||||||
<% else if !_.isEmpty(caller_id.comment): %>
|
<% else if !_.isEmpty(caller_id.comment): %>
|
||||||
<% shown = true %>
|
<% shown = true %>
|
||||||
<%- @T('maybe') %> <%= caller_id.comment %><br>
|
<div class="user-card">
|
||||||
|
<%- @unique_avatar(caller_id.comment, caller_id.comment.split(" ").map((name) -> name[0]).join("")) %>
|
||||||
|
<%- @T('maybe') %>: <%= caller_id.comment %><br>
|
||||||
|
<a class="text-muted" href="<%- App.Utils.phoneify(item.to_pretty) %>"><%= item.to_pretty %></a>
|
||||||
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if !shown && !_.isEmpty(item.to_comment): %>
|
<% if !shown && !_.isEmpty(item.to_comment): %>
|
||||||
<% shown = true %>
|
<% shown = true %>
|
||||||
<%= item.to_comment %>
|
<div class="user-card">
|
||||||
<br>
|
<%- @unique_avatar(item.to_comment, item.to_comment.split(" ").map((name) -> name[0]).join("")) %>
|
||||||
|
<%= item.to_comment %><br>
|
||||||
|
<a class="text-muted" href="<%- App.Utils.phoneify(item.to_pretty) %>"><%= item.to_pretty %></a>
|
||||||
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if shown: %>
|
<% if !shown: %>
|
||||||
<small><%= item.to_pretty %></small>
|
<div class="user-card">
|
||||||
<% else: %>
|
<% if item.direction isnt 'in': %>
|
||||||
|
<%- @unique_avatar(item.to_pretty || item.to, '??') %>
|
||||||
|
<% end %>
|
||||||
<% if !_.isEmpty(item.to_pretty): %>
|
<% if !_.isEmpty(item.to_pretty): %>
|
||||||
<%= item.to_pretty %>
|
<a class="inherit-color" href="<%- App.Utils.phoneify(item.to_pretty) %>"><%= item.to_pretty %></a>
|
||||||
|
<% if item.direction is 'out': %>
|
||||||
|
<div class="btn btn--text btn--create no-padding js-newUser" href="#" data-phone="<%= item.to_pretty %>"><%- @Icon('plus-small') %> <span><%- @T('Create User') %></div>
|
||||||
|
<% end %>
|
||||||
<% else: %>
|
<% else: %>
|
||||||
<%= item.to %>
|
<a class="inherit-color" href="<%- App.Utils.phoneify(item.to) %>"><%= item.to %></a>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</td>
|
</td>
|
||||||
<!--<td style="vertical-align: middle"><%= item.queue %></td>-->
|
<!--<td style="vertical-align: middle"><%= item.queue %></td>-->
|
||||||
<td style="vertical-align: middle">
|
<td>
|
||||||
<% if item.state_human: %>
|
<% if item.state_human: %>
|
||||||
<%- @Icon('status', "#{item.status_class} inline") %> <%- @T(item.state_human) %>
|
<%- @Icon('status', "#{item.status_class} inline") %> <%- @T(item.state_human) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</td>
|
</td>
|
||||||
<td style="vertical-align: middle"><%= @time_duration(item.duration_waiting_time) %></td>
|
<td><%= @time_duration(item.duration_waiting_time) %></td>
|
||||||
<td style="vertical-align: middle"><%= @time_duration(item.duration_talking_time) %></td>
|
<td><%= @time_duration(item.duration_talking_time) %></td>
|
||||||
<td style="vertical-align: middle"><%- @humanTime(item.created_at) %></td>
|
<td><%- @humanTime(item.created_at) %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
<%- @user.avatar() %>
|
||||||
|
<div class="<%= @classes.join(' ') %>" data-id="<%- @user.id %>"><% if @level isnt 'known': %><%- @T('maybe') %>: <% end %><%= @user.displayNameLong() %></div>
|
|
@ -93,5 +93,32 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2><%- @T('Notify Map') %></h2>
|
||||||
|
|
||||||
|
<p><%- @T('Notify certain users by matching caller id.') %><%- @T('If no mapping is defined, all user get notified about any event.') %>
|
||||||
|
|
||||||
|
<div class="settings-entry">
|
||||||
|
<table class="settings-list js-notifyMap" style="width: 100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="30%"><%- @T('Destination caller id or queue') %>
|
||||||
|
<th width="60%"><%- @T('Agents') %>
|
||||||
|
<th width="10%"><%- @T('Action') %>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% for row in @config.notify_map: %>
|
||||||
|
<tr class="js-row">
|
||||||
|
<td class="settings-list-control-cell"><input name="queue" value="<%= row.queue %>" class="form-control form-control--small js-summary">
|
||||||
|
<td class="settings-list-control-cell js-userSelector">
|
||||||
|
<td class="settings-list-row-control"><div class="btn btn--text js-removeMap"><%- @Icon('trash') %> <%- @T('Remove') %></div>
|
||||||
|
<% end %>
|
||||||
|
<tr>
|
||||||
|
<td class="settings-list-control-cell"><input name="queue" value="" placeholder="4930609854189" class="form-control form-control--small js-summary">
|
||||||
|
<td class="settings-list-control-cell js-userSelectorBlank">
|
||||||
|
<td class="settings-list-row-control"><div class="btn btn--text btn--create js-addMap"><%- @Icon('plus-small') %> <%- @T('Add') %></div>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn--primary js-submit"><%- @T('Save') %></button>
|
<button type="submit" class="btn btn--primary js-submit"><%- @T('Save') %></button>
|
||||||
</form>
|
</form>
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<h2>Placetel <%- @T('Settings') %></h2>
|
<h2>Placetel <%- @T('Settings') %></h2>
|
||||||
|
|
||||||
<p><%- @T('You need to configure the Zammad endpoints in the %s', 'Placetel') %>:<p>
|
<p><%- @T('You need to configure the Zammad endpoints in the %s web interface', 'Placetel') %>:<p>
|
||||||
|
|
||||||
<div class="settings-entry">
|
<div class="settings-entry">
|
||||||
<table class="settings-list" style="width: 100%;">
|
<table class="settings-list" style="width: 100%;">
|
||||||
|
@ -109,5 +109,32 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
|
<h2><%- @T('User assignment to telephones') %></h2>
|
||||||
|
|
||||||
|
<p><%- @T('User assignment to telephones to be able to offer extended like open new ticket screen on answering a call.') %>
|
||||||
|
|
||||||
|
<div class="settings-entry">
|
||||||
|
<table class="settings-list js-userDeviceMap" style="width: 100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="45%"><%- @T('Placetel') %>/<%- @T('Device') %>
|
||||||
|
<th width="45%"><%- @T('Zammad') %>/<%- @T('User') %>
|
||||||
|
<th width="10%"><%- @T('Action') %>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% for row in @config.user_device_map: %>
|
||||||
|
<tr class="js-row">
|
||||||
|
<td class="settings-list-control-cell"><input name="device_id" value="<%= row.device_id %>" class="form-control form-control--small js-summary">
|
||||||
|
<td class="settings-list-control-cell"><input name="user_id" value="<%= row.user_id %>" 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="device_id" value="" placeholder="e. g. 777042617425@fpbx.de" class="form-control form-control--small js-summary">
|
||||||
|
<td class="settings-list-control-cell"><input name="user_id" value="" placeholder="<%- @Ti('e. g. user@example.com') %>" 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>
|
||||||
|
|
||||||
<button type="submit" class="btn btn--primary js-submit"><%- @T('Save') %></button>
|
<button type="submit" class="btn btn--primary js-submit"><%- @T('Save') %></button>
|
||||||
</form>
|
</form>
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<h2>sipgate.io <%- @T('Settings') %></h2>
|
<h2>sipgate.io <%- @T('Settings') %></h2>
|
||||||
|
|
||||||
<p><%- @T('You need to configure the Zammad endpoints in the Sipgate web interface') %>:<p>
|
<p><%- @T('You need to configure the Zammad endpoints in the %s web interface', 'Sipgate') %>:<p>
|
||||||
|
|
||||||
<div class="settings-entry">
|
<div class="settings-entry">
|
||||||
<table class="settings-list" style="width: 100%;">
|
<table class="settings-list" style="width: 100%;">
|
||||||
|
@ -96,5 +96,51 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<p><%- @T('In order for Zammad to access %s, a %s API user and password must be stored here', 'Sipgate', 'Sipgate') %>:<p>
|
||||||
|
<div class="settings-entry">
|
||||||
|
<table class="settings-list" style="width: 100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="20%"><%- @T('Type') %>
|
||||||
|
<th width="80%"><%- @T('Content') %>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="settings-list-row-control"><%- @T('API User') %>
|
||||||
|
<td class="settings-list-control-cell"><input type="input" class="form-control form-control--small js-select" value="<%= @config.api_user %>" name="api_user" placeholder="someuser@example.com">
|
||||||
|
<tr>
|
||||||
|
<td class="settings-list-row-control"><%- @T('API Password') %>
|
||||||
|
<td class="settings-list-control-cell"><input type="password" class="form-control form-control--small js-select" value="<%= @config.api_password %>" name="api_password" placeholder="">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2><%- @T('User assignment to Sipgate users') %></h2>
|
||||||
|
|
||||||
|
<p><%- @T('User assignment to Sipgate users to be able to offer extended like open new ticket screen on answering a call.') %>
|
||||||
|
|
||||||
|
<div class="settings-entry">
|
||||||
|
<table class="settings-list js-userRemoteMap" style="width: 100%;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="45%"><%- @T('Sipgate') %>/<%- @T('User') %>
|
||||||
|
<th width="45%"><%- @T('Zammad') %>/<%- @T('User') %>
|
||||||
|
<th width="10%"><%- @T('Action') %>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% for row in @config.user_remote_map: %>
|
||||||
|
<tr class="js-row">
|
||||||
|
<td class="settings-list-control-cell"><input name="remote_user_id" value="<%= row.remote_user_id %>" class="form-control form-control--small js-summary">
|
||||||
|
<td class="settings-list-control-cell"><input name="user_id" value="<%= row.user_id %>" 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="remote_user_id" value="" placeholder="e. g. W123" class="form-control form-control--small js-summary">
|
||||||
|
<td class="settings-list-control-cell"><input name="user_id" value="" placeholder="<%- @Ti('e. g. user@example.com') %>" 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>
|
||||||
|
|
||||||
<button type="submit" class="btn btn--primary js-submit"><%- @T('Save') %></button>
|
<button type="submit" class="btn btn--primary js-submit"><%- @T('Save') %></button>
|
||||||
</form>
|
</form>
|
|
@ -14,7 +14,7 @@
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if item.navheader: %>
|
<% if item.navheader: %>
|
||||||
<li class="dropdown-header"><%- @T( item.navheader ) %></li>
|
<li class="dropdown-header"><%- @T(item.navheader) %></li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<li><a href="<%= item.target %>"><%- @T( item.name ) %><% if item['count'] isnt undefined: %><span class="badge badge--text count"><%= item['count'] %></span><% end %></a></li>
|
<li><a href="<%= item.target %>"><%- @T( item.name ) %><% if item['count'] isnt undefined: %><span class="badge badge--text count"><%= item['count'] %></span><% end %></a></li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
<div class="call-widget">
|
||||||
|
<div class="call-widget-header">
|
||||||
|
<%- @Icon('status', "neutral") %>
|
||||||
|
<div class="label"><%- @T('Inbound Call') %></div>
|
||||||
|
<!--
|
||||||
|
<div class="flex-spacer"></div>
|
||||||
|
<div class="btn btn--text js-remove" title="<%- @Ti('Remove') %>"><%- @Icon('diagonal-cross') %></div>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
<div class="horizontal center">
|
||||||
|
<% user = undefined %>
|
||||||
|
<% shown = false %>
|
||||||
|
<% if @item.preferences.from && !_.isEmpty(@item.preferences.from): %>
|
||||||
|
<% for caller_id in @item.preferences.from: %>
|
||||||
|
<% if caller_id.user_id && App.User.exists(caller_id.user_id): %>
|
||||||
|
<% user = App.User.fullLocal(caller_id.user_id) %>
|
||||||
|
<% classes = ['user-popover', 'u-textTruncate'] %>
|
||||||
|
<% classes.push('is-inactive') if !user.active %>
|
||||||
|
<% if shown: %><div class="spacer"></div><% end %>
|
||||||
|
<div class="user-card">
|
||||||
|
<a href="<%- user.uiUrl() %>"><%- user.avatar() %></a>
|
||||||
|
<div class="<%= classes.join(' ') %>" data-id="<%- user.id %>"><% if caller_id.level isnt 'known': %><%- @T('maybe') %>: <% end %><%= user.displayNameLong() %></div>
|
||||||
|
<span class="text-muted"><%= @item.from_pretty %></span>
|
||||||
|
</div>
|
||||||
|
<% shown = true %>
|
||||||
|
<% else if !_.isEmpty(caller_id.comment): %>
|
||||||
|
<% shown = true %>
|
||||||
|
<div class="user-card">
|
||||||
|
<%- @unique_avatar(caller_id.comment, caller_id.comment.split(" ").map((name) -> name[0]).join("")) %>
|
||||||
|
<%- @T('maybe') %>: <%= caller_id.comment %><br>
|
||||||
|
<span class="text-muted"><%= @item.from_pretty %></span>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% if !shown && !_.isEmpty(@item.from_comment): %>
|
||||||
|
<% shown = true %>
|
||||||
|
<div class="user-card">
|
||||||
|
<%- @unique_avatar(@item.from_comment, @item.from_comment.split(" ").map((name) -> name[0]).join("")) %>
|
||||||
|
<%= @item.from_comment %><br>
|
||||||
|
<span class="text-muted"><%= @item.from_pretty %></span>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if !shown: %>
|
||||||
|
<div class="user-card">
|
||||||
|
<%- @unique_avatar(@item.from_pretty || @item.from, '??') %>
|
||||||
|
<% if !_.isEmpty(@item.from_pretty): %>
|
||||||
|
<a class="inherit-color" href="<%- App.Utils.phoneify(@item.from_pretty) %>"><%= @item.from_pretty %></a>
|
||||||
|
<% if @item.direction is 'in': %>
|
||||||
|
<div class="btn btn--text btn--create no-padding js-newUser" href="#" data-phone="<%= @item.from_pretty %>"><%- @Icon('plus-small') %> <span><%- @T('Create User') %></div>
|
||||||
|
<% end %>
|
||||||
|
<% else: %>
|
||||||
|
<a class="inherit-color" href="<%- App.Utils.phoneify(@item.from) %>"><%= @item.from %></a>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<div class="flex-spacer"></div>
|
||||||
|
<div class="btn btn--small btn--quad btn--create space-left js-newTicket" title="<%- @Ti('New Ticket') %>" data-user-id="<% if user: %><%- user.id %><% end %>"><%- @Icon('plus') %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -115,6 +115,10 @@ strong {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inherit-color {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.text-muted {
|
.text-muted {
|
||||||
color: hsl(60,1%,74%);
|
color: hsl(60,1%,74%);
|
||||||
}
|
}
|
||||||
|
@ -419,6 +423,10 @@ pre code.hljs {
|
||||||
&--small {
|
&--small {
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
|
|
||||||
|
&.btn--quad {
|
||||||
|
padding: 4px 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--slim {
|
&--slim {
|
||||||
|
@ -588,12 +596,18 @@ pre code.hljs {
|
||||||
background: none;
|
background: none;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
|
|
||||||
.table & {
|
.table & {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-height: 38px;
|
min-height: 38px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.no-padding {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
|
@ -662,7 +676,7 @@ pre code.hljs {
|
||||||
padding: 10px 12px 9px;
|
padding: 10px 12px 9px;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin-top: -1px;
|
margin: -1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1111,6 +1125,10 @@ th.align-right {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table > tbody > tr.u-center > td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
.table-hover > tbody > tr:hover,
|
.table-hover > tbody > tr:hover,
|
||||||
.table-hover > tbody > tr.is-hover {
|
.table-hover > tbody > tr.is-hover {
|
||||||
background: white;
|
background: white;
|
||||||
|
@ -1161,9 +1179,15 @@ th.align-right {
|
||||||
.table tr.is-grayed-out {
|
.table tr.is-grayed-out {
|
||||||
color: hsl(120,1%,77%);
|
color: hsl(120,1%,77%);
|
||||||
|
|
||||||
.icon {
|
.icon,
|
||||||
|
.btn span {
|
||||||
opacity: 0.33;
|
opacity: 0.33;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
background: hsl(120,1%,86%);
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
td .icon {
|
td .icon {
|
||||||
|
@ -1321,6 +1345,11 @@ td .icon-trash {
|
||||||
.table .radio-replacement {
|
.table .radio-replacement {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
|
|
||||||
|
&.checkbox-replacement--fullscreen {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-overview tbody .icon-checkbox,
|
.table-overview tbody .icon-checkbox,
|
||||||
|
@ -3665,6 +3694,45 @@ footer {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.call-widget {
|
||||||
|
background: hsl(228,17%,91%);
|
||||||
|
padding: 8px 10px;
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
border-top: 1px solid hsl(228,10%,81%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
display: flex;
|
||||||
|
color: inherit;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: inherit;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn--text {
|
||||||
|
color: inherit;
|
||||||
|
opacity: .5;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-diagonal-cross {
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
margin-top: -5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-muted {
|
||||||
|
color: hsl(228,6%,67%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.tasks {
|
.tasks {
|
||||||
background: #2c2d36;
|
background: #2c2d36;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -4810,6 +4878,22 @@ footer {
|
||||||
@extend .u-clickable;
|
@extend .u-clickable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-card {
|
||||||
|
padding: 2px 0 0 50px;
|
||||||
|
position: relative;
|
||||||
|
min-height: 40px;
|
||||||
|
max-width: 192px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.stat-icon {
|
.stat-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ class CtiController < ApplicationController
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
result = Cti::Log.log
|
result = Cti::Log.log(current_user)
|
||||||
result[:backends] = backends
|
result[:backends] = backends
|
||||||
render json: result
|
render json: result
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,75 +6,35 @@ class Integration::CtiController < ApplicationController
|
||||||
|
|
||||||
# notify about inbound call / block inbound call
|
# notify about inbound call / block inbound call
|
||||||
def event
|
def event
|
||||||
if params['direction'] == 'in'
|
local_params = ActiveSupport::HashWithIndifferentAccess.new(params.permit!.to_h)
|
||||||
if params['event'] == 'newCall'
|
|
||||||
config_inbound = config_integration[:inbound] || {}
|
|
||||||
block_caller_ids = config_inbound[:block_caller_ids] || []
|
|
||||||
|
|
||||||
# check if call need to be blocked
|
cti = Cti::Driver::Cti.new(params: local_params, config: config_integration)
|
||||||
block_caller_ids.each do |item|
|
|
||||||
next unless item[:caller_id] == params['from']
|
|
||||||
|
|
||||||
render json: { action: 'reject', reason: 'busy' }, status: :ok
|
result = cti.process
|
||||||
|
|
||||||
#params['Reject'] = 'busy'
|
# check if inbound call should get rejected
|
||||||
params['comment'] = 'reject, busy'
|
if result[:action] == 'reject'
|
||||||
if params['user']
|
response_ok(action: 'reject', reason: 'busy')
|
||||||
params['comment'] = "#{params['user']} -> reject, busy"
|
|
||||||
end
|
|
||||||
Cti::Log.process(params)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Cti::Log.process(params)
|
|
||||||
|
|
||||||
render json: {}, status: :ok
|
|
||||||
return true
|
|
||||||
elsif 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
|
|
||||||
|
|
||||||
# set callerId
|
|
||||||
data = {}
|
|
||||||
to = 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]
|
|
||||||
data = {
|
|
||||||
action: 'dial',
|
|
||||||
caller_id: from,
|
|
||||||
number: params[:to]
|
|
||||||
}
|
|
||||||
break
|
|
||||||
end
|
|
||||||
if data.blank? && default_caller_id.present?
|
|
||||||
from = default_caller_id
|
|
||||||
data = {
|
|
||||||
action: 'dial',
|
|
||||||
caller_id: default_caller_id,
|
|
||||||
number: params[:to]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
render json: data, status: :ok
|
|
||||||
|
|
||||||
if from.present?
|
|
||||||
params['from'] = from
|
|
||||||
end
|
|
||||||
Cti::Log.process(params)
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
render json: { error: 'Invalid direction!' }, status: :unprocessable_entity
|
|
||||||
|
# check if oubound call change the outbound caller_id
|
||||||
|
if result[:action] == 'set_caller_id'
|
||||||
|
data = {
|
||||||
|
action: 'dial',
|
||||||
|
caller_id: result[:params][:from_caller_id],
|
||||||
|
number: result[:params][:to_caller_id],
|
||||||
|
}
|
||||||
|
response_ok(data)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if result[:action] == 'invalid_direction'
|
||||||
|
response_error('Invalid direction!')
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
response_ok({})
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -115,4 +75,8 @@ class Integration::CtiController < ApplicationController
|
||||||
render json: { error: error }, status: :unauthorized
|
render json: { error: error }, status: :unauthorized
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def response_ok(data)
|
||||||
|
render json: data, status: :ok
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,115 +10,29 @@ class Integration::PlacetelController < ApplicationController
|
||||||
|
|
||||||
local_params = ActiveSupport::HashWithIndifferentAccess.new(params.permit!.to_h)
|
local_params = ActiveSupport::HashWithIndifferentAccess.new(params.permit!.to_h)
|
||||||
|
|
||||||
# do placetel event mapping
|
cti = Cti::Driver::Placetel.new(params: local_params, config: config_integration)
|
||||||
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['user'].blank? && local_params['peer']
|
result = cti.process
|
||||||
local_params['user'] = get_voip_user_by_peer(local_params['peer'])
|
|
||||||
end
|
|
||||||
|
|
||||||
if local_params['direction'].blank?
|
# check if inbound call should get rejected
|
||||||
entry = Cti::Log.find_by(call_id: params[:call_id])
|
if result[:action] == 'reject'
|
||||||
if entry
|
response_reject(result)
|
||||||
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
|
return true
|
||||||
end
|
end
|
||||||
response_error('Invalid direction!')
|
|
||||||
|
# check if oubound call change the outbound caller_id
|
||||||
|
if result[:action] == 'set_caller_id'
|
||||||
|
response_set_caller_id(result)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if result[:action] == 'invalid_direction'
|
||||||
|
response_error('Invalid direction!')
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
response_ok(response)
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -168,65 +82,29 @@ class Integration::PlacetelController < ApplicationController
|
||||||
xml_error(error, 401)
|
xml_error(error, 401)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_voip_user_by_peer(peer)
|
def response_reject(_result)
|
||||||
load_voip_users[peer]
|
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;'
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_voip_users
|
def response_set_caller_id(result)
|
||||||
return {} if config_integration.blank? || config_integration[:api_token].blank?
|
xml = Builder::XmlMarkup.new(indent: 2)
|
||||||
|
xml.instruct!
|
||||||
list = Cache.get('placetelGetVoipUsers')
|
content = xml.Response() do
|
||||||
return list if list
|
xml.Dial(callerId: result[:params][:from_caller_id]) { xml.Number(result[:params][:to_caller_id]) }
|
||||||
|
|
||||||
response = UserAgent.post(
|
|
||||||
'https://api.placetel.de/api/getVoIPUsers.json',
|
|
||||||
{
|
|
||||||
api_key: config_integration[:api_token],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
log: {
|
|
||||||
facility: 'placetel',
|
|
||||||
},
|
|
||||||
json: true,
|
|
||||||
open_timeout: 4,
|
|
||||||
read_timeout: 6,
|
|
||||||
total_timeout: 6,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if !response.success?
|
|
||||||
logger.error "Can't fetch getVoipUsers from '#{url}', http code: #{response.code}"
|
|
||||||
Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour })
|
|
||||||
return {}
|
|
||||||
end
|
end
|
||||||
result = response.data
|
send_data(content, type: 'application/xml; charset=UTF-8;')
|
||||||
if result.blank?
|
|
||||||
logger.error "Can't fetch getVoipUsers from '#{url}', result: #{response.inspect}"
|
|
||||||
Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour })
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
if result.is_a?(Hash) && (result['result'] == '-1' || result['result_code'] == 'error')
|
|
||||||
logger.error "Can't fetch getVoipUsers from '#{url}', result: #{result.inspect}"
|
|
||||||
Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour })
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
if !result.is_a?(Array)
|
|
||||||
logger.error "Can't fetch getVoipUsers from '#{url}', result: #{result.inspect}"
|
|
||||||
Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour })
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
|
|
||||||
list = {}
|
|
||||||
result.each do |entry|
|
|
||||||
next if entry['name'].blank?
|
|
||||||
|
|
||||||
if entry['uid'].present?
|
|
||||||
list[entry['uid']] = entry['name']
|
|
||||||
end
|
|
||||||
next if entry['uid2'].blank?
|
|
||||||
|
|
||||||
list[entry['uid2']] = entry['name']
|
|
||||||
end
|
|
||||||
Cache.write('placetelGetVoipUsers', list, { expires_in: 24.hours })
|
|
||||||
list
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def response_ok(_result)
|
||||||
|
xml = Builder::XmlMarkup.new(indent: 2)
|
||||||
|
xml.instruct!
|
||||||
|
content = xml.Response()
|
||||||
|
send_data content, type: 'application/xml; charset=UTF-8;'
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,84 +6,33 @@ class Integration::SipgateController < ApplicationController
|
||||||
before_action :check_configured
|
before_action :check_configured
|
||||||
|
|
||||||
# notify about inbound call / block inbound call
|
# notify about inbound call / block inbound call
|
||||||
def in
|
def event
|
||||||
if params['event'] == 'newCall'
|
|
||||||
config_inbound = config_integration[:inbound] || {}
|
|
||||||
block_caller_ids = config_inbound[:block_caller_ids] || []
|
|
||||||
|
|
||||||
# check if call need to be blocked
|
local_params = ActiveSupport::HashWithIndifferentAccess.new(params.permit!.to_h)
|
||||||
block_caller_ids.each do |item|
|
|
||||||
next if item[:caller_id] != params['from']
|
|
||||||
|
|
||||||
xml = Builder::XmlMarkup.new(indent: 2)
|
cti = Cti::Driver::SipgateIo.new(params: local_params, config: config_integration)
|
||||||
xml.instruct!
|
|
||||||
content = xml.Response(onHangup: url, onAnswer: url) do
|
|
||||||
xml.Reject('reason' => 'busy')
|
|
||||||
end
|
|
||||||
|
|
||||||
send_data content, type: 'application/xml; charset=UTF-8;'
|
result = cti.process
|
||||||
|
|
||||||
#params['Reject'] = 'busy'
|
# check if inbound call should get rejected
|
||||||
params['comment'] = 'reject, busy'
|
if result[:action] == 'reject'
|
||||||
if params['user']
|
response_reject(result)
|
||||||
params['comment'] = "#{params['user']} -> reject, busy"
|
return true
|
||||||
end
|
|
||||||
Cti::Log.process(params)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Cti::Log.process(params)
|
# check if oubound call change the outbound caller_id
|
||||||
|
if result[:action] == 'set_caller_id'
|
||||||
xml = Builder::XmlMarkup.new(indent: 2)
|
response_set_caller_id(result)
|
||||||
xml.instruct!
|
return true
|
||||||
content = xml.Response(onHangup: url, onAnswer: url)
|
|
||||||
send_data content, type: 'application/xml; charset=UTF-8;'
|
|
||||||
end
|
|
||||||
|
|
||||||
# set caller id of outbound call
|
|
||||||
def 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
|
end
|
||||||
|
|
||||||
xml = Builder::XmlMarkup.new(indent: 2)
|
if result[:action] == 'invalid_direction'
|
||||||
xml.instruct!
|
response_error('Invalid direction!')
|
||||||
|
return true
|
||||||
# set callerId
|
|
||||||
content = nil
|
|
||||||
to = 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(onHangup: url, onAnswer: url) 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(onHangup: url, onAnswer: url) do
|
|
||||||
xml.Dial(callerId: default_caller_id) { xml.Number(params[:to]) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
content = xml.Response(onHangup: url, onAnswer: url)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
send_data(content, type: 'application/xml; charset=UTF-8;')
|
response_ok(response)
|
||||||
if from.present?
|
true
|
||||||
params['from'] = from
|
|
||||||
end
|
|
||||||
Cti::Log.process(params)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -128,4 +77,30 @@ class Integration::SipgateController < ApplicationController
|
||||||
def url
|
def url
|
||||||
"#{base_url}/#{params['direction']}"
|
"#{base_url}/#{params['direction']}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def response_reject(_result)
|
||||||
|
xml = Builder::XmlMarkup.new(indent: 2)
|
||||||
|
xml.instruct!
|
||||||
|
content = xml.Response(onHangup: url, onAnswer: url) do
|
||||||
|
xml.Reject({ reason: 'busy' })
|
||||||
|
end
|
||||||
|
send_data content, type: 'application/xml; charset=UTF-8;'
|
||||||
|
end
|
||||||
|
|
||||||
|
def response_set_caller_id(result)
|
||||||
|
xml = Builder::XmlMarkup.new(indent: 2)
|
||||||
|
xml.instruct!
|
||||||
|
content = xml.Response(onHangup: url, onAnswer: url) do
|
||||||
|
xml.Dial(callerId: result[:params][:from_caller_id]) { xml.Number(result[:params][:to_caller_id]) }
|
||||||
|
end
|
||||||
|
send_data(content, type: 'application/xml; charset=UTF-8;')
|
||||||
|
end
|
||||||
|
|
||||||
|
def response_ok(_result)
|
||||||
|
xml = Builder::XmlMarkup.new(indent: 2)
|
||||||
|
xml.instruct!
|
||||||
|
content = xml.Response(onHangup: url, onAnswer: url)
|
||||||
|
send_data content, type: 'application/xml; charset=UTF-8;'
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,6 +39,8 @@ module Cti
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
get items (users) for a certain caller id
|
||||||
|
|
||||||
caller_id_records = Cti::CallerId.lookup('49123456789')
|
caller_id_records = Cti::CallerId.lookup('49123456789')
|
||||||
|
|
||||||
returns
|
returns
|
||||||
|
@ -312,6 +314,30 @@ returns
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
return users by caller_id
|
||||||
|
|
||||||
|
[user1, user2] = Cti::CallerId.known_agents_by_number('491234567')
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.known_agents_by_number(number)
|
||||||
|
users = []
|
||||||
|
caller_ids = Cti::CallerId.extract_numbers(number)
|
||||||
|
caller_id_records = Cti::CallerId.lookup(caller_ids)
|
||||||
|
caller_id_records.each do |caller_id_record|
|
||||||
|
next if caller_id_record.level != 'known'
|
||||||
|
|
||||||
|
user = User.find_by(id: caller_id_record.user_id)
|
||||||
|
next if !user
|
||||||
|
next if !user.permissions?('cti.agent')
|
||||||
|
|
||||||
|
users.push user
|
||||||
|
end
|
||||||
|
users
|
||||||
|
end
|
||||||
|
|
||||||
def update_cti_logs
|
def update_cti_logs
|
||||||
return if object != 'User'
|
return if object != 'User'
|
||||||
|
|
||||||
|
|
223
app/models/cti/driver/base.rb
Normal file
223
app/models/cti/driver/base.rb
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
class Cti::Driver::Base
|
||||||
|
|
||||||
|
def initialize(params = {})
|
||||||
|
@config = params[:config] || config
|
||||||
|
@params = mapping(params[:params])
|
||||||
|
end
|
||||||
|
|
||||||
|
def mapping(params)
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
def process
|
||||||
|
|
||||||
|
# validate diections
|
||||||
|
result = direction_check
|
||||||
|
return result if result.present?
|
||||||
|
|
||||||
|
# reject inbound call
|
||||||
|
result = reject_check
|
||||||
|
if result.present? && result[:action] == 'reject'
|
||||||
|
@params['comment'] = 'reject, busy'
|
||||||
|
if @params['user'].present?
|
||||||
|
@params['comment'] = "#{@params['user']} -> reject, busy"
|
||||||
|
end
|
||||||
|
Cti::Log.process(@params)
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
# set caller id of outbound call
|
||||||
|
result = caller_id_rewrite(@params)
|
||||||
|
if result.present? && result[:action] == 'set_caller_id'
|
||||||
|
@params['from'] = result[:params][:from_caller_id]
|
||||||
|
Cti::Log.process(@params)
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
log = Cti::Log.process(@params)
|
||||||
|
|
||||||
|
# push new call notifiation
|
||||||
|
push_incoming_call(log)
|
||||||
|
|
||||||
|
# open screen if call got answerd
|
||||||
|
push_open_ticket_screen(log)
|
||||||
|
|
||||||
|
result || {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def direction_check
|
||||||
|
|
||||||
|
# check possible diections
|
||||||
|
if @params['direction'] != 'in' && @params['direction'] != 'out'
|
||||||
|
return {
|
||||||
|
action: 'invalid_direction',
|
||||||
|
params: @params
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject_check
|
||||||
|
return nil if @params['direction'] != 'in'
|
||||||
|
return nil if @params['event'] != 'newCall'
|
||||||
|
|
||||||
|
config_inbound = @config[:inbound] || {}
|
||||||
|
block_caller_ids = config_inbound[:block_caller_ids] || []
|
||||||
|
|
||||||
|
# check if call need to be blocked
|
||||||
|
block_caller_ids.each do |item|
|
||||||
|
next if item[:caller_id] != @params['from']
|
||||||
|
|
||||||
|
return {
|
||||||
|
action: 'reject'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def caller_id_rewrite(params)
|
||||||
|
return nil if params['direction'] != 'out'
|
||||||
|
return nil if params['event'] != 'newCall'
|
||||||
|
|
||||||
|
config_outbound = @config[: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
|
||||||
|
|
||||||
|
to = params[:to]
|
||||||
|
return nil if to.blank?
|
||||||
|
|
||||||
|
if routing_table.present?
|
||||||
|
routing_table.each do |row|
|
||||||
|
dest = row[:dest].gsub(/\*/, '.+?')
|
||||||
|
next if to !~ /^#{dest}$/
|
||||||
|
|
||||||
|
return {
|
||||||
|
action: 'set_caller_id',
|
||||||
|
params: {
|
||||||
|
from_caller_id: row[:caller_id],
|
||||||
|
to_caller_id: params[:to],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if default_caller_id.present?
|
||||||
|
return {
|
||||||
|
action: 'set_caller_id',
|
||||||
|
params: {
|
||||||
|
from_caller_id: default_caller_id,
|
||||||
|
to_caller_id: params[:to],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def push_open_ticket_screen(log)
|
||||||
|
return if log.destroyed?
|
||||||
|
return if @params[:event] != 'answer'
|
||||||
|
return if @params[:direction] != 'in'
|
||||||
|
|
||||||
|
user = push_open_ticket_screen_recipient
|
||||||
|
return if !user
|
||||||
|
return if !user.permissions?('cti.agent')
|
||||||
|
|
||||||
|
customer_id = log.best_customer_id_of_log_entry
|
||||||
|
|
||||||
|
id = rand(999_999_999)
|
||||||
|
PushMessages.send_to(user.id, {
|
||||||
|
event: 'remote_task',
|
||||||
|
data: {
|
||||||
|
key: "TicketCreateScreen-#{id}",
|
||||||
|
controller: 'TicketCreate',
|
||||||
|
params: { customer_id: customer_id.to_s, title: 'Call', id: id },
|
||||||
|
show: true,
|
||||||
|
url: "ticket/create/id/#{id}"
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def push_open_ticket_screen_recipient
|
||||||
|
|
||||||
|
# try to find answering which answered call
|
||||||
|
user = nil
|
||||||
|
|
||||||
|
# based on answeringNumber
|
||||||
|
if @params[:answeringNumber].present?
|
||||||
|
user = Cti::CallerId.known_agents_by_number(@params[:answeringNumber]).first
|
||||||
|
end
|
||||||
|
|
||||||
|
# based on user param
|
||||||
|
if !user && @params[:user].present?
|
||||||
|
user = User.find_by(login: @params[:user].downcase)
|
||||||
|
end
|
||||||
|
|
||||||
|
# based on user_id param
|
||||||
|
if !user && @params[:user_id].present?
|
||||||
|
user = User.find_by(id: @params[:user_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
def push_incoming_call(log)
|
||||||
|
return if log.destroyed?
|
||||||
|
return if @params[:event] != 'newCall'
|
||||||
|
return if @params[:direction] != 'in'
|
||||||
|
|
||||||
|
# check if only a certain user should get the notification
|
||||||
|
if @config[:notify_map].present?
|
||||||
|
user_ids = []
|
||||||
|
@config[:notify_map].each do |row|
|
||||||
|
next if row[:user_ids].blank? || row[:queue] != @params[:to]
|
||||||
|
|
||||||
|
row[:user_ids].each do |user_id|
|
||||||
|
user = User.find_by(id: user_id)
|
||||||
|
next if !user
|
||||||
|
next if !user.permissions?('cti.agent')
|
||||||
|
|
||||||
|
user_ids.push user.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# add agents which have this number directly assigned
|
||||||
|
Cti::CallerId.known_agents_by_number(@params[:to]).each do |user|
|
||||||
|
next if !user
|
||||||
|
next if !user.permissions?('cti.agent')
|
||||||
|
|
||||||
|
user_ids.push user.id
|
||||||
|
end
|
||||||
|
|
||||||
|
user_ids.uniq.each do |user_id|
|
||||||
|
PushMessages.send_to(
|
||||||
|
user_id,
|
||||||
|
{
|
||||||
|
event: 'cti_event',
|
||||||
|
data: log,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
# send notify about event
|
||||||
|
users = User.with_permissions('cti.agent')
|
||||||
|
users.each do |user|
|
||||||
|
PushMessages.send_to(
|
||||||
|
user.id,
|
||||||
|
{
|
||||||
|
event: 'cti_event',
|
||||||
|
data: log,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
7
app/models/cti/driver/cti.rb
Normal file
7
app/models/cti/driver/cti.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
class Cti::Driver::Cti < Cti::Driver::Base
|
||||||
|
|
||||||
|
def config
|
||||||
|
Setting.get('cti_config')
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
139
app/models/cti/driver/placetel.rb
Normal file
139
app/models/cti/driver/placetel.rb
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
class Cti::Driver::Placetel < Cti::Driver::Base
|
||||||
|
|
||||||
|
def config
|
||||||
|
Setting.get('placetel_config')
|
||||||
|
end
|
||||||
|
|
||||||
|
def mapping(params)
|
||||||
|
|
||||||
|
# do event mapping
|
||||||
|
if params['event'] == 'IncomingCall'
|
||||||
|
params['direction'] = 'in'
|
||||||
|
params['event'] = 'newCall'
|
||||||
|
elsif params['event'] == 'HungUp'
|
||||||
|
params['event'] = 'hangup'
|
||||||
|
elsif params['event'] == 'CallAccepted'
|
||||||
|
params['event'] = 'answer'
|
||||||
|
end
|
||||||
|
|
||||||
|
if params['user'].blank? && params['peer'].present?
|
||||||
|
params['user'] = get_voip_user_by_peer(params['peer'])
|
||||||
|
end
|
||||||
|
|
||||||
|
# lookup current direction if not given
|
||||||
|
if params['direction'].blank?
|
||||||
|
entry = Cti::Log.find_by(call_id: params[:call_id])
|
||||||
|
if entry
|
||||||
|
params['direction'] = entry.direction
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# do case mapping
|
||||||
|
if params['type'] == 'missed'
|
||||||
|
params['cause'] = 'cancel'
|
||||||
|
elsif params['type'] == 'voicemail'
|
||||||
|
params['cause'] = 'voicemail'
|
||||||
|
elsif params['type'] == 'blocked'
|
||||||
|
params['cause'] = 'blocked'
|
||||||
|
elsif params['type'] == 'accepted'
|
||||||
|
params['cause'] = 'normalClearing'
|
||||||
|
end
|
||||||
|
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
def push_open_ticket_screen_recipient
|
||||||
|
|
||||||
|
# try to find answering which answered call
|
||||||
|
user = nil
|
||||||
|
|
||||||
|
# based on peer
|
||||||
|
if @params['peer'].present?
|
||||||
|
user_id = get_user_id_by_peer(@params['peer'])
|
||||||
|
if user_id.present?
|
||||||
|
user = if User.exists?(user_id)
|
||||||
|
User.find(user_id)
|
||||||
|
else
|
||||||
|
User.find_by(email: user_id.downcase)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_voip_user_by_peer(peer)
|
||||||
|
load_voip_users[peer]
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_voip_users
|
||||||
|
return {} if @config.blank? || @config[:api_token].blank?
|
||||||
|
|
||||||
|
list = Cache.get('placetelGetVoipUsers')
|
||||||
|
return list if list
|
||||||
|
|
||||||
|
response = UserAgent.post(
|
||||||
|
'https://api.placetel.de/api/getVoIPUsers.json',
|
||||||
|
{
|
||||||
|
api_key: @config[:api_token],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
log: {
|
||||||
|
facility: 'placetel',
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
open_timeout: 4,
|
||||||
|
read_timeout: 6,
|
||||||
|
total_timeout: 6,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if !response.success?
|
||||||
|
Rails.logger.error "Can't fetch getVoipUsers from '#{url}', http code: #{response.code}"
|
||||||
|
Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour })
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
result = response.data
|
||||||
|
if result.blank?
|
||||||
|
Rails.logger.error "Can't fetch getVoipUsers from '#{url}', result: #{response.inspect}"
|
||||||
|
Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour })
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
if result.is_a?(Hash) && (result['result'] == '-1' || result['result_code'] == 'error')
|
||||||
|
Rails.logger.error "Can't fetch getVoipUsers from '#{url}', result: #{result.inspect}"
|
||||||
|
Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour })
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
if !result.is_a?(Array)
|
||||||
|
Rails.logger.error "Can't fetch getVoipUsers from '#{url}', result: #{result.inspect}"
|
||||||
|
Cache.write('placetelGetVoipUsers', {}, { expires_in: 1.hour })
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
list = {}
|
||||||
|
result.each do |entry|
|
||||||
|
next if entry['name'].blank?
|
||||||
|
|
||||||
|
if entry['uid'].present?
|
||||||
|
list[entry['uid']] = entry['name']
|
||||||
|
end
|
||||||
|
next if entry['uid2'].blank?
|
||||||
|
|
||||||
|
list[entry['uid2']] = entry['name']
|
||||||
|
end
|
||||||
|
Cache.write('placetelGetVoipUsers', list, { expires_in: 24.hours })
|
||||||
|
list
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_user_id_by_peer(peer)
|
||||||
|
return if @config.blank? || @config[:user_device_map].blank?
|
||||||
|
|
||||||
|
@config[:user_device_map].each do |row|
|
||||||
|
next if row[:user_id].blank?
|
||||||
|
return row[:user_id] if row[:device_id] == peer
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
107
app/models/cti/driver/sipgate_io.rb
Normal file
107
app/models/cti/driver/sipgate_io.rb
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
class Cti::Driver::SipgateIo < Cti::Driver::Base
|
||||||
|
|
||||||
|
def config
|
||||||
|
Setting.get('sipgate_config')
|
||||||
|
end
|
||||||
|
|
||||||
|
def push_open_ticket_screen_recipient
|
||||||
|
|
||||||
|
# try to find answering which answered call
|
||||||
|
user = nil
|
||||||
|
|
||||||
|
# based on peer
|
||||||
|
if @params['userId'].present?
|
||||||
|
user_id = get_user_id_by_sipgate_user_id(@params['userId'])
|
||||||
|
if user_id.present?
|
||||||
|
user = if User.exists?(user_id)
|
||||||
|
User.find(user_id)
|
||||||
|
else
|
||||||
|
User.find_by(email: user_id.downcase)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_voip_users
|
||||||
|
return {} if @config.blank? || @config[:api_user].blank? || @config[:api_password].blank?
|
||||||
|
|
||||||
|
list = Cache.get('sipgateUserList')
|
||||||
|
return list if list
|
||||||
|
|
||||||
|
url = 'https://api.sipgate.com/v2/users'
|
||||||
|
|
||||||
|
response = UserAgent.get(
|
||||||
|
url,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
user: @config[:api_user],
|
||||||
|
password: @config[:api_password],
|
||||||
|
log: {
|
||||||
|
facility: 'sipagte.io',
|
||||||
|
},
|
||||||
|
json: true,
|
||||||
|
open_timeout: 4,
|
||||||
|
read_timeout: 6,
|
||||||
|
total_timeout: 6,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if !response.success?
|
||||||
|
Rails.logger.error "Can't fetch users from '#{url}', http code: #{response.code}"
|
||||||
|
Cache.write('sipgateUserList', {}, { expires_in: 1.hour })
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
result = response.data
|
||||||
|
if result.blank?
|
||||||
|
Rails.logger.error "Can't fetch users from '#{url}', result: #{response.inspect}"
|
||||||
|
Cache.write('sipgateUserList', {}, { expires_in: 1.hour })
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
if result.is_a?(Array) && (result['result'] == '-1' || result['result_code'] == 'error')
|
||||||
|
Rails.logger.error "Can't fetch users from '#{url}', result: #{result.inspect}"
|
||||||
|
Cache.write('sipgateUserList', {}, { expires_in: 1.hour })
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
if !result.is_a?(Hash)
|
||||||
|
Rails.logger.error "Can't fetch users from '#{url}', result: #{result.inspect}"
|
||||||
|
Cache.write('sipgateUserList', {}, { expires_in: 1.hour })
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
if result['items'].blank?
|
||||||
|
Rails.logger.error "Can't fetch users from '#{url}', no items found, result: #{result.inspect}"
|
||||||
|
Cache.write('sipgateUserList', {}, { expires_in: 1.hour })
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
list = {}
|
||||||
|
result['items'].each do |entry|
|
||||||
|
next if entry['id'].blank?
|
||||||
|
|
||||||
|
name = ''
|
||||||
|
%w[firstname lastname email].each do |item|
|
||||||
|
next if entry[item].blank?
|
||||||
|
|
||||||
|
name += ' ' if name.present?
|
||||||
|
name += entry[item]
|
||||||
|
end
|
||||||
|
|
||||||
|
list[entry['id']] = name
|
||||||
|
end
|
||||||
|
Cache.write('sipgateUserList', list, { expires_in: 24.hours })
|
||||||
|
list
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_user_id_by_sipgate_user_id(user_id)
|
||||||
|
return if @config.blank? || @config[:user_remote_map].blank?
|
||||||
|
|
||||||
|
@config[:user_remote_map].each do |row|
|
||||||
|
next if row[:user_id].blank?
|
||||||
|
return row[:user_id] if row[:remote_user_id] == user_id
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -8,19 +8,20 @@ module Cti
|
||||||
|
|
||||||
validates :state, format: { with: /\A(newCall|answer|hangup)\z/, message: 'newCall|answer|hangup is allowed' }
|
validates :state, format: { with: /\A(newCall|answer|hangup)\z/, message: 'newCall|answer|hangup is allowed' }
|
||||||
|
|
||||||
after_commit :push_incoming_call, :push_caller_list_update
|
after_commit :push_caller_list_update
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
Cti::Log.create!(
|
Cti::Log.create!(
|
||||||
direction: 'in',
|
direction: 'in',
|
||||||
from: '007',
|
from: '007',
|
||||||
from_comment: 'AAA',
|
from_comment: '',
|
||||||
to: '008',
|
to: '008',
|
||||||
to_comment: 'BBB',
|
to_comment: 'BBB',
|
||||||
call_id: '1',
|
call_id: '1',
|
||||||
comment: '',
|
comment: '',
|
||||||
state: 'newCall',
|
state: 'newCall',
|
||||||
|
done: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
Cti::Log.create!(
|
Cti::Log.create!(
|
||||||
|
@ -32,6 +33,7 @@ module Cti
|
||||||
call_id: '2',
|
call_id: '2',
|
||||||
comment: '',
|
comment: '',
|
||||||
state: 'answer',
|
state: 'answer',
|
||||||
|
done: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
Cti::Log.create!(
|
Cti::Log.create!(
|
||||||
|
@ -43,6 +45,7 @@ module Cti
|
||||||
call_id: '3',
|
call_id: '3',
|
||||||
comment: '',
|
comment: '',
|
||||||
state: 'hangup',
|
state: 'hangup',
|
||||||
|
done: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
example data, can be used for demo
|
example data, can be used for demo
|
||||||
|
@ -66,7 +69,15 @@ example data, can be used for demo
|
||||||
object: 'User',
|
object: 'User',
|
||||||
o_id: 2,
|
o_id: 2,
|
||||||
user_id: 2,
|
user_id: 2,
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
caller_id: '4930726128135',
|
||||||
|
comment: nil,
|
||||||
|
level: 'maybe',
|
||||||
|
object: 'User',
|
||||||
|
o_id: 2,
|
||||||
|
user_id: 3,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
created_at: Time.zone.now,
|
created_at: Time.zone.now,
|
||||||
|
@ -81,6 +92,7 @@ example data, can be used for demo
|
||||||
call_id: rand(999_999_999),
|
call_id: rand(999_999_999),
|
||||||
comment: '',
|
comment: '',
|
||||||
state: 'newCall',
|
state: 'newCall',
|
||||||
|
done: true,
|
||||||
preferences: {
|
preferences: {
|
||||||
to: [
|
to: [
|
||||||
{
|
{
|
||||||
|
@ -105,6 +117,7 @@ example data, can be used for demo
|
||||||
call_id: rand(999_999_999),
|
call_id: rand(999_999_999),
|
||||||
comment: '',
|
comment: '',
|
||||||
state: 'answer',
|
state: 'answer',
|
||||||
|
done: true,
|
||||||
preferences: {
|
preferences: {
|
||||||
from: [
|
from: [
|
||||||
{
|
{
|
||||||
|
@ -163,6 +176,7 @@ example data, can be used for demo
|
||||||
call_id: rand(999_999_999),
|
call_id: rand(999_999_999),
|
||||||
comment: '',
|
comment: '',
|
||||||
state: 'hangup',
|
state: 'hangup',
|
||||||
|
done: true,
|
||||||
start_at: Time.zone.now - 15.seconds,
|
start_at: Time.zone.now - 15.seconds,
|
||||||
end_at: Time.zone.now,
|
end_at: Time.zone.now,
|
||||||
preferences: {
|
preferences: {
|
||||||
|
@ -194,6 +208,7 @@ example data, can be used for demo
|
||||||
call_id: rand(999_999_999),
|
call_id: rand(999_999_999),
|
||||||
comment: '',
|
comment: '',
|
||||||
state: 'hangup',
|
state: 'hangup',
|
||||||
|
done: true,
|
||||||
start_at: Time.zone.now - 15.seconds,
|
start_at: Time.zone.now - 15.seconds,
|
||||||
end_at: Time.zone.now,
|
end_at: Time.zone.now,
|
||||||
preferences: {
|
preferences: {
|
||||||
|
@ -225,6 +240,7 @@ example data, can be used for demo
|
||||||
call_id: rand(999_999_999),
|
call_id: rand(999_999_999),
|
||||||
comment: '',
|
comment: '',
|
||||||
state: 'hangup',
|
state: 'hangup',
|
||||||
|
done: true,
|
||||||
start_at: Time.zone.now - 15.seconds,
|
start_at: Time.zone.now - 15.seconds,
|
||||||
end_at: Time.zone.now,
|
end_at: Time.zone.now,
|
||||||
preferences: {
|
preferences: {
|
||||||
|
@ -254,6 +270,7 @@ example data, can be used for demo
|
||||||
call_id: rand(999_999_999),
|
call_id: rand(999_999_999),
|
||||||
comment: '',
|
comment: '',
|
||||||
state: 'hangup',
|
state: 'hangup',
|
||||||
|
done: true,
|
||||||
start_at: Time.zone.now - 20.seconds,
|
start_at: Time.zone.now - 20.seconds,
|
||||||
end_at: Time.zone.now,
|
end_at: Time.zone.now,
|
||||||
preferences: {},
|
preferences: {},
|
||||||
|
@ -269,7 +286,7 @@ example data, can be used for demo
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
Cti::Log.log
|
Cti::Log.log(current_user)
|
||||||
|
|
||||||
returns
|
returns
|
||||||
|
|
||||||
|
@ -280,8 +297,8 @@ returns
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def self.log
|
def self.log(current_user)
|
||||||
list = Cti::Log.log_records
|
list = Cti::Log.log_records(current_user)
|
||||||
|
|
||||||
# add assets
|
# add assets
|
||||||
assets = list.map(&:preferences)
|
assets = list.map(&:preferences)
|
||||||
|
@ -299,7 +316,7 @@ returns
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
Cti::Log.log_records
|
Cti::Log.log_records(current_user)
|
||||||
|
|
||||||
returns
|
returns
|
||||||
|
|
||||||
|
@ -307,7 +324,12 @@ returns
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def self.log_records
|
def self.log_records(current_user)
|
||||||
|
cti_config = Setting.get('cti_config')
|
||||||
|
if cti_config[:notify_map].present?
|
||||||
|
return Cti::Log.where(queue: queues_of_user(current_user, cti_config)).order(created_at: :desc).limit(60)
|
||||||
|
end
|
||||||
|
|
||||||
Cti::Log.order(created_at: :desc).limit(60)
|
Cti::Log.order(created_at: :desc).limit(60)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -349,9 +371,15 @@ Cti::Log.process(
|
||||||
to_comment = queue
|
to_comment = queue
|
||||||
end
|
end
|
||||||
from_comment, preferences = CallerId.get_comment_preferences(params['from'], 'from')
|
from_comment, preferences = CallerId.get_comment_preferences(params['from'], 'from')
|
||||||
|
if queue.blank?
|
||||||
|
queue = params['to']
|
||||||
|
end
|
||||||
else
|
else
|
||||||
from_comment = user
|
from_comment = user
|
||||||
to_comment, preferences = CallerId.get_comment_preferences(params['to'], 'to')
|
to_comment, preferences = CallerId.get_comment_preferences(params['to'], 'to')
|
||||||
|
if queue.blank?
|
||||||
|
queue = params['from']
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
log = find_by(call_id: call_id)
|
log = find_by(call_id: call_id)
|
||||||
|
@ -363,7 +391,7 @@ Cti::Log.process(
|
||||||
end
|
end
|
||||||
raise "call_id #{call_id} already exists!" if log
|
raise "call_id #{call_id} already exists!" if log
|
||||||
|
|
||||||
create(
|
log = create(
|
||||||
direction: params['direction'],
|
direction: params['direction'],
|
||||||
from: params['from'],
|
from: params['from'],
|
||||||
from_comment: from_comment,
|
from_comment: from_comment,
|
||||||
|
@ -417,29 +445,12 @@ Cti::Log.process(
|
||||||
else
|
else
|
||||||
raise ArgumentError, "Unknown event #{event.inspect}"
|
raise ArgumentError, "Unknown event #{event.inspect}"
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def push_incoming_call
|
log
|
||||||
return true if destroyed?
|
|
||||||
return true if state != 'newCall'
|
|
||||||
return true if direction != 'in'
|
|
||||||
|
|
||||||
# send notify about event
|
|
||||||
users = User.with_permissions('cti.agent')
|
|
||||||
users.each do |user|
|
|
||||||
Sessions.send_to(
|
|
||||||
user.id,
|
|
||||||
{
|
|
||||||
event: 'cti_event',
|
|
||||||
data: self,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
end
|
|
||||||
true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.push_caller_list_update?(record)
|
def self.push_caller_list_update?(record)
|
||||||
list_ids = Cti::Log.log_records.pluck(:id)
|
list_ids = Cti::Log.order(created_at: :desc).limit(60).pluck(:id)
|
||||||
return true if list_ids.include?(record.id)
|
return true if list_ids.include?(record.id)
|
||||||
|
|
||||||
false
|
false
|
||||||
|
@ -498,5 +509,54 @@ optional you can put the max oldest chat entries as argument
|
||||||
parsed = TelephoneNumber.parse(to&.sub(/^\+?/, '+'))
|
parsed = TelephoneNumber.parse(to&.sub(/^\+?/, '+'))
|
||||||
parsed.send(parsed.valid? ? :international_number : :original_number)
|
parsed.send(parsed.valid? ? :international_number : :original_number)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
returnes queues of user
|
||||||
|
|
||||||
|
['queue1', 'queue2'] = Cti::Log.queues_of_user(User.find(123), config)
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def self.queues_of_user(user, config)
|
||||||
|
queues = []
|
||||||
|
config[:notify_map]&.each do |row|
|
||||||
|
next if row[:user_ids].blank?
|
||||||
|
next if !row[:user_ids].include?(user.id.to_s) && !row[:user_ids].include?(user.id)
|
||||||
|
|
||||||
|
queues.push row[:queue]
|
||||||
|
end
|
||||||
|
if user.phone.present?
|
||||||
|
caller_ids = Cti::CallerId.extract_numbers(user.phone)
|
||||||
|
queues = queues.concat(caller_ids)
|
||||||
|
end
|
||||||
|
queues
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
return best customer id of caller log
|
||||||
|
|
||||||
|
log = Cti::Log.find(123)
|
||||||
|
customer_id = log.best_customer_id_of_log_entry
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def best_customer_id_of_log_entry
|
||||||
|
customer_id = nil
|
||||||
|
if preferences[:from].present?
|
||||||
|
preferences[:from].each do |entry|
|
||||||
|
if customer_id.blank?
|
||||||
|
customer_id = entry[:user_id]
|
||||||
|
end
|
||||||
|
next if entry[:level] != 'known'
|
||||||
|
|
||||||
|
customer_id = entry[:user_id]
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
customer_id
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Zammad::Application.routes.draw do
|
Zammad::Application.routes.draw do
|
||||||
|
|
||||||
match '/api/v1/sipgate/in', to: 'integration/sipgate#in', via: :post
|
match '/api/v1/sipgate/in', to: 'integration/sipgate#event', via: :post
|
||||||
match '/api/v1/sipgate/out', to: 'integration/sipgate#out', via: :post
|
match '/api/v1/sipgate/out', to: 'integration/sipgate#event', via: :post
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -249,6 +249,39 @@ RSpec.describe Cti::CallerId do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.known_agents_by_number' do
|
||||||
|
context 'with known agent caller_id' do
|
||||||
|
let!(:agent_user1) { create(:agent_user, phone: '0123456') }
|
||||||
|
let!(:agent_user2) { create(:agent_user, phone: '0123457') }
|
||||||
|
|
||||||
|
it 'gives matching agents' do
|
||||||
|
expect(described_class.known_agents_by_number('49123456'))
|
||||||
|
.to match_array([agent_user1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with known customer caller_id' do
|
||||||
|
let!(:customer_user1) { create(:customer_user, phone: '0123456') }
|
||||||
|
|
||||||
|
it 'returns an empty array' do
|
||||||
|
expect(described_class.known_agents_by_number('49123456')).to eq([])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with maybe caller_id' do
|
||||||
|
let(:ticket1) do
|
||||||
|
create(:ticket_article, created_by_id: customer_user2.id, body: 'some text 0123457') # create ticket
|
||||||
|
Observer::Transaction.commit
|
||||||
|
Scheduler.worker(true)
|
||||||
|
end
|
||||||
|
let!(:customer_user2) { create(:customer_user) }
|
||||||
|
|
||||||
|
it 'returns an empty array' do
|
||||||
|
expect(described_class.known_agents_by_number('49123457').count).to eq(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'callbacks' do
|
describe 'callbacks' do
|
||||||
subject!(:caller_id) { build(:cti_caller_id, caller_id: phone) }
|
subject!(:caller_id) { build(:cti_caller_id, caller_id: phone) }
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Cti::Log do
|
RSpec.describe Cti::Log do
|
||||||
subject(:log) { create(:'cti/log') }
|
subject(:user) { create(:user, roles: Role.where(name: 'Agent'), phone: phone) }
|
||||||
|
|
||||||
|
let(:phone) { '' }
|
||||||
|
let(:log) { create(:'cti/log') }
|
||||||
|
|
||||||
describe '.log' do
|
describe '.log' do
|
||||||
it 'returns a hash with :list and :assets keys' do
|
it 'returns a hash with :list and :assets keys' do
|
||||||
expect(described_class.log).to be_a(Hash).and include(:list, :assets)
|
expect(described_class.log(user)).to match(hash_including(:list, :assets))
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when over 60 Log records exist' do
|
context 'when over 60 Log records exist' do
|
||||||
|
@ -17,7 +20,7 @@ RSpec.describe Cti::Log do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the 60 latest ones in the :list key' do
|
it 'returns the 60 latest ones in the :list key' do
|
||||||
expect(described_class.log[:list]).to match_array(cti_logs.last(60))
|
expect(described_class.log(user)[:list]).to match_array(cti_logs.last(60))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,10 +28,52 @@ RSpec.describe Cti::Log do
|
||||||
subject!(:cti_log) { create(:'cti/log', preferences: { from: [caller_id] }) }
|
subject!(:cti_log) { create(:'cti/log', preferences: { from: [caller_id] }) }
|
||||||
|
|
||||||
let(:caller_id) { create(:caller_id) }
|
let(:caller_id) { create(:caller_id) }
|
||||||
let(:user) { User.find_by(id: caller_id.user_id) }
|
let(:caller_user) { User.find_by(id: caller_id.user_id) }
|
||||||
|
|
||||||
it 'returns a hash of the CallerId Users and their assets in the :assets key' do
|
it 'returns a hash of the CallerId Users and their assets in the :assets key' do
|
||||||
expect(described_class.log[:assets]).to eq(user.assets({}))
|
expect(described_class.log(user)[:assets]).to eq(caller_user.assets({}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a notify map is defined' do
|
||||||
|
subject!(:cti_logs) do
|
||||||
|
[create(:'cti/log', queue: 'queue0'),
|
||||||
|
create(:'cti/log', queue: 'queue2'),
|
||||||
|
create(:'cti/log', queue: 'queue3'),
|
||||||
|
create(:'cti/log', queue: 'queue4')]
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
cti_config = Setting.get('cti_config')
|
||||||
|
cti_config[:notify_map] = [ { queue: 'queue4', user_ids: [user.id.to_s] } ]
|
||||||
|
Setting.set('cti_config', cti_config)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns one matching log record' do
|
||||||
|
|
||||||
|
expect(described_class.log(user)[:list]).to match_array([cti_logs[3]])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.push_caller_list_update?' do
|
||||||
|
let!(:existing_logs) { create_list(:'cti/log', 60) }
|
||||||
|
let(:log) { create(:'cti/log') }
|
||||||
|
|
||||||
|
context 'when given log is older than existing logs' do
|
||||||
|
before { travel(-10.seconds) }
|
||||||
|
|
||||||
|
it 'return false' do
|
||||||
|
expect(described_class.push_caller_list_update?(log)).to eq false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when given log is newer than existing logs' do
|
||||||
|
before { travel(10.seconds) }
|
||||||
|
|
||||||
|
it 'return true' do
|
||||||
|
expect(described_class.push_caller_list_update?(log)).to eq true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -52,12 +97,25 @@ RSpec.describe Cti::Log do
|
||||||
let(:event) { 'newCall' }
|
let(:event) { 'newCall' }
|
||||||
|
|
||||||
context 'with unrecognized "call_id"' do
|
context 'with unrecognized "call_id"' do
|
||||||
it 'creates a new Log record (#state: "newCall", #done: false)' do
|
it 'creates a new Log record' do
|
||||||
expect { described_class.process(attributes) }
|
expect { described_class.process(attributes) }
|
||||||
.to change(described_class, :count).by(1)
|
.to change(described_class, :count).by(1)
|
||||||
|
|
||||||
expect(described_class.last.attributes)
|
expect(described_class.last.attributes)
|
||||||
.to include('state' => 'newCall', 'done' => false)
|
.to include(
|
||||||
|
'call_id' => '1',
|
||||||
|
'state' => 'newCall',
|
||||||
|
'done' => false,
|
||||||
|
'queue' => '49123457',
|
||||||
|
'from' => '49123456',
|
||||||
|
'from_comment' => nil,
|
||||||
|
'from_pretty' => '49123456',
|
||||||
|
'start_at' => nil,
|
||||||
|
'end_at' => nil,
|
||||||
|
'to' => '49123457',
|
||||||
|
'to_comment' => 'user 1',
|
||||||
|
'to_pretty' => '49123457'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'for direction "in", with a CallerId record matching the "from" number' do
|
context 'for direction "in", with a CallerId record matching the "from" number' do
|
||||||
|
@ -178,6 +236,93 @@ RSpec.describe Cti::Log do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'for preferences.from verification' do
|
||||||
|
subject(:log) do
|
||||||
|
described_class.process(attributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:customer_user_of_ticket) { create(:customer_user) }
|
||||||
|
let(:ticket_sample) do
|
||||||
|
create(:ticket_article, created_by_id: customer_user_of_ticket.id, body: 'some text 0123457')
|
||||||
|
Observer::Transaction.commit
|
||||||
|
Scheduler.worker(true)
|
||||||
|
end
|
||||||
|
let(:caller_id) { '0123456' }
|
||||||
|
let(:attributes) do
|
||||||
|
{
|
||||||
|
'cause' => '',
|
||||||
|
'event' => 'newCall',
|
||||||
|
'user' => 'user 1',
|
||||||
|
'from' => caller_id,
|
||||||
|
'to' => '49123450',
|
||||||
|
'call_id' => '1',
|
||||||
|
'direction' => 'in',
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with now related customer' do
|
||||||
|
it 'gives no caller information' do
|
||||||
|
expect(log.preferences[:from]).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with related known customer' do
|
||||||
|
let!(:customer_user) { create(:customer_user, phone: '0123456') }
|
||||||
|
|
||||||
|
it 'gives caller information' do
|
||||||
|
expect(log.preferences[:from].count).to eq(1)
|
||||||
|
expect(log.preferences[:from].first)
|
||||||
|
.to include(
|
||||||
|
'level' => 'known',
|
||||||
|
'user_id' => customer_user.id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with related known customers' do
|
||||||
|
let!(:customer_user1) { create(:customer_user, phone: '0123456') }
|
||||||
|
let!(:customer_user2) { create(:customer_user, phone: '0123456') }
|
||||||
|
|
||||||
|
it 'gives caller information' do
|
||||||
|
expect(log.preferences[:from].count).to eq(2)
|
||||||
|
expect(log.preferences[:from].first)
|
||||||
|
.to include(
|
||||||
|
'level' => 'known',
|
||||||
|
'user_id' => customer_user2.id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with related maybe customer' do
|
||||||
|
let(:caller_id) { '0123457' }
|
||||||
|
let!(:ticket) { ticket_sample }
|
||||||
|
|
||||||
|
it 'gives caller information' do
|
||||||
|
expect(log.preferences[:from].count).to eq(1)
|
||||||
|
expect(log.preferences[:from].first)
|
||||||
|
.to include(
|
||||||
|
'level' => 'maybe',
|
||||||
|
'user_id' => customer_user_of_ticket.id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with related maybe and known customer' do
|
||||||
|
let(:caller_id) { '0123457' }
|
||||||
|
let!(:customer) { create(:customer_user, phone: '0123457') }
|
||||||
|
let!(:ticket) { ticket_sample }
|
||||||
|
|
||||||
|
it 'gives caller information' do
|
||||||
|
expect(log.preferences[:from].count).to eq(1)
|
||||||
|
expect(log.preferences[:from].first)
|
||||||
|
.to include(
|
||||||
|
'level' => 'known',
|
||||||
|
'user_id' => customer.id,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Callbacks -' do
|
describe 'Callbacks -' do
|
||||||
|
@ -280,6 +425,71 @@ RSpec.describe Cti::Log do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.queues_of_user' do
|
||||||
|
context 'without notify_map and no own phone number' do
|
||||||
|
it 'gives an empty array' do
|
||||||
|
expect(described_class.queues_of_user(user, Setting.get('cti_config'))).to eq([])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with notify_map and no own phone number' do
|
||||||
|
before do
|
||||||
|
cti_config = Setting.get('cti_config')
|
||||||
|
cti_config[:notify_map] = [ { queue: 'queue4', user_ids: [user.id.to_s] } ]
|
||||||
|
Setting.set('cti_config', cti_config)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'gives an array with queue' do
|
||||||
|
expect(described_class.queues_of_user(user, Setting.get('cti_config'))).to eq(['queue4'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with notify_map and with own phone number' do
|
||||||
|
let(:phone) { '012345678' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
cti_config = Setting.get('cti_config')
|
||||||
|
cti_config[:notify_map] = [ { queue: 'queue4', user_ids: [user.id.to_s] } ]
|
||||||
|
Setting.set('cti_config', cti_config)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'gives an array with queue and phone number' do
|
||||||
|
expect(described_class.queues_of_user(user, Setting.get('cti_config'))).to eq(%w[queue4 4912345678])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#best_customer_id_of_log_entry' do
|
||||||
|
subject(:log1) do
|
||||||
|
described_class.process(
|
||||||
|
'event' => 'newCall',
|
||||||
|
'user' => 'user 1',
|
||||||
|
'from' => '01234599',
|
||||||
|
'to' => '49123450',
|
||||||
|
'call_id' => '1',
|
||||||
|
'direction' => 'in',
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:agent1) { create(:agent_user, phone: '01234599') }
|
||||||
|
let!(:customer2) { create(:customer_user, phone: '') }
|
||||||
|
let!(:ticket_article1) { create(:ticket_article, created_by_id: customer2.id, body: 'some text 01234599') }
|
||||||
|
|
||||||
|
context 'with agent1 (known), customer1 (known) and customer2 (maybe)' do
|
||||||
|
let!(:customer1) { create(:customer_user, phone: '01234599') }
|
||||||
|
|
||||||
|
it 'gives customer1' do
|
||||||
|
expect(log1.best_customer_id_of_log_entry).to eq(customer1.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with agent1 (known) and customer2 (maybe)' do
|
||||||
|
it 'gives customer2' do
|
||||||
|
expect(log1.best_customer_id_of_log_entry).to eq(agent1.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#to_json' do
|
describe '#to_json' do
|
||||||
it 'includes virtual attributes' do
|
it 'includes virtual attributes' do
|
||||||
expect(log.as_json).to include('from_pretty', 'to_pretty')
|
expect(log.as_json).to include('from_pretty', 'to_pretty')
|
||||||
|
|
|
@ -61,10 +61,6 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
note: 'some note',
|
note: 'some note',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
notify_user_ids: {
|
|
||||||
2 => true,
|
|
||||||
4 => false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -72,29 +68,47 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'request handling' do
|
describe 'request handling' do
|
||||||
|
let!(:token) { Setting.get('cti_token') }
|
||||||
|
|
||||||
it 'does token check' do
|
it 'does token check' do
|
||||||
params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&call_id=4991155921769858278-1&user%5B%5D=user+1&user%5B%5D=user+2'
|
post '/api/v1/cti/not_existing_token', params: {
|
||||||
post '/api/v1/cti/not_existing_token', params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
from: '4912347114711',
|
||||||
|
to: '4930600000000',
|
||||||
|
call_id: '4991155921769858278-1',
|
||||||
|
user: 'user 1',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
expect(json_response).to be_a_kind_of(Hash)
|
expect(json_response).to be_a_kind_of(Hash)
|
||||||
expect(json_response['error']).to eq('Invalid token, please contact your admin!')
|
expect(json_response['error']).to eq('Invalid token, please contact your admin!')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does basic call' do
|
it 'does basic call' do
|
||||||
token = Setting.get('cti_token')
|
|
||||||
|
|
||||||
# inbound - I
|
# inbound - I
|
||||||
params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&call_id=4991155921769858278-1&user%5B%5D=user+1&user%5B%5D=user+2'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
from: '4912347114711',
|
||||||
|
to: '4930600000000',
|
||||||
|
call_id: '4991155921769858278-1',
|
||||||
|
user: ['user+1', 'user+2'],
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
expect(json_response).to be_a_kind_of(Hash)
|
expect(json_response).to be_a_kind_of(Hash)
|
||||||
expect(json_response).to be_blank
|
expect(json_response).to be_blank
|
||||||
|
|
||||||
# inbound - II - block caller
|
# inbound - II - block caller
|
||||||
params = 'event=newCall&direction=in&from=491715000000&to=4930600000000&call_id=4991155921769858278-2&user%5B%5D=user+1&user%5B%5D=user+2'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
from: '491715000000',
|
||||||
|
to: '4930600000000',
|
||||||
|
call_id: '4991155921769858278-2',
|
||||||
|
user: ['user+1', 'user+2'],
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
expect(json_response).to be_a_kind_of(Hash)
|
expect(json_response).to be_a_kind_of(Hash)
|
||||||
|
@ -102,8 +116,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(json_response['reason']).to eq('busy')
|
expect(json_response['reason']).to eq('busy')
|
||||||
|
|
||||||
# outbound - I - set default_caller_id
|
# outbound - I - set default_caller_id
|
||||||
params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&call_id=8621106404543334274-3&user%5B%5D=user+1'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'out',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '4912347114711',
|
||||||
|
call_id: '8621106404543334274-3',
|
||||||
|
user: 'user 1',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(json_response).to be_a_kind_of(Hash)
|
expect(json_response).to be_a_kind_of(Hash)
|
||||||
expect(json_response['action']).to eq('dial')
|
expect(json_response['action']).to eq('dial')
|
||||||
|
@ -111,8 +131,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(json_response['caller_id']).to eq('4930777000000')
|
expect(json_response['caller_id']).to eq('4930777000000')
|
||||||
|
|
||||||
# outbound - II - set caller_id based on routing_table by explicite number
|
# outbound - II - set caller_id based on routing_table by explicite number
|
||||||
params = 'event=newCall&direction=out&from=4930600000000&to=491714000000&call_id=8621106404543334274-4&user%5B%5D=user+1'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'out',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '491714000000',
|
||||||
|
call_id: '8621106404543334274-4',
|
||||||
|
user: 'user 1',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(json_response).to be_a_kind_of(Hash)
|
expect(json_response).to be_a_kind_of(Hash)
|
||||||
expect(json_response['action']).to eq('dial')
|
expect(json_response['action']).to eq('dial')
|
||||||
|
@ -120,8 +146,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(json_response['caller_id']).to eq('41715880339000')
|
expect(json_response['caller_id']).to eq('41715880339000')
|
||||||
|
|
||||||
# outbound - III - set caller_id based on routing_table by 41*
|
# outbound - III - set caller_id based on routing_table by 41*
|
||||||
params = 'event=newCall&direction=out&from=4930600000000&to=4147110000000&call_id=8621106404543334274-5&user%5B%5D=user+1'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'out',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '4147110000000',
|
||||||
|
call_id: '8621106404543334274-5',
|
||||||
|
user: 'user 1',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(json_response).to be_a_kind_of(Hash)
|
expect(json_response).to be_a_kind_of(Hash)
|
||||||
expect(json_response['action']).to eq('dial')
|
expect(json_response['action']).to eq('dial')
|
||||||
|
@ -130,8 +162,15 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
|
|
||||||
# no config
|
# no config
|
||||||
Setting.set('cti_config', {})
|
Setting.set('cti_config', {})
|
||||||
params = 'event=newCall&direction=in&from=4912347114711&to=4930600000000&call_id=4991155921769858278-6&user%5B%5D=user+1&user%5B%5D=user+2'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
from: '4912347114711',
|
||||||
|
to: '4930600000000',
|
||||||
|
call_id: '4991155921769858278-6',
|
||||||
|
user: ['user+1', 'user+2'],
|
||||||
|
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:unprocessable_entity)
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
expect(json_response).to be_a_kind_of(Hash)
|
expect(json_response).to be_a_kind_of(Hash)
|
||||||
expect(json_response['error']).to eq('Feature not configured, please contact your admin!')
|
expect(json_response['error']).to eq('Feature not configured, please contact your admin!')
|
||||||
|
@ -139,11 +178,16 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does log call' do
|
it 'does log call' do
|
||||||
token = Setting.get('cti_token')
|
|
||||||
|
|
||||||
# outbound - I - new call
|
# outbound - I - new call
|
||||||
params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&call_id=1234567890-1&user%5B%5D=user+1'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'out',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '4912347114711',
|
||||||
|
call_id: '1234567890-1',
|
||||||
|
user: 'user 1',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-1')
|
log = Cti::Log.find_by(call_id: '1234567890-1')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -153,7 +197,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.from_comment).to eq('user 1')
|
expect(log.from_comment).to eq('user 1')
|
||||||
expect(log.to_comment).to eq('CallerId Customer1')
|
expect(log.to_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to be_nil
|
expect(log.comment).to be_nil
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930777000000')
|
||||||
expect(log.state).to eq('newCall')
|
expect(log.state).to eq('newCall')
|
||||||
expect(log.done).to eq(true)
|
expect(log.done).to eq(true)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -165,8 +209,12 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 2.seconds
|
travel 2.seconds
|
||||||
|
|
||||||
# outbound - I - hangup by agent
|
# outbound - I - hangup by agent
|
||||||
params = 'event=hangup&direction=out&call_id=1234567890-1&cause=cancel'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'hangup',
|
||||||
|
direction: 'out',
|
||||||
|
call_id: '1234567890-1',
|
||||||
|
cause: 'cancel',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-1')
|
log = Cti::Log.find_by(call_id: '1234567890-1')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -176,7 +224,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.from_comment).to eq('user 1')
|
expect(log.from_comment).to eq('user 1')
|
||||||
expect(log.to_comment).to eq('CallerId Customer1')
|
expect(log.to_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to eq('cancel')
|
expect(log.comment).to eq('cancel')
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930777000000')
|
||||||
expect(log.state).to eq('hangup')
|
expect(log.state).to eq('hangup')
|
||||||
expect(log.done).to eq(true)
|
expect(log.done).to eq(true)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -186,8 +234,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.duration_talking_time).to be_nil
|
expect(log.duration_talking_time).to be_nil
|
||||||
|
|
||||||
# outbound - II - new call
|
# outbound - II - new call
|
||||||
params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&call_id=1234567890-2&user%5B%5D=user+1'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'out',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '4912347114711',
|
||||||
|
call_id: '1234567890-2',
|
||||||
|
user: ['user 1'],
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-2')
|
log = Cti::Log.find_by(call_id: '1234567890-2')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -197,7 +251,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.from_comment).to eq('user 1')
|
expect(log.from_comment).to eq('user 1')
|
||||||
expect(log.to_comment).to eq('CallerId Customer1')
|
expect(log.to_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to be_nil
|
expect(log.comment).to be_nil
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930777000000')
|
||||||
expect(log.state).to eq('newCall')
|
expect(log.state).to eq('newCall')
|
||||||
expect(log.done).to eq(true)
|
expect(log.done).to eq(true)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -209,8 +263,13 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 2.seconds
|
travel 2.seconds
|
||||||
|
|
||||||
# outbound - II - answer by customer
|
# outbound - II - answer by customer
|
||||||
params = 'event=answer&direction=out&call_id=1234567890-2&from=4930600000000&to=4912347114711'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'answer',
|
||||||
|
direction: 'out',
|
||||||
|
call_id: '1234567890-2',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '4912347114711',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-2')
|
log = Cti::Log.find_by(call_id: '1234567890-2')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -220,7 +279,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.from_comment).to eq('user 1')
|
expect(log.from_comment).to eq('user 1')
|
||||||
expect(log.to_comment).to eq('CallerId Customer1')
|
expect(log.to_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to be_nil
|
expect(log.comment).to be_nil
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930777000000')
|
||||||
expect(log.state).to eq('answer')
|
expect(log.state).to eq('answer')
|
||||||
expect(log.done).to eq(true)
|
expect(log.done).to eq(true)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -232,8 +291,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 2.seconds
|
travel 2.seconds
|
||||||
|
|
||||||
# outbound - II - hangup by customer
|
# outbound - II - hangup by customer
|
||||||
params = 'event=hangup&direction=out&call_id=1234567890-2&cause=normalClearing&from=4930600000000&to=4912347114711'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'hangup',
|
||||||
|
direction: 'out',
|
||||||
|
call_id: '1234567890-2',
|
||||||
|
cause: 'normalClearing',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '4912347114711',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-2')
|
log = Cti::Log.find_by(call_id: '1234567890-2')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -243,7 +308,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.from_comment).to eq('user 1')
|
expect(log.from_comment).to eq('user 1')
|
||||||
expect(log.to_comment).to eq('CallerId Customer1')
|
expect(log.to_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to eq('normalClearing')
|
expect(log.comment).to eq('normalClearing')
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930777000000')
|
||||||
expect(log.state).to eq('hangup')
|
expect(log.state).to eq('hangup')
|
||||||
expect(log.done).to eq(true)
|
expect(log.done).to eq(true)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -255,8 +320,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 1.second
|
travel 1.second
|
||||||
|
|
||||||
# inbound - I - new call
|
# inbound - I - new call
|
||||||
params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&call_id=1234567890-3&user%5B%5D=user+1'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '4912347114711',
|
||||||
|
call_id: '1234567890-3',
|
||||||
|
user: 'user 1',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-3')
|
log = Cti::Log.find_by(call_id: '1234567890-3')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -266,7 +337,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.to_comment).to eq('user 1')
|
expect(log.to_comment).to eq('user 1')
|
||||||
expect(log.from_comment).to eq('CallerId Customer1')
|
expect(log.from_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to be_nil
|
expect(log.comment).to be_nil
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930600000000')
|
||||||
expect(log.state).to eq('newCall')
|
expect(log.state).to eq('newCall')
|
||||||
expect(log.done).to eq(false)
|
expect(log.done).to eq(false)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -278,8 +349,13 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 1.second
|
travel 1.second
|
||||||
|
|
||||||
# inbound - I - answer by customer
|
# inbound - I - answer by customer
|
||||||
params = 'event=answer&direction=in&call_id=1234567890-3&to=4930600000000&from=4912347114711'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'answer',
|
||||||
|
direction: 'in',
|
||||||
|
call_id: '1234567890-3',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '4912347114711',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-3')
|
log = Cti::Log.find_by(call_id: '1234567890-3')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -289,7 +365,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.to_comment).to eq('user 1')
|
expect(log.to_comment).to eq('user 1')
|
||||||
expect(log.from_comment).to eq('CallerId Customer1')
|
expect(log.from_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to be_nil
|
expect(log.comment).to be_nil
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930600000000')
|
||||||
expect(log.state).to eq('answer')
|
expect(log.state).to eq('answer')
|
||||||
expect(log.done).to eq(true)
|
expect(log.done).to eq(true)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -301,8 +377,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 1.second
|
travel 1.second
|
||||||
|
|
||||||
# inbound - I - hangup by customer
|
# inbound - I - hangup by customer
|
||||||
params = 'event=hangup&direction=in&call_id=1234567890-3&cause=normalClearing&to=4930600000000&from=4912347114711'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'hangup',
|
||||||
|
direction: 'in',
|
||||||
|
call_id: '1234567890-3',
|
||||||
|
cause: 'normalClearing',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '4912347114711',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-3')
|
log = Cti::Log.find_by(call_id: '1234567890-3')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -312,7 +394,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.to_comment).to eq('user 1')
|
expect(log.to_comment).to eq('user 1')
|
||||||
expect(log.from_comment).to eq('CallerId Customer1')
|
expect(log.from_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to eq('normalClearing')
|
expect(log.comment).to eq('normalClearing')
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930600000000')
|
||||||
expect(log.state).to eq('hangup')
|
expect(log.state).to eq('hangup')
|
||||||
expect(log.done).to eq(true)
|
expect(log.done).to eq(true)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -324,18 +406,24 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 1.second
|
travel 1.second
|
||||||
|
|
||||||
# inbound - II - new call
|
# inbound - II - new call
|
||||||
params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&call_id=1234567890-4&user%5B%5D=user+1,user+2'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '4912347114711',
|
||||||
|
call_id: '1234567890-4',
|
||||||
|
user: ['user 1', 'user 2'],
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-4')
|
log = Cti::Log.find_by(call_id: '1234567890-4')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
expect(log.to).to eq('4930600000000')
|
expect(log.to).to eq('4930600000000')
|
||||||
expect(log.from).to eq('4912347114711')
|
expect(log.from).to eq('4912347114711')
|
||||||
expect(log.direction).to eq('in')
|
expect(log.direction).to eq('in')
|
||||||
expect(log.to_comment).to eq('user 1,user 2')
|
expect(log.to_comment).to eq('user 1, user 2')
|
||||||
expect(log.from_comment).to eq('CallerId Customer1')
|
expect(log.from_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to be_nil
|
expect(log.comment).to be_nil
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930600000000')
|
||||||
expect(log.state).to eq('newCall')
|
expect(log.state).to eq('newCall')
|
||||||
expect(log.done).to eq(false)
|
expect(log.done).to eq(false)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -347,8 +435,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 1.second
|
travel 1.second
|
||||||
|
|
||||||
# inbound - II - answer by voicemail
|
# inbound - II - answer by voicemail
|
||||||
params = 'event=answer&direction=in&call_id=1234567890-4&to=4930600000000&from=4912347114711&user=voicemail'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'answer',
|
||||||
|
direction: 'in',
|
||||||
|
call_id: '1234567890-4',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '4912347114711',
|
||||||
|
user: 'voicemail',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-4')
|
log = Cti::Log.find_by(call_id: '1234567890-4')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -358,7 +452,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.to_comment).to eq('voicemail')
|
expect(log.to_comment).to eq('voicemail')
|
||||||
expect(log.from_comment).to eq('CallerId Customer1')
|
expect(log.from_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to be_nil
|
expect(log.comment).to be_nil
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930600000000')
|
||||||
expect(log.state).to eq('answer')
|
expect(log.state).to eq('answer')
|
||||||
expect(log.done).to eq(true)
|
expect(log.done).to eq(true)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -370,8 +464,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 1.second
|
travel 1.second
|
||||||
|
|
||||||
# inbound - II - hangup by customer
|
# inbound - II - hangup by customer
|
||||||
params = 'event=hangup&direction=in&call_id=1234567890-4&cause=normalClearing&to=4930600000000&from=4912347114711'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'hangup',
|
||||||
|
direction: 'in',
|
||||||
|
call_id: '1234567890-4',
|
||||||
|
cause: 'normalClearing',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '4912347114711',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-4')
|
log = Cti::Log.find_by(call_id: '1234567890-4')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -381,7 +481,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.to_comment).to eq('voicemail')
|
expect(log.to_comment).to eq('voicemail')
|
||||||
expect(log.from_comment).to eq('CallerId Customer1')
|
expect(log.from_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to eq('normalClearing')
|
expect(log.comment).to eq('normalClearing')
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930600000000')
|
||||||
expect(log.state).to eq('hangup')
|
expect(log.state).to eq('hangup')
|
||||||
expect(log.done).to eq(false)
|
expect(log.done).to eq(false)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -393,8 +493,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 1.second
|
travel 1.second
|
||||||
|
|
||||||
# inbound - III - new call
|
# inbound - III - new call
|
||||||
params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&call_id=1234567890-5&user%5B%5D=user+1,user+2'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '4912347114711',
|
||||||
|
call_id: '1234567890-5',
|
||||||
|
user: 'user 1,user 2',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-5')
|
log = Cti::Log.find_by(call_id: '1234567890-5')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -404,7 +510,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.to_comment).to eq('user 1,user 2')
|
expect(log.to_comment).to eq('user 1,user 2')
|
||||||
expect(log.from_comment).to eq('CallerId Customer1')
|
expect(log.from_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to be_nil
|
expect(log.comment).to be_nil
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930600000000')
|
||||||
expect(log.state).to eq('newCall')
|
expect(log.state).to eq('newCall')
|
||||||
expect(log.done).to eq(false)
|
expect(log.done).to eq(false)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -416,8 +522,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 1.second
|
travel 1.second
|
||||||
|
|
||||||
# inbound - III - hangup by customer
|
# inbound - III - hangup by customer
|
||||||
params = 'event=hangup&direction=in&call_id=1234567890-5&cause=normalClearing&to=4930600000000&from=4912347114711'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'hangup',
|
||||||
|
direction: 'in',
|
||||||
|
call_id: '1234567890-5',
|
||||||
|
cause: 'normalClearing',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '4912347114711',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-5')
|
log = Cti::Log.find_by(call_id: '1234567890-5')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -427,7 +539,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.to_comment).to eq('user 1,user 2')
|
expect(log.to_comment).to eq('user 1,user 2')
|
||||||
expect(log.from_comment).to eq('CallerId Customer1')
|
expect(log.from_comment).to eq('CallerId Customer1')
|
||||||
expect(log.comment).to eq('normalClearing')
|
expect(log.comment).to eq('normalClearing')
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930600000000')
|
||||||
expect(log.state).to eq('hangup')
|
expect(log.state).to eq('hangup')
|
||||||
expect(log.done).to eq(false)
|
expect(log.done).to eq(false)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -439,8 +551,14 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 1.second
|
travel 1.second
|
||||||
|
|
||||||
# inbound - IV - new call
|
# inbound - IV - new call
|
||||||
params = 'event=newCall&direction=in&to=4930600000000&from=49999992222222&call_id=1234567890-6&user%5B%5D=user+1,user+2'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '49999992222222',
|
||||||
|
call_id: '1234567890-6',
|
||||||
|
user: 'user 1,user 2',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-6')
|
log = Cti::Log.find_by(call_id: '1234567890-6')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -452,7 +570,7 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.preferences['to']).to be_falsey
|
expect(log.preferences['to']).to be_falsey
|
||||||
expect(log.preferences['from']).to be_truthy
|
expect(log.preferences['from']).to be_truthy
|
||||||
expect(log.comment).to be_nil
|
expect(log.comment).to be_nil
|
||||||
expect(log.queue).to be_nil
|
expect(log.queue).to eq('4930600000000')
|
||||||
expect(log.state).to eq('newCall')
|
expect(log.state).to eq('newCall')
|
||||||
expect(log.done).to eq(false)
|
expect(log.done).to eq(false)
|
||||||
expect(log.initialized_at).to be_truthy
|
expect(log.initialized_at).to be_truthy
|
||||||
|
@ -464,8 +582,15 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
travel 1.second
|
travel 1.second
|
||||||
|
|
||||||
# inbound - IV - new call
|
# inbound - IV - new call
|
||||||
params = 'event=newCall&direction=in&to=4930600000000&from=anonymous&call_id=1234567890-7&user%5B%5D=user+1,user+2&queue=some_queue_name'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: 'anonymous',
|
||||||
|
call_id: '1234567890-7',
|
||||||
|
user: 'user 1,user 2',
|
||||||
|
queue: 'some_queue_name',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-7')
|
log = Cti::Log.find_by(call_id: '1234567890-7')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -486,10 +611,10 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.duration_waiting_time).to be_nil
|
expect(log.duration_waiting_time).to be_nil
|
||||||
expect(log.duration_talking_time).to be_nil
|
expect(log.duration_talking_time).to be_nil
|
||||||
|
|
||||||
# get caller list
|
|
||||||
get '/api/v1/cti/log'
|
get '/api/v1/cti/log'
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
|
||||||
|
# get caller list
|
||||||
authenticated_as(agent_user)
|
authenticated_as(agent_user)
|
||||||
get '/api/v1/cti/log', as: :json
|
get '/api/v1/cti/log', as: :json
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
@ -515,12 +640,110 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(json_response['list'][6]['call_id']).to eq('1234567890-1')
|
expect(json_response['list'][6]['call_id']).to eq('1234567890-1')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'does log call with notify group with two a log entry' do
|
||||||
|
|
||||||
|
# outbound - I - new call
|
||||||
|
post "/api/v1/cti/#{token}", params: {
|
||||||
|
event: 'newCall',
|
||||||
|
direction: 'out',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '4912347114711',
|
||||||
|
call_id: '1234567890-1',
|
||||||
|
user: 'user 1',
|
||||||
|
}
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
|
# outbound - II - new call
|
||||||
|
post "/api/v1/cti/#{token}", params: {
|
||||||
|
event: 'newCall',
|
||||||
|
direction: 'out',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '4912347114711',
|
||||||
|
call_id: '1234567890-2',
|
||||||
|
user: 'user 1',
|
||||||
|
}
|
||||||
|
|
||||||
|
# inbound - III - new call
|
||||||
|
post "/api/v1/cti/#{token}", params: {
|
||||||
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '4912347114711',
|
||||||
|
call_id: '1234567890-5',
|
||||||
|
user: 'user 1,user 2',
|
||||||
|
}
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
|
# get caller list (with notify group with 2 log entries)
|
||||||
|
cti_config = Setting.get('cti_config')
|
||||||
|
cti_config[:notify_map] = [{ queue: '4930777000000', user_ids: [agent_user.id.to_s] }]
|
||||||
|
Setting.set('cti_config', cti_config)
|
||||||
|
|
||||||
|
authenticated_as(agent_user)
|
||||||
|
get '/api/v1/cti/log', as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(json_response.dig('assets', 'User')).not_to be(nil)
|
||||||
|
expect(json_response['list'].map { |x| x['call_id'] }).to match_array(%w[1234567890-1 1234567890-2])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does log call with notify group without a log entry' do
|
||||||
|
|
||||||
|
# outbound - I - new call
|
||||||
|
post "/api/v1/cti/#{token}", params: {
|
||||||
|
event: 'newCall',
|
||||||
|
direction: 'out',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '4912347114711',
|
||||||
|
call_id: '1234567890-1',
|
||||||
|
user: 'user 1',
|
||||||
|
}
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
|
# outbound - II - new call
|
||||||
|
post "/api/v1/cti/#{token}", params: {
|
||||||
|
event: 'newCall',
|
||||||
|
direction: 'out',
|
||||||
|
from: '4930600000000',
|
||||||
|
to: '4912347114711',
|
||||||
|
call_id: '1234567890-2',
|
||||||
|
user: 'user 1',
|
||||||
|
}
|
||||||
|
|
||||||
|
# inbound - III - new call
|
||||||
|
post "/api/v1/cti/#{token}", params: {
|
||||||
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: '4912347114711',
|
||||||
|
call_id: '1234567890-5',
|
||||||
|
user: 'user 1,user 2',
|
||||||
|
}
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
|
# get caller list (with notify group without a log entry)
|
||||||
|
cti_config = Setting.get('cti_config')
|
||||||
|
cti_config[:notify_map] = [{ queue: '4912347114711', user_ids: [agent_user.to_s] }]
|
||||||
|
Setting.set('cti_config', cti_config)
|
||||||
|
|
||||||
|
authenticated_as(agent_user)
|
||||||
|
get '/api/v1/cti/log', as: :json
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(json_response['list']).to eq([])
|
||||||
|
end
|
||||||
|
|
||||||
it 'does queue param tests' do
|
it 'does queue param tests' do
|
||||||
token = Setting.get('cti_token')
|
|
||||||
|
|
||||||
# inbound - queue & user
|
# inbound - queue & user
|
||||||
params = 'event=newCall&direction=in&to=4930600000000&from=anonymous&call_id=1234567890-1&user%5B%5D=user+1,user+2&queue=some_queue_name'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: 'anonymous',
|
||||||
|
call_id: '1234567890-1',
|
||||||
|
user: 'user 1,user 2',
|
||||||
|
queue: 'some_queue_name',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-1')
|
log = Cti::Log.find_by(call_id: '1234567890-1')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
@ -542,8 +765,15 @@ RSpec.describe 'Integration CTI', type: :request do
|
||||||
expect(log.duration_talking_time).to be_nil
|
expect(log.duration_talking_time).to be_nil
|
||||||
|
|
||||||
# inbound - queue & no user
|
# inbound - queue & no user
|
||||||
params = 'event=newCall&direction=in&to=4930600000000&from=anonymous&call_id=1234567890-2&user%5B%5D=&queue=some_queue_name'
|
post "/api/v1/cti/#{token}", params: {
|
||||||
post "/api/v1/cti/#{token}", params: params
|
event: 'newCall',
|
||||||
|
direction: 'in',
|
||||||
|
to: '4930600000000',
|
||||||
|
from: 'anonymous',
|
||||||
|
call_id: '1234567890-2',
|
||||||
|
user: '',
|
||||||
|
queue: 'some_queue_name',
|
||||||
|
}
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
log = Cti::Log.find_by(call_id: '1234567890-2')
|
log = Cti::Log.find_by(call_id: '1234567890-2')
|
||||||
expect(log).to be_truthy
|
expect(log).to be_truthy
|
||||||
|
|
|
@ -153,7 +153,7 @@ class IntegrationCtiTest < TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
# Regression test for #2096
|
# Regression test for #2096
|
||||||
def test_inactive_users_displayed_with_strikethrough_in_caller_log
|
def test_inactive_users_displayed_inactive_in_caller_log
|
||||||
id = rand(99_999_999)
|
id = rand(99_999_999)
|
||||||
|
|
||||||
@browser = browser_instance
|
@browser = browser_instance
|
||||||
|
@ -205,10 +205,10 @@ class IntegrationCtiTest < TestCase
|
||||||
# view caller log
|
# view caller log
|
||||||
click(css: 'a[href="#cti"]')
|
click(css: 'a[href="#cti"]')
|
||||||
|
|
||||||
# assertion: names appear in strikethrough
|
# assertion: names appear in inactive
|
||||||
match(
|
match(
|
||||||
css: 'span.is-inactive',
|
css: 'span.avatar--inactive',
|
||||||
value: 'John Doe',
|
value: 'JD',
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -279,12 +279,12 @@ class IntegrationCtiTest < TestCase
|
||||||
|
|
||||||
# assertions: Caller ID includes user organization
|
# assertions: Caller ID includes user organization
|
||||||
match(
|
match(
|
||||||
css: '.js-callerLog tr:first-of-type span.user-popover',
|
css: '.js-callerLog tr:first-of-type div.user-popover',
|
||||||
value: 'John Doe (Zammad Foundation)',
|
value: 'John Doe (Zammad Foundation)',
|
||||||
)
|
)
|
||||||
|
|
||||||
match(
|
match(
|
||||||
css: '.js-callerLog tr:last-of-type span.user-popover',
|
css: '.js-callerLog tr:last-of-type div.user-popover',
|
||||||
value: 'John Doe (Zammad Foundation)',
|
value: 'John Doe (Zammad Foundation)',
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue