Improved CTI features.

This commit is contained in:
Martin Edenhofer 2016-04-27 13:37:13 +02:00
parent a746f559cd
commit c747c0c1f1
6 changed files with 191 additions and 20 deletions

View file

@ -1,9 +1,17 @@
class App.CTI extends App.Controller class App.CTI extends App.Controller
events:
'click .js-check': 'done'
constructor: -> constructor: ->
super super
return if !@isRole('CTI')
@list = []
@meta = @meta =
active: false active: false
counter: 0
state: {}
preferences = @Session.get('preferences') || {} preferences = @Session.get('preferences') || {}
@meta.active = preferences.cti || false @meta.active = preferences.cti || false
@ -14,9 +22,20 @@ class App.CTI extends App.Controller
'cti_event' 'cti_event'
(data) => (data) =>
console.log('cti_event', data) console.log('cti_event', data)
if data.direction is 'in'
if data.state is 'newCall' if data.state is 'newCall'
console.log('notify') if @switch()
@notify(data) @notify(data)
return if @meta.state[data.id]
@meta.state[data.id] = true
@meta.counter += 1
@updateNavMenu()
if data.state is 'answer' || data.state is 'hangup'
return if !@meta.state[data.id]
delete @meta.state[data.id]
@meta.counter -= 1
@updateNavMenu()
'cti_event' 'cti_event'
) )
App.Event.bind( App.Event.bind(
@ -26,6 +45,11 @@ class App.CTI extends App.Controller
@render() @render()
'cti_list_push' 'cti_list_push'
) )
App.Event.bind(
'auth'
(data) =>
@meta.counter = 0
)
# fetch data, render view # fetch data, render view
load: -> load: ->
@ -39,13 +63,10 @@ class App.CTI extends App.Controller
) )
notify: (data) -> notify: (data) ->
console.log(data)
#return if !
if data.state is 'newCall' && data.direction is 'in'
App.Event.trigger 'notify', { App.Event.trigger 'notify', {
type: 'notice' type: 'notice'
msg: App.i18n.translateContent('Call from %s for %s', data.from, data.to) msg: App.i18n.translateContent('Call from %s for %s', data.from, data.to)
timeout: 2500 timeout: 12000
} }
featureActive: => featureActive: =>
@ -59,18 +80,48 @@ class App.CTI extends App.Controller
@renderScreenUnauthorized(objectName: 'CTI') @renderScreenUnauthorized(objectName: 'CTI')
return return
format = (time) ->
# Minutes and seconds
mins = ~~(time / 60)
secs = time % 60
# Hours, minutes and seconds
hrs = ~~(time / 3600)
mins = ~~((time % 3600) / 60)
secs = time % 60
# Output like "1:01" or "4:03:59" or "123:03:59"
mins = "0#{mins}" if mins < 10
secs = "0#{secs}" if secs < 10
if hrs > 0
return "#{hrs}:#{mins}:#{secs}"
"#{mins}:#{secs}"
for item in @list
if item.start && item.end
item.duration = format((Date.parse(item.end) - Date.parse(item.start))/1000)
@html App.view('cti/index')( @html App.view('cti/index')(
list: @list list: @list
) )
@updateNavMenu() @updateNavMenu()
done: (e) =>
element = $(e.currentTarget)
id = element.closest('tr').data('id')
done = element.prop('checked')
@ajax(
type: 'POST'
url: "#{@apiPath}/cti/done/#{id}"
data: JSON.stringify(done: done)
)
show: (params) => show: (params) =>
@title 'CTI', true @title 'CTI', true
@navupdate '#cti' @navupdate '#cti'
counter: -> counter: =>
counter = 0 @meta.counter
switch: (state = undefined) => switch: (state = undefined) =>

View file

