Added set of auto offline if agent is not answering chats for 120 seconds.
This commit is contained in:
parent
4803fa9383
commit
f5fa05bf96
12 changed files with 143 additions and 40 deletions
|
@ -18,6 +18,7 @@ class App.CustomerChat extends App.Controller
|
||||||
@maxChatWindows = parseInt(preferences.chat.max_windows)
|
@maxChatWindows = parseInt(preferences.chat.max_windows)
|
||||||
|
|
||||||
@pushStateIntervalOn = undefined
|
@pushStateIntervalOn = undefined
|
||||||
|
@idleTimeout = parseInt(@Config.get('chat_agent_idle_timeout') || 120)
|
||||||
@messageCounter = 0
|
@messageCounter = 0
|
||||||
@meta =
|
@meta =
|
||||||
active: false
|
active: false
|
||||||
|
@ -107,10 +108,10 @@ class App.CustomerChat extends App.Controller
|
||||||
|
|
||||||
@el.find('.js-waitingCustomers').popover(
|
@el.find('.js-waitingCustomers').popover(
|
||||||
trigger: 'hover'
|
trigger: 'hover'
|
||||||
container: 'body'
|
|
||||||
html: true
|
html: true
|
||||||
animation: false
|
animation: false
|
||||||
delay: 100
|
delay: 100
|
||||||
|
placement: 'bottom'
|
||||||
title: ->
|
title: ->
|
||||||
App.i18n.translateContent('Waiting Customers')
|
App.i18n.translateContent('Waiting Customers')
|
||||||
content: =>
|
content: =>
|
||||||
|
@ -119,10 +120,10 @@ class App.CustomerChat extends App.Controller
|
||||||
|
|
||||||
@el.find('.js-chattingCustomers').popover(
|
@el.find('.js-chattingCustomers').popover(
|
||||||
trigger: 'hover'
|
trigger: 'hover'
|
||||||
container: 'body'
|
|
||||||
html: true
|
html: true
|
||||||
animation: false
|
animation: false
|
||||||
delay: 100
|
delay: 100
|
||||||
|
placement: 'bottom'
|
||||||
title: ->
|
title: ->
|
||||||
App.i18n.translateContent('Chatting Customers')
|
App.i18n.translateContent('Chatting Customers')
|
||||||
content: =>
|
content: =>
|
||||||
|
@ -131,10 +132,10 @@ class App.CustomerChat extends App.Controller
|
||||||
|
|
||||||
@el.find('.js-activeAgents').popover(
|
@el.find('.js-activeAgents').popover(
|
||||||
trigger: 'hover'
|
trigger: 'hover'
|
||||||
container: 'body'
|
|
||||||
html: true
|
html: true
|
||||||
animation: false
|
animation: false
|
||||||
delay: 100
|
delay: 100
|
||||||
|
placement: 'bottom'
|
||||||
title: ->
|
title: ->
|
||||||
App.i18n.translateContent('Active Agents')
|
App.i18n.translateContent('Active Agents')
|
||||||
content: =>
|
content: =>
|
||||||
|
@ -225,8 +226,11 @@ class App.CustomerChat extends App.Controller
|
||||||
updateMeta: =>
|
updateMeta: =>
|
||||||
if @meta.waiting_chat_count && @maxChatWindows > @windowCount()
|
if @meta.waiting_chat_count && @maxChatWindows > @windowCount()
|
||||||
@$('.js-acceptChat').addClass('is-clickable is-blinking')
|
@$('.js-acceptChat').addClass('is-clickable is-blinking')
|
||||||
|
@idleTimeoutStart()
|
||||||
else
|
else
|
||||||
@$('.js-acceptChat').removeClass('is-clickable is-blinking')
|
@$('.js-acceptChat').removeClass('is-clickable is-blinking')
|
||||||
|
@idleTimeoutStop()
|
||||||
|
|
||||||
@$('.js-badgeWaitingCustomers').text(@meta.waiting_chat_count)
|
@$('.js-badgeWaitingCustomers').text(@meta.waiting_chat_count)
|
||||||
@$('.js-badgeChattingCustomers').text(@meta.running_chat_count)
|
@$('.js-badgeChattingCustomers').text(@meta.running_chat_count)
|
||||||
@$('.js-badgeActiveAgents').text(@meta.active_agent_count)
|
@$('.js-badgeActiveAgents').text(@meta.active_agent_count)
|
||||||
|
@ -271,6 +275,7 @@ class App.CustomerChat extends App.Controller
|
||||||
acceptChat: =>
|
acceptChat: =>
|
||||||
return if @windowCount() >= @maxChatWindows
|
return if @windowCount() >= @maxChatWindows
|
||||||
App.WebSocket.send(event:'chat_session_start')
|
App.WebSocket.send(event:'chat_session_start')
|
||||||
|
@idleTimeoutStop()
|
||||||
|
|
||||||
settings: (errors = {}) ->
|
settings: (errors = {}) ->
|
||||||
new Setting(
|
new Setting(
|
||||||
|
@ -278,6 +283,21 @@ class App.CustomerChat extends App.Controller
|
||||||
errors: errors
|
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
|
class CustomerChatRouter extends App.ControllerPermanent
|
||||||
constructor: (params) ->
|
constructor: (params) ->
|
||||||
super
|
super
|
||||||
|
|
|
@ -165,6 +165,57 @@ class Chat < ApplicationModel
|
||||||
|
|
||||||
=begin
|
=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
|
cleanup old chat messages
|
||||||
|
|
||||||
Chat.cleanup
|
Chat.cleanup
|
||||||
|
|
|
@ -31,6 +31,7 @@ class Observer::Chat::Leave::BackgroundJob
|
||||||
}
|
}
|
||||||
chat_session.send_to_recipients(message, @client_id)
|
chat_session.send_to_recipients(message, @client_id)
|
||||||
|
|
||||||
|
Chat.broadcast_agent_state_update
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
25
db/migrate/20160106000001_update_chat5.rb
Normal file
25
db/migrate/20160106000001_update_chat5.rb
Normal 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
|
20
db/seeds.rb
20
db/seeds.rb
|
@ -1214,6 +1214,26 @@ Setting.create_if_not_exists(
|
||||||
frontend: true
|
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(
|
Setting.create_if_not_exists(
|
||||||
title: 'Define searchable models.',
|
title: 'Define searchable models.',
|
||||||
name: 'models_searchable',
|
name: 'models_searchable',
|
||||||
|
|
|
@ -9,7 +9,7 @@ class Sessions::Event::ChatAgentState < Sessions::Event::ChatBase
|
||||||
Chat::Agent.state(@session['id'], @payload['data']['active'])
|
Chat::Agent.state(@session['id'], @payload['data']['active'])
|
||||||
|
|
||||||
# broadcast new state to agents
|
# broadcast new state to agents
|
||||||
broadcast_agent_state_update(@session['id'])
|
Chat.broadcast_agent_state_update(@session['id'])
|
||||||
|
|
||||||
{
|
{
|
||||||
event: 'chat_agent_state',
|
event: 'chat_agent_state',
|
||||||
|
|
|
@ -23,37 +23,6 @@ class Sessions::Event::ChatBase < Sessions::Event::Base
|
||||||
}
|
}
|
||||||
end
|
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
|
def agent_permission_check
|
||||||
if !@session
|
if !@session
|
||||||
error = {
|
error = {
|
||||||
|
|
|
@ -32,10 +32,10 @@ class Sessions::Event::ChatSessionClose < Sessions::Event::ChatBase
|
||||||
chat_session.save
|
chat_session.save
|
||||||
|
|
||||||
# set state update to all agents
|
# set state update to all agents
|
||||||
broadcast_agent_state_update
|
Chat.broadcast_agent_state_update
|
||||||
|
|
||||||
# send position update to other waiting sessions
|
# send position update to other waiting sessions
|
||||||
broadcast_customer_state_update
|
Chat.broadcast_customer_state_update
|
||||||
|
|
||||||
# notify about "leaving"
|
# notify about "leaving"
|
||||||
else
|
else
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Sessions::Event::ChatSessionInit < Sessions::Event::ChatBase
|
||||||
)
|
)
|
||||||
|
|
||||||
# send broadcast to agents
|
# send broadcast to agents
|
||||||
broadcast_agent_state_update
|
Chat.broadcast_agent_state_update
|
||||||
|
|
||||||
# return new session
|
# return new session
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,10 +52,10 @@ class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase
|
||||||
Sessions.send(@client_id, data)
|
Sessions.send(@client_id, data)
|
||||||
|
|
||||||
# send state update with sessions to agents
|
# send state update with sessions to agents
|
||||||
broadcast_agent_state_update
|
Chat.broadcast_agent_state_update
|
||||||
|
|
||||||
# send position update to other waiting sessions
|
# send position update to other waiting sessions
|
||||||
broadcast_customer_state_update
|
Chat.broadcast_customer_state_update
|
||||||
|
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,7 @@ export ZAMMAD_SETTING_TTL=15
|
||||||
rails r "Setting.set('developer_mode', true)"
|
rails r "Setting.set('developer_mode', true)"
|
||||||
rails r "Setting.set('websocket_port', '$WS_PORT')"
|
rails r "Setting.set('websocket_port', '$WS_PORT')"
|
||||||
rails r "Setting.set('fqdn', '$IP:$BROWSER_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
|
pumactl start --pidfile tmp/pids/puma.pid -d -p $APP_PORT -e $RAILS_ENV
|
||||||
script/websocket-server.rb start -d -p $WS_PORT
|
script/websocket-server.rb start -d -p $WS_PORT
|
||||||
|
|
|
@ -576,6 +576,11 @@ class ChatTest < TestCase
|
||||||
)
|
)
|
||||||
agent.find_elements( { css: '.active .chat-window .js-close' } ).each(&:click)
|
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
|
# no customer action, hide widget
|
||||||
customer = browser_instance
|
customer = browser_instance
|
||||||
location(
|
location(
|
||||||
|
@ -618,6 +623,17 @@ class ChatTest < TestCase
|
||||||
timeout: 120,
|
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
|
# no customer action, show sorry screen
|
||||||
reload(
|
reload(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
|
|
Loading…
Reference in a new issue