diff --git a/app/models/chat.rb b/app/models/chat.rb index bd1a4a64a..96988d943 100644 --- a/app/models/chat.rb +++ b/app/models/chat.rb @@ -26,7 +26,7 @@ class Chat < ApplicationModel } # get queue postion if needed - session = Chat.session_state(session_id) + session = Chat::Session.messages_by_session_id(session_id) if session return { state: 'reconnect', @@ -51,10 +51,10 @@ class Chat < ApplicationModel end # if all seads are used - if active_chat_count >= max_queue + if Chat.active_chat_count >= max_queue return { state: 'no_seats_available', - queue: seads_available, + queue: Chat.seads_available, } end @@ -62,32 +62,14 @@ class Chat < ApplicationModel { state: 'online' } end - def self.session_state(session_id) - session_attributes = [] - chat_session = Chat::Session.find_by(session_id: session_id) - return if !chat_session - Chat::Message.where(chat_session_id: chat_session.id).each { |message| - session_attributes.push message.attributes - } - session_attributes - end - def self.agent_state(user_id) return { state: 'chat_disabled' } if !Setting.get('chat') - actice_sessions = [] - Chat::Session.where(state: 'running', user_id: user_id).order('created_at ASC').each {|session| - session_attributes = session.attributes - session_attributes['messages'] = [] - Chat::Message.where(chat_session_id: session.id).each { |message| - session_attributes['messages'].push message.attributes - } - actice_sessions.push session_attributes - } { waiting_chat_count: waiting_chat_count, running_chat_count: running_chat_count, - #available_agents: available_agents, - active_sessions: actice_sessions, + active_sessions: Chat::Session.active_chats_by_user_id(user_id), + seads_available: seads_available, + seads_total: seads_total, active: Chat::Agent.state(user_id) } end @@ -100,11 +82,11 @@ class Chat < ApplicationModel Chat::Session.where(state: ['running']).count end - def active_chat_count + def self.active_chat_count Chat::Session.where(state: %w(waiting running)).count end - def available_agents(diff = 2.minutes) + def self.available_agents(diff = 2.minutes) agents = {} Chat::Agent.where(active: true).where('updated_at > ?', Time.zone.now - diff).each {|record| agents[record.updated_by_id] = record.concurrent @@ -112,15 +94,15 @@ class Chat < ApplicationModel agents end - def seads_total(diff = 2.minutes) + def self.seads_total(diff = 2.minutes) total = 0 - available_agents(diff).each {|_record, concurrent| + available_agents(diff).each {|_user_id, concurrent| total += concurrent } total end - def seads_available(diff = 2.minutes) + def self.seads_available(diff = 2.minutes) seads_total(diff) - active_chat_count end end diff --git a/app/models/chat/session.rb b/app/models/chat/session.rb index 4932fc1e1..0edd06435 100644 --- a/app/models/chat/session.rb +++ b/app/models/chat/session.rb @@ -35,4 +35,27 @@ class Chat::Session < ApplicationModel } position end + + def self.messages_by_session_id(session_id) + chat_session = Chat::Session.find_by(session_id: session_id) + return if !chat_session + session_attributes = [] + Chat::Message.where(chat_session_id: chat_session.id).each { |message| + session_attributes.push message.attributes + } + session_attributes + end + + def self.active_chats_by_user_id(user_id) + actice_sessions = [] + Chat::Session.where(state: 'running', user_id: user_id).order('created_at ASC').each {|session| + session_attributes = session.attributes + session_attributes['messages'] = [] + Chat::Message.where(chat_session_id: session.id).each { |message| + session_attributes['messages'].push message.attributes + } + actice_sessions.push session_attributes + } + actice_sessions + end end diff --git a/db/migrate/20151113000001_update_chat.rb b/db/migrate/20151113000001_update_chat.rb new file mode 100644 index 000000000..4cfa73acd --- /dev/null +++ b/db/migrate/20151113000001_update_chat.rb @@ -0,0 +1,9 @@ +class UpdateChat < ActiveRecord::Migration + def up + add_index :chat_sessions, [:session_id] + add_index :chat_sessions, [:chat_id] + add_index :chat_messages, [:chat_session_id] + add_index :chat_agents, [:active] + add_index :chat_agents, [:updated_by_id], unique: true + end +end diff --git a/lib/sessions/event/chat_base.rb b/lib/sessions/event/chat_base.rb index e687cf2a8..cdd9f0587 100644 --- a/lib/sessions/event/chat_base.rb +++ b/lib/sessions/event/chat_base.rb @@ -4,7 +4,6 @@ class Sessions::Event::ChatBase @data = data @session = session @client_id = client_id - end def pre @@ -35,4 +34,22 @@ class Sessions::Event::ChatBase } 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 + end diff --git a/lib/sessions/event/chat_session_close.rb b/lib/sessions/event/chat_session_close.rb index d0f0005ad..6827f6a68 100644 --- a/lib/sessions/event/chat_session_close.rb +++ b/lib/sessions/event/chat_session_close.rb @@ -49,6 +49,9 @@ class Sessions::Event::ChatSessionClose < Sessions::Event::ChatBase # set state update to all agents broadcast_agent_state_update + # send position update to other waiting sessions + broadcast_customer_state_update + # notify about "leaving" else message = { diff --git a/lib/sessions/event/chat_session_start.rb b/lib/sessions/event/chat_session_start.rb index 4f14df7f9..0480c6259 100644 --- a/lib/sessions/event/chat_session_start.rb +++ b/lib/sessions/event/chat_session_start.rb @@ -39,20 +39,9 @@ class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase chat_session.send_to_recipients(data) # 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) - } + broadcast_customer_state_update + # send state update to agents broadcast_agent_state_update nil diff --git a/public/assets/chat/chat.coffee b/public/assets/chat/chat.coffee index 4d15ef45c..52a6255dd 100644 --- a/public/assets/chat/chat.coffee +++ b/public/assets/chat/chat.coffee @@ -132,12 +132,15 @@ do($ = window.jQuery, window) -> @log 'debug', 'Zammad Chat: ready' when 'offline' @onError 'Zammad Chat: No agent online' + @hide() @wsClose() when 'chat_disabled' @onError 'Zammad Chat: Chat is disabled' + @hide() @wsClose() when 'no_seats_available' @onError 'Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue + @hide() @wsClose() when 'reconnect' @log 'debug', 'old messages', pipe.data.session @@ -291,9 +294,6 @@ do($ = window.jQuery, window) -> if @onInitialQueueDelayId clearTimeout(@onInitialQueueDelayId) - sessionStorage.removeItem 'sessionId' - sessionStorage.removeItem 'unfinished_message' - @closeWindow() closeWindow: => @@ -309,6 +309,10 @@ do($ = window.jQuery, window) -> session_id: @sessionId @setSessionId undefined + sessionStorage.removeItem 'unfinished_message' + + # restart connection + @onWebSocketOpen() hide: -> @el.removeClass('zammad-chat-is-visible') diff --git a/public/assets/chat/index.html b/public/assets/chat/index.html index 1bbc4360d..dc811675e 100644 --- a/public/assets/chat/index.html +++ b/public/assets/chat/index.html @@ -2,7 +2,6 @@ Zammad Chat - - +
@@ -142,9 +141,6 @@ host: 'ws://localhost', port: 6042, debug: true, - background: '#494d52', - flat: true, - show: false }); $('.settings :input').on({ diff --git a/public/assets/chat/znuny.html b/public/assets/chat/znuny.html new file mode 100644 index 000000000..1da94fc28 --- /dev/null +++ b/public/assets/chat/znuny.html @@ -0,0 +1,147 @@ + + +Zammad Chat + + + + + + + + +
+
+ + + + + + +
+

Settings

+
+ + + +
+ + + +
+ + px + + +
+ + px + + +
+ +
+
+ + + + \ No newline at end of file diff --git a/test/unit/chat_test.rb b/test/unit/chat_test.rb new file mode 100644 index 000000000..1cc260205 --- /dev/null +++ b/test/unit/chat_test.rb @@ -0,0 +1,306 @@ +# encoding: utf-8 +require 'test_helper' + +class ChatTest < ActiveSupport::TestCase + + # create base + groups = Group.all + roles = Role.where( name: %w(Agent Chat) ) + agent1 = User.create_or_update( + login: 'ticket-chat-agent1@example.com', + firstname: 'Notification', + lastname: 'Agent1', + email: 'ticket-chat-agent1@example.com', + password: 'agentpw', + active: true, + roles: roles, + groups: groups, + updated_at: '2015-02-05 16:37:00', + updated_by_id: 1, + created_by_id: 1, + ) + agent2 = User.create_or_update( + login: 'ticket-chat-agent2@example.com', + firstname: 'Notification', + lastname: 'Agent2', + email: 'ticket-chat-agent2@example.com', + password: 'agentpw', + active: true, + roles: roles, + groups: groups, + updated_at: '2015-02-05 16:38:00', + updated_by_id: 1, + created_by_id: 1, + ) + + test 'default test' do + + Chat.delete_all + Chat::Topic.delete_all + Chat::Session.delete_all + Chat::Message.delete_all + Chat::Agent.delete_all + Setting.set('chat', false) + chat = Chat.create( + name: 'default', + max_queue: 5, + note: '', + active: true, + updated_by_id: 1, + created_by_id: 1, + ) + chat_topic = Chat::Topic.create( + chat_id: chat.id, + name: 'default', + updated_by_id: 1, + created_by_id: 1, + ) + + # check if feature is disabled + assert_equal('chat_disabled', chat.customer_state[:state]) + assert_equal('chat_disabled', Chat.agent_state(agent1.id)[:state]) + Setting.set('chat', true) + + # check customer state + assert_equal('offline', chat.customer_state[:state]) + + # check agent state + agent_state = Chat.agent_state(agent1.id) + assert_equal(0, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(0, agent_state[:seads_available]) + assert_equal(0, agent_state[:seads_total]) + assert_equal(false, agent_state[:active]) + + # set agent 1 to active + chat_agent1 = Chat::Agent.create_or_update( + active: true, + concurrent: 4, + updated_by_id: agent1.id, + created_by_id: agent1.id, + ) + + # check customer state + assert_equal('online', chat.customer_state[:state]) + + # check agent state + agent_state = Chat.agent_state(agent1.id) + assert_equal(0, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(4, agent_state[:seads_available]) + assert_equal(4, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # start session + chat_session1 = Chat::Session.create( + chat_id: chat.id, + user_id: agent1.id, + ) + assert(chat_session1.session_id) + + # check customer state + assert_equal('online', chat.customer_state[:state]) + + # check agent state + agent_state = Chat.agent_state(agent1.id) + assert_equal(1, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(3, agent_state[:seads_available]) + assert_equal(4, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # activate second agent + chat_agent2 = Chat::Agent.create_or_update( + active: true, + concurrent: 2, + updated_by_id: agent2.id, + created_by_id: agent2.id, + ) + + # check customer state + assert_equal('online', chat.customer_state[:state]) + + # check agent1 state + agent_state = Chat.agent_state(agent1.id) + assert_equal(1, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(5, agent_state[:seads_available]) + assert_equal(6, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # check agent2 state + agent_state = Chat.agent_state(agent2.id) + assert_equal(1, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(5, agent_state[:seads_available]) + assert_equal(6, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # start next chat + chat_session2 = Chat::Session.create( + chat_id: chat.id, + ) + + # check customer state + assert_equal('online', chat.customer_state[:state]) + + # check agent1 state + agent_state = Chat.agent_state(agent1.id) + assert_equal(2, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(4, agent_state[:seads_available]) + assert_equal(6, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # check agent2 state + agent_state = Chat.agent_state(agent2.id) + assert_equal(2, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(4, agent_state[:seads_available]) + assert_equal(6, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # start new chats + chat_session3 = Chat::Session.create( + chat_id: chat.id, + ) + chat_session4 = Chat::Session.create( + chat_id: chat.id, + ) + chat_session5 = Chat::Session.create( + chat_id: chat.id, + ) + chat_session6 = Chat::Session.create( + chat_id: chat.id, + ) + + # check customer state + assert_equal('no_seats_available', chat.customer_state[:state]) + + # check agent1 state + agent_state = Chat.agent_state(agent1.id) + assert_equal(6, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(0, agent_state[:seads_available]) + assert_equal(6, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # check agent2 state + agent_state = Chat.agent_state(agent2.id) + assert_equal(6, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(0, agent_state[:seads_available]) + assert_equal(6, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + chat_session6.state = 'running' + chat_session6.save + + # check customer state + assert_equal('no_seats_available', chat.customer_state[:state]) + assert_equal(0, chat.customer_state[:queue]) + + # check agent1 state + agent_state = Chat.agent_state(agent1.id) + assert_equal(5, agent_state[:waiting_chat_count]) + assert_equal(1, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(0, agent_state[:seads_available]) + assert_equal(6, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # check agent2 state + agent_state = Chat.agent_state(agent2.id) + assert_equal(5, agent_state[:waiting_chat_count]) + assert_equal(1, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(0, agent_state[:seads_available]) + assert_equal(6, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + chat_agent2.active = false + chat_agent2.save + + # check customer state + assert_equal('no_seats_available', chat.customer_state[:state]) + assert_equal(-2, chat.customer_state[:queue]) + + # check agent1 state + agent_state = Chat.agent_state(agent1.id) + assert_equal(5, agent_state[:waiting_chat_count]) + assert_equal(1, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(-2, agent_state[:seads_available]) + assert_equal(4, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # check agent2 state + agent_state = Chat.agent_state(agent2.id) + assert_equal(5, agent_state[:waiting_chat_count]) + assert_equal(1, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(-2, agent_state[:seads_available]) + assert_equal(4, agent_state[:seads_total]) + assert_equal(false, agent_state[:active]) + + chat_session6.state = 'closed' + chat_session6.save + + # check customer state + assert_equal('no_seats_available', chat.customer_state[:state]) + assert_equal(-1, chat.customer_state[:queue]) + + # check agent1 state + agent_state = Chat.agent_state(agent1.id) + assert_equal(5, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(-1, agent_state[:seads_available]) + assert_equal(4, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # check agent2 state + agent_state = Chat.agent_state(agent2.id) + assert_equal(5, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(-1, agent_state[:seads_available]) + assert_equal(4, agent_state[:seads_total]) + assert_equal(false, agent_state[:active]) + + chat_session5.destroy + chat_session4.destroy + + # check customer state + assert_equal('online', chat.customer_state[:state]) + + # check agent1 state + agent_state = Chat.agent_state(agent1.id) + assert_equal(3, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(1, agent_state[:seads_available]) + assert_equal(4, agent_state[:seads_total]) + assert_equal(true, agent_state[:active]) + + # check agent2 state + agent_state = Chat.agent_state(agent2.id) + assert_equal(3, agent_state[:waiting_chat_count]) + assert_equal(0, agent_state[:running_chat_count]) + assert_equal([], agent_state[:active_sessions]) + assert_equal(1, agent_state[:seads_available]) + assert_equal(4, agent_state[:seads_total]) + assert_equal(false, agent_state[:active]) + + end + +end