@ -5,20 +5,24 @@
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th style="width: 28px;"></th>
<th><%- @T('From') %></th> <th><%- @T('From') %></th>
<th><%- @T('To') %></th> <th><%- @T('To') %></th>
<th><%- @T('State') %></th> <th><%- @T('State') %></th>
<th><%- @T('Comment') %></th> <th><%- @T('Comment') %></th>
<th><%- @T('Duration') %></th>
<th><%- @T('Time') %></th> <th><%- @T('Time') %></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<% for item in @list: %> <% for item in @list: %>
<tr> <tr <% if item.state is 'hangup' && item.done: %>class="is-inactive"<% end %> data-id="<%- item.id %>">
<td><%= item.from %><% if item.from_comment: %> (<%= item.from_comment %>)<% end %></td> <td><% if item.state is 'hangup': %><input type="checkbox" class="js-check" <% if item.done: %>checked<% end %>><% end %>
<td><%= item.to %><% if item.to_comment: %> (<%= item.to_comment %>)<% end %></td> <td><% if item.from_comment: %><%= item.from_comment %><br><% end %><%= item.from %></td>
<td><% if item.to_comment: %><%= item.to_comment %><br><% end %><%= item.to %></td>
<td><%= item.state %></td> <td><%= item.state %></td>
<td><%= item.comment %></td> <td><%= item.comment %></td>
<td><%= item.duration %></td>
<td><%- @humanTime(item.created_at) %></td> <td><%- @humanTime(item.created_at) %></td>
</tr> </tr>
<% end %> <% end %>

View file

@ -12,6 +12,16 @@ class Integration::SipgateController < ApplicationController
render json: list render json: list
end end
# set caller log to done
def done
return if !authentication_check
return if deny_if_not_role('CTI')
log = Cti::Log.find(params['id'])
log.done = params['done']
log.save
render json: {}
end
# notify about inbound call / block inbound call # notify about inbound call / block inbound call
def in def in
http_log_config facility: 'sipgate.io' http_log_config facility: 'sipgate.io'
@ -143,12 +153,23 @@ class Integration::SipgateController < ApplicationController
log = Cti::Log.find_by(call_id: params['callId']) log = Cti::Log.find_by(call_id: params['callId'])
raise "No such call_id #{params['callId']}" if !log raise "No such call_id #{params['callId']}" if !log
log.state = 'answer' log.state = 'answer'
log.start = Time.zone.now
if user
log.to_comment = user
end
log.comment = comment log.comment = comment
log.save log.save
elsif params['event'] == 'hangup' elsif params['event'] == 'hangup'
log = Cti::Log.find_by(call_id: params['callId']) log = Cti::Log.find_by(call_id: params['callId'])
raise "No such call_id #{params['callId']}" if !log raise "No such call_id #{params['callId']}" if !log
if params['direction'] == 'in' && log.state == 'newCall'
log.done = false
end
if params['direction'] == 'in' && log.to_comment == 'voicemail'
log.done = false
end
log.state = 'hangup' log.state = 'hangup'
log.end = Time.zone.now
log.comment = comment log.comment = comment
log.save log.save
else else

View file

@ -1,6 +1,7 @@
Zammad::Application.routes.draw do Zammad::Application.routes.draw do
match '/api/v1/cti/log', to: 'integration/sipgate#index', via: :get match '/api/v1/cti/log', to: 'integration/sipgate#index', via: :get
match '/api/v1/cti/done/:id', to: 'integration/sipgate#done', via: :post
match '/api/v1/sipgate/in', to: 'integration/sipgate#in', via: :post match '/api/v1/sipgate/in', to: 'integration/sipgate#in', via: :post
match '/api/v1/sipgate/out', to: 'integration/sipgate#out', via: :post match '/api/v1/sipgate/out', to: 'integration/sipgate#out', via: :post

View file

@ -0,0 +1,7 @@
class UpdateCtiLog < ActiveRecord::Migration
def up
add_column :cti_logs, :start, :timestamp, null: true
add_column :cti_logs, :end, :timestamp, null: true
add_column :cti_logs, :done, :boolean, null: false, default: true
end
end

View file

