Added set of auto offline if agent is not answering chats for 120 seconds.

This commit is contained in:
Martin Edenhofer 2016-01-06 01:45:03 +01:00
parent 4803fa9383
commit f5fa05bf96
12 changed files with 143 additions and 40 deletions

View file

@ -18,6 +18,7 @@ class App.CustomerChat extends App.Controller
@maxChatWindows = parseInt(preferences.chat.max_windows)
@pushStateIntervalOn = undefined
@idleTimeout = parseInt(@Config.get('chat_agent_idle_timeout') || 120)
@messageCounter = 0
@meta =
active: false
@ -107,10 +108,10 @@ class App.CustomerChat extends App.Controller
@el.find('.js-waitingCustomers').popover(
trigger: 'hover'
container: 'body'
html: true
animation: false
delay: 100
placement: 'bottom'
title: ->
App.i18n.translateContent('Waiting Customers')
content: =>
@ -119,10 +120,10 @@ class App.CustomerChat extends App.Controller
@el.find('.js-chattingCustomers').popover(
trigger: 'hover'
container: 'body'
html: true
animation: false
delay: 100
placement: 'bottom'
title: ->
App.i18n.translateContent('Chatting Customers')
content: =>
@ -131,10 +132,10 @@ class App.CustomerChat extends App.Controller
@el.find('.js-activeAgents').popover(
trigger: 'hover'
container: 'body'
html: true
animation: false
delay: 100
placement: 'bottom'
title: ->
App.i18n.translateContent('Active Agents')
content: =>
@ -225,8 +226,11 @@ class App.CustomerChat extends App.Controller
updateMeta: =>
if @meta.waiting_chat_count && @maxChatWindows > @windowCount()
@$('.js-acceptChat').addClass('is-clickable is-blinking')
@idleTimeoutStart()
else
@$('.js-acceptChat').removeClass('is-clickable is-blinking')
@idleTimeoutStop()
@$('.js-badgeWaitingCustomers').text(@meta.waiting_chat_count)
@$('.js-badgeChattingCustomers').text(@meta.running_chat_count)
@$('.js-badgeActiveAgents').text(@meta.active_agent_count)
@ -271,6 +275,7 @@ class App.CustomerChat extends App.Controller
acceptChat: =>
return if @windowCount() >= @maxChatWindows
App.WebSocket.send(event:'chat_session_start')
@idleTimeoutStop()
settings: (errors = {}) ->
new Setting(
@ -278,6 +283,21 @@ class App.CustomerChat extends App.Controller
errors: errors
)
idleTimeoutStart: =>
return if @idleTimeoutId
switchOff = =>
@switch(false)
@notify(
type: 'notice'
msg: App.i18n.translateContent('Chat not answered, set to offline automatically.')
)
@idleTimeoutId = @delay(switchOff, @idleTimeout * 1000)
idleTimeoutStop: =>
return if !@idleTimeoutId
@clearDelay(@idleTimeoutId)
@idleTimeoutId = undefined
class CustomerChatRouter extends App.ControllerPermanent
constructor: (params) ->
super

View file

@ -165,6 +165,57 @@ class Chat < ApplicationModel
=begin
broadcast new agent status to all agents
Chat.broadcast_agent_state_update
optional you can ignore it for dedecated user
Chat.broadcast_agent_state_update(ignore_user_id)
=end
def self.broadcast_agent_state_update(ignore_user_id = nil)
# send broadcast to agents
Chat::Agent.where('active = ? OR updated_at > ?', true, Time.zone.now - 15.minutes).each {|item|
next if item.updated_by_id == ignore_user_id
data = {
event: 'chat_status_agent',
data: Chat.agent_state(item.updated_by_id),
}
Sessions.send_to(item.updated_by_id, data)
}
end
=begin
broadcast new customer queue position to all waiting customers
Chat.broadcast_customer_state_update
=end
def self.broadcast_customer_state_update
# send position update to other waiting sessions
position = 0
Chat::Session.where(state: 'waiting').order('created_at ASC').each {|local_chat_session|
position += 1
data = {
event: 'chat_session_queue',
data: {
state: 'queue',
position: position,
session_id: local_chat_session.session_id,
},
}
local_chat_session.send_to_recipients(data)
}
end
=begin
cleanup old chat messages
Chat.cleanup

View file

@ -31,6 +31,7 @@ class Observer::Chat::Leave::BackgroundJob
}
chat_session.send_to_recipients(message, @client_id)
Chat.broadcast_agent_state_update
end
end

View file

@ -0,0 +1,25 @@
class UpdateChat5 < ActiveRecord::Migration
def change
Setting.create_if_not_exists(
title: 'Agent idle timeout',
name: 'chat_agent_idle_timeout',
area: 'Chat::Extended',
description: 'Idle timeout in seconds till agent is set offline automatically.',
options: {
form: [
{
display: '',
null: false,
name: 'chat_agent_idle_timeout',
tag: 'input',
},
],
},
preferences: {},
state: '120',
frontend: true
)
end
end

View file

@ -1214,6 +1214,26 @@ Setting.create_if_not_exists(
frontend: true
)
Setting.create_if_not_exists(
title: 'Agent idle timeout',
name: 'chat_agent_idle_timeout',
area: 'Chat::Extended',
description: 'Idle timeout in seconds till agent is set offline automatically.',
options: {
form: [
{
display: '',
null: false,
name: 'chat_agent_idle_timeout',
tag: 'input',
},
],
},
preferences: {},
state: '120',
frontend: true
)
Setting.create_if_not_exists(
title: 'Define searchable models.',
name: 'models_searchable',

View file

@ -9,7 +9,7 @@ class Sessions::Event::ChatAgentState < Sessions::Event::ChatBase
Chat::Agent.state(@session['id'], @payload['data']['active'])
# broadcast new state to agents
broadcast_agent_state_update(@session['id'])
Chat.broadcast_agent_state_update(@session['id'])
{
event: 'chat_agent_state',

View file

@ -23,37 +23,6 @@ class Sessions::Event::ChatBase < Sessions::Event::Base
}
end
def broadcast_agent_state_update(ignore_user_id = nil)
# send broadcast to agents
Chat::Agent.where(active: true).each {|item|
next if item.updated_by_id == ignore_user_id
data = {
event: 'chat_status_agent',
data: Chat.agent_state(item.updated_by_id),
}
Sessions.send_to(item.updated_by_id, data)
}
end
def broadcast_customer_state_update
# send position update to other waiting sessions
position = 0
Chat::Session.where(state: 'waiting').order('created_at ASC').each {|local_chat_session|
position += 1
data = {
event: 'chat_session_queue',
data: {
state: 'queue',
position: position,
session_id: local_chat_session.session_id,
},
}
local_chat_session.send_to_recipients(data)
}
end
def agent_permission_check
if !@session
error = {

View file

@ -32,10 +32,10 @@ class Sessions::Event::ChatSessionClose < Sessions::Event::ChatBase
chat_session.save
# set state update to all agents
broadcast_agent_state_update
Chat.broadcast_agent_state_update
# send position update to other waiting sessions
broadcast_customer_state_update
Chat.broadcast_customer_state_update
# notify about "leaving"
else

View file

@ -23,7 +23,7 @@ class Sessions::Event::ChatSessionInit < Sessions::Event::ChatBase
)
# send broadcast to agents
broadcast_agent_state_update
Chat.broadcast_agent_state_update
# return new session
{

View file

@ -52,10 +52,10 @@ class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase
Sessions.send(@client_id, data)
# send state update with sessions to agents
broadcast_agent_state_update
Chat.broadcast_agent_state_update
# send position update to other waiting sessions
broadcast_customer_state_update
Chat.broadcast_customer_state_update
nil
end

View file

@ -8,6 +8,7 @@ export ZAMMAD_SETTING_TTL=15
rails r "Setting.set('developer_mode', true)"
rails r "Setting.set('websocket_port', '$WS_PORT')"
rails r "Setting.set('fqdn', '$IP:$BROWSER_PORT')"
rails r "Setting.set('chat_agent_idle_timeout', '45')"
pumactl start --pidfile tmp/pids/puma.pid -d -p $APP_PORT -e $RAILS_ENV
script/websocket-server.rb start -d -p $WS_PORT

View file

@ -576,6 +576,11 @@ class ChatTest < TestCase
)
agent.find_elements( { css: '.active .chat-window .js-close' } ).each(&:click)
exists(
browser: agent,
css: '#navigation .js-switch input[checked]'
)
# no customer action, hide widget
customer = browser_instance
location(
@ -618,6 +623,17 @@ class ChatTest < TestCase
timeout: 120,
)
# check if agent is offline, idle timeout, chat not answered
exists_not(
browser: agent,
css: '#navigation .js-switch input[checked]'
)
switch(
browser: agent,
css: '#navigation .js-switch',
type: 'on',
)
# no customer action, show sorry screen
reload(
browser: customer,