@ -68,7 +68,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
) )
groups = Group.where(name: 'Users') groups = Group.where(name: 'Users')
roles = Role.where(name: 'Agent') roles = Role.where(name: %w(Agent CTI))
agent = User.create_or_update( agent = User.create_or_update(
login: 'cti-agent@example.com', login: 'cti-agent@example.com',
firstname: 'E', firstname: 'E',
@ -225,6 +225,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('user 1', log.from_comment) assert_equal('user 1', log.from_comment)
assert_equal(nil, log.comment) assert_equal(nil, log.comment)
assert_equal('newCall', log.state) assert_equal('newCall', log.state)
assert_equal(true, log.done)
# outbound - I - hangup by agent # outbound - I - hangup by agent
params = 'event=hangup&direction=out&callId=1234567890-1&cause=cancel' params = 'event=hangup&direction=out&callId=1234567890-1&cause=cancel'
@ -238,6 +239,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('user 1', log.from_comment) assert_equal('user 1', log.from_comment)
assert_equal('cancel', log.comment) assert_equal('cancel', log.comment)
assert_equal('hangup', log.state) assert_equal('hangup', log.state)
assert_equal(true, log.done)
# outbound - II - new call # outbound - II - new call
params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&callId=1234567890-2&user%5B%5D=user+1' params = 'event=newCall&direction=out&from=4930600000000&to=4912347114711&callId=1234567890-2&user%5B%5D=user+1'
@ -251,6 +253,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('user 1', log.from_comment) assert_equal('user 1', log.from_comment)
assert_equal(nil, log.comment) assert_equal(nil, log.comment)
assert_equal('newCall', log.state) assert_equal('newCall', log.state)
assert_equal(true, log.done)
# outbound - II - answer by customer # outbound - II - answer by customer
params = 'event=answer&direction=out&callId=1234567890-2&from=4930600000000&to=4912347114711' params = 'event=answer&direction=out&callId=1234567890-2&from=4930600000000&to=4912347114711'
@ -264,6 +267,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('user 1', log.from_comment) assert_equal('user 1', log.from_comment)
assert_equal(nil, log.comment) assert_equal(nil, log.comment)
assert_equal('answer', log.state) assert_equal('answer', log.state)
assert_equal(true, log.done)
# outbound - II - hangup by customer # outbound - II - hangup by customer
params = 'event=hangup&direction=out&callId=1234567890-2&cause=normalClearing&from=4930600000000&to=4912347114711' params = 'event=hangup&direction=out&callId=1234567890-2&cause=normalClearing&from=4930600000000&to=4912347114711'
@ -277,6 +281,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('user 1', log.from_comment) assert_equal('user 1', log.from_comment)
assert_equal('normalClearing', log.comment) assert_equal('normalClearing', log.comment)
assert_equal('hangup', log.state) assert_equal('hangup', log.state)
assert_equal(true, log.done)
# inbound - I - new call # inbound - I - new call
params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&callId=1234567890-3&user%5B%5D=user+1' params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&callId=1234567890-3&user%5B%5D=user+1'
@ -290,6 +295,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('user 1', log.to_comment) assert_equal('user 1', log.to_comment)
assert_equal(nil, log.comment) assert_equal(nil, log.comment)
assert_equal('newCall', log.state) assert_equal('newCall', log.state)
assert_equal(true, log.done)
# inbound - I - answer by customer # inbound - I - answer by customer
params = 'event=answer&direction=in&callId=1234567890-3&to=4930600000000&from=4912347114711' params = 'event=answer&direction=in&callId=1234567890-3&to=4930600000000&from=4912347114711'
@ -303,6 +309,7 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('user 1', log.to_comment) assert_equal('user 1', log.to_comment)
assert_equal(nil, log.comment) assert_equal(nil, log.comment)
assert_equal('answer', log.state) assert_equal('answer', log.state)
assert_equal(true, log.done)
# inbound - I - hangup by customer # inbound - I - hangup by customer
params = 'event=hangup&direction=in&callId=1234567890-3&cause=normalClearing&to=4930600000000&from=4912347114711' params = 'event=hangup&direction=in&callId=1234567890-3&cause=normalClearing&to=4930600000000&from=4912347114711'
@ -316,7 +323,79 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_equal('user 1', log.to_comment) assert_equal('user 1', log.to_comment)
assert_equal('normalClearing', log.comment) assert_equal('normalClearing', log.comment)
assert_equal('hangup', log.state) assert_equal('hangup', log.state)
assert_equal(true, log.done)
# inbound - II - new call
params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&callId=1234567890-4&user%5B%5D=user+1,user+2'
post '/api/v1/sipgate/in', params
assert_response(200)
log = Cti::Log.find_by(call_id: '1234567890-4')
assert(log)
assert_equal('4930600000000', log.to)
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('user 1,user 2', log.to_comment)
assert_equal(nil, log.comment)
assert_equal('newCall', log.state)
assert_equal(true, log.done)
# inbound - II - answer by voicemail
params = 'event=answer&direction=in&callId=1234567890-4&to=4930600000000&from=4912347114711&user=voicemail'
post '/api/v1/sipgate/in', params
assert_response(200)
log = Cti::Log.find_by(call_id: '1234567890-4')
assert(log)
assert_equal('4930600000000', log.to)
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('voicemail', log.to_comment)
assert_equal(nil, log.comment)
assert_equal('answer', log.state)
assert_equal(true, log.done)
# inbound - II - hangup by customer
params = 'event=hangup&direction=in&callId=1234567890-4&cause=normalClearing&to=4930600000000&from=4912347114711'
post '/api/v1/sipgate/in', params
assert_response(200)
log = Cti::Log.find_by(call_id: '1234567890-4')
assert(log)
assert_equal('4930600000000', log.to)
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('voicemail', log.to_comment)
assert_equal('normalClearing', log.comment)
assert_equal('hangup', log.state)
assert_equal(false, log.done)
# inbound - III - new call
params = 'event=newCall&direction=in&to=4930600000000&from=4912347114711&callId=1234567890-5&user%5B%5D=user+1,user+2'
post '/api/v1/sipgate/in', params
assert_response(200)
log = Cti::Log.find_by(call_id: '1234567890-5')
assert(log)
assert_equal('4930600000000', log.to)
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('user 1,user 2', log.to_comment)
assert_equal(nil, log.comment)
assert_equal('newCall', log.state)
assert_equal(true, log.done)
# inbound - III - hangup by customer
params = 'event=hangup&direction=in&callId=1234567890-5&cause=normalClearing&to=4930600000000&from=4912347114711'
post '/api/v1/sipgate/in', params
assert_response(200)
log = Cti::Log.find_by(call_id: '1234567890-5')
assert(log)
assert_equal('4930600000000', log.to)
assert_equal('4912347114711', log.from)
assert_equal('in', log.direction)
assert_equal('user 1,user 2', log.to_comment)
assert_equal('normalClearing', log.comment)
assert_equal('hangup', log.state)
assert_equal(false, log.done)
# get caller list
get '/api/v1/cti/log' get '/api/v1/cti/log'
assert_response(401) assert_response(401)
@ -326,7 +405,15 @@ class SipgateControllerTest < ActionDispatch::IntegrationTest
assert_response(200) assert_response(200)
result = JSON.parse(@response.body) result = JSON.parse(@response.body)
assert_equal(result.class, Array) assert_equal(result.class, Array)
assert_equal(3, result.count) assert_equal(5, result.count)
assert_equal('hangup', result[1]['state'])
assert_equal('4930777000000', result[1]['from'])
assert_equal('user 1', result[1]['from_comment'])
assert_equal('4912347114711', result[1]['to'])
assert_equal(nil, result[1]['to_comment'])
assert_equal('1234567890-2', result[1]['call_id'])
assert_equal('normalClearing', result[1]['comment'])
assert_equal('hangup', result[1]['state'])
end end