From af7a49164b8c3e2af36415fee594f71e09b17785 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 11 Dec 2017 18:05:30 -0200 Subject: [PATCH 1/3] Add impersonate option for livechat triggers Closes #8941 --- packages/rocketchat-i18n/i18n/en.i18n.json | 5 +- .../app/client/lib/chatMessages.js | 11 ++- .../app/client/lib/triggers.js | 68 +++++++++++++++---- .../client/stylesheets/livechat.less | 6 +- .../client/views/app/livechatTriggersForm.js | 12 ++-- .../app/triggers/livechatTriggerAction.html | 9 ++- .../app/triggers/livechatTriggerAction.js | 19 ++++++ packages/rocketchat-livechat/package.js | 1 + .../server/lib/Livechat.js | 38 +++++++---- .../server/lib/QueueMethods.js | 8 ++- .../server/methods/getNextAgent.js | 25 +++++++ .../server/methods/sendMessageLivechat.js | 17 ++++- 12 files changed, 179 insertions(+), 40 deletions(-) create mode 100644 packages/rocketchat-livechat/server/methods/getNextAgent.js diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 59bf4aa6105a..8126a0788f5f 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -434,6 +434,7 @@ "Current_Chats": "Current Chats", "Current_Status": "Current Status", "Custom": "Custom", + "Custom_agent": "Custom agent", "Custom_Emoji": "Custom Emoji", "Custom_Emoji_Add": "Add New Emoji", "Custom_Emoji_Added_Successfully": "Custom emoji added successfully", @@ -822,6 +823,7 @@ "Iframe_Integration_send_target_origin_Description": "Origin with protocol prefix, which commands are sent to e.g. 'https://localhost', or * to allow sending to anywhere.", "IMAP_intercepter_already_running": "IMAP intercepter already running", "IMAP_intercepter_Not_running": "IMAP intercepter Not running", + "Impersonate_next_agent_from_queue": "Impersonate next agent from queue", "Impersonate_user": "Impersonate User", "Impersonate_user_description": "When enabled, integration posts as the user that triggered integration", "Import": "Import", @@ -1606,6 +1608,7 @@ "Select_a_department": "Select a department", "Select_a_user": "Select a user", "Select_an_avatar": "Select an avatar", + "Select_an_option": "Select an option", "Select_file": "Select file", "Select_role": "Select a Role", "Select_service_to_login": "Select a service to login to load your picture or upload one directly from your computer", @@ -2122,4 +2125,4 @@ "your_message_optional": "your message (optional)", "Your_password_is_wrong": "Your password is wrong!", "Your_push_was_sent_to_s_devices": "Your push was sent to %s devices" -} \ No newline at end of file +} diff --git a/packages/rocketchat-livechat/app/client/lib/chatMessages.js b/packages/rocketchat-livechat/app/client/lib/chatMessages.js index cb41d52ef9e9..362cc7069ffd 100644 --- a/packages/rocketchat-livechat/app/client/lib/chatMessages.js +++ b/packages/rocketchat-livechat/app/client/lib/chatMessages.js @@ -111,7 +111,16 @@ this.ChatMessages = class ChatMessages { }; MsgTyping.stop(rid); - Meteor.call('sendMessageLivechat', msgObject, (error, result) => { + let agent; + const currentAgent = !visitor.roomSubscribed && Livechat.agent; + if (currentAgent) { + agent = { + _id: currentAgent._id, + username: currentAgent.username + }; + } + + Meteor.call('sendMessageLivechat', msgObject, agent, (error, result) => { if (error) { ChatMessage.update(msgObject._id, { $set: { error: true } }); showError(error.reason); diff --git a/packages/rocketchat-livechat/app/client/lib/triggers.js b/packages/rocketchat-livechat/app/client/lib/triggers.js index e303ef110937..d4ca929e6dea 100644 --- a/packages/rocketchat-livechat/app/client/lib/triggers.js +++ b/packages/rocketchat-livechat/app/client/lib/triggers.js @@ -1,3 +1,41 @@ +/* globals Livechat */ + +function getAgent(triggerAction) { + return new Promise((resolve, reject) => { + const { params } = triggerAction; + if (params.sender === 'queue') { + const cache = localStorage.getItem('triggerAgent'); + if (cache) { + const cacheAgent = JSON.parse(cache); + + // cache valid for 1h + if (cacheAgent.ts && Date.now() - cacheAgent.ts < 3600000) { + return resolve(cacheAgent.agent); + } + } + + Meteor.call('livechat:getNextAgent', { + token: visitor.getToken(), + department: Livechat.department + }, (error, result) => { + if (error) { + return reject(error); + } + localStorage.setItem('triggerAgent', JSON.stringify({ + agent: result, + ts: Date.now() + })); + + resolve(result); + }); + } else if (params.sender === 'custom') { + resolve({ + username: params.name + }); + } + }); +} + this.Triggers = (function() { let triggers = []; let initiated = false; @@ -13,23 +51,27 @@ this.Triggers = (function() { // flag to skip the trigger if the action is 'send-message' trigger.skip = true; - let roomId = visitor.getRoom(); + getAgent(action).then((agent) => { + let roomId = visitor.getRoom(); - if (!roomId) { - roomId = Random.id(); - visitor.setRoom(roomId); - } + if (!roomId) { + roomId = Random.id(); + visitor.setRoom(roomId); + } + + Session.set('triggered', true); + ChatMessage.insert({ + msg: action.params.msg, + rid: roomId, + u: agent + }); - Session.set('triggered', true); - ChatMessage.insert({ - msg: action.params.msg, - rid: roomId, - u: { - username: action.params.name + if (agent._id) { + Livechat.agent = agent; } - }); - parentCall('openWidget'); + parentCall('openWidget'); + }); } }); }; diff --git a/packages/rocketchat-livechat/client/stylesheets/livechat.less b/packages/rocketchat-livechat/client/stylesheets/livechat.less index 6b86af663b38..7d2e34e95075 100644 --- a/packages/rocketchat-livechat/client/stylesheets/livechat.less +++ b/packages/rocketchat-livechat/client/stylesheets/livechat.less @@ -26,9 +26,11 @@ .trigger-value { width: 70%; - input { - display: inline-block !important; + textarea, input, select { + display: block !important; width: auto !important; + margin-bottom: 4px; + min-width: 50%; } } diff --git a/packages/rocketchat-livechat/client/views/app/livechatTriggersForm.js b/packages/rocketchat-livechat/client/views/app/livechatTriggersForm.js index 651b2367a092..f5360e5468b5 100644 --- a/packages/rocketchat-livechat/client/views/app/livechatTriggersForm.js +++ b/packages/rocketchat-livechat/client/views/app/livechatTriggersForm.js @@ -56,12 +56,16 @@ Template.livechatTriggersForm.events({ $('.each-action').each(function() { if ($('.trigger-action', this).val() === 'send-message') { + const params = { + sender: $('[name=send-message-sender]', this).val(), + msg: $('[name=send-message-msg]', this).val() + }; + if (params.sender === 'custom') { + params.name = $('[name=send-message-name]', this).val(); + } data.actions.push({ name: $('.trigger-action', this).val(), - params: { - name: $('[name=send-message-name]', this).val(), - msg: $('[name=send-message-msg]', this).val() - } + params }); } else { data.actions.push({ diff --git a/packages/rocketchat-livechat/client/views/app/triggers/livechatTriggerAction.html b/packages/rocketchat-livechat/client/views/app/triggers/livechatTriggerAction.html index 4a4328b7ae87..9eb80a69147a 100644 --- a/packages/rocketchat-livechat/client/views/app/triggers/livechatTriggerAction.html +++ b/packages/rocketchat-livechat/client/views/app/triggers/livechatTriggerAction.html @@ -7,8 +7,13 @@
- - + + +
diff --git a/packages/rocketchat-livechat/client/views/app/triggers/livechatTriggerAction.js b/packages/rocketchat-livechat/client/views/app/triggers/livechatTriggerAction.js index f47ad1c549e2..355f46f54a3f 100644 --- a/packages/rocketchat-livechat/client/views/app/triggers/livechatTriggerAction.js +++ b/packages/rocketchat-livechat/client/views/app/triggers/livechatTriggerAction.js @@ -6,6 +6,15 @@ Template.livechatTriggerAction.helpers({ } else if (this.name !== current) { return 'hidden'; } + }, + showCustomName() { + return Template.instance().sender.get() === 'custom' ? '' : 'hidden'; + }, + senderSelected(current) { + return this.params && this.params.sender === current ? true : false; + }, + disableIfGuestPool() { + return RocketChat.settings.get('Livechat_Routing_Method') === 'Guest_Pool'; } }); @@ -13,9 +22,19 @@ Template.livechatTriggerAction.events({ 'change .trigger-action'(e, instance) { instance.$('.trigger-action-value ').addClass('hidden'); instance.$(`.${ e.currentTarget.value }`).removeClass('hidden'); + }, + 'change [name=send-message-sender]'(e, instance) { + instance.sender.set(e.currentTarget.value); } }); Template.livechatTriggerAction.onCreated(function() { this.firstAction = true; + + this.sender = new ReactiveVar(''); + + const data = Template.currentData(); + if (data && data.name === 'send-message') { + this.sender.set(data.params.sender); + } }); diff --git a/packages/rocketchat-livechat/package.js b/packages/rocketchat-livechat/package.js index f6312b1e8cb0..5634009a1a3d 100644 --- a/packages/rocketchat-livechat/package.js +++ b/packages/rocketchat-livechat/package.js @@ -145,6 +145,7 @@ Package.onUse(function(api) { api.addFiles('server/methods/getCustomFields.js', 'server'); api.addFiles('server/methods/getAgentData.js', 'server'); api.addFiles('server/methods/getInitialData.js', 'server'); + api.addFiles('server/methods/getNextAgent.js', 'server'); api.addFiles('server/methods/loginByToken.js', 'server'); api.addFiles('server/methods/pageVisited.js', 'server'); api.addFiles('server/methods/registerGuest.js', 'server'); diff --git a/packages/rocketchat-livechat/server/lib/Livechat.js b/packages/rocketchat-livechat/server/lib/Livechat.js index 11d85e9fe84a..6f014d0a9b52 100644 --- a/packages/rocketchat-livechat/server/lib/Livechat.js +++ b/packages/rocketchat-livechat/server/lib/Livechat.js @@ -33,7 +33,24 @@ RocketChat.Livechat = { return RocketChat.models.Users.findOnlineAgents(); } }, - getRoom(guest, message, roomInfo) { + getRequiredDepartment(onlineRequired = true) { + const departments = RocketChat.models.LivechatDepartment.findEnabledWithAgents(); + if (departments.count() > 0) { + return departments.fetch().find((dept) => { + if (dept.showOnRegistration) { + if (onlineRequired) { + const onlineAgents = RocketChat.models.LivechatDepartmentAgents.getOnlineForDepartment(dept._id); + if (onlineAgents.count() > 0) { + return true; + } + } else { + return true; + } + } + }); + } + }, + getRoom(guest, message, roomInfo, agent) { let room = RocketChat.models.Rooms.findOneById(message.rid); let newRoom = false; @@ -44,20 +61,17 @@ RocketChat.Livechat = { if (room == null) { // if no department selected verify if there is at least one active and pick the first - if (!guest.department) { - const departments = RocketChat.models.LivechatDepartment.findEnabledWithAgents(); - if (departments.count() > 0) { - departments.forEach((dept) => { - if (!guest.department && dept.showOnRegistration) { - guest.department = dept._id; - } - }); + if (!agent && !guest.department) { + const department = this.getRequiredDepartment(); + + if (department) { + guest.department = department._id; } } // delegate room creation to QueueMethods const routingMethod = RocketChat.settings.get('Livechat_Routing_Method'); - room = RocketChat.QueueMethods[routingMethod](guest, message, roomInfo); + room = RocketChat.QueueMethods[routingMethod](guest, message, roomInfo, agent); newRoom = true; } else { @@ -69,8 +83,8 @@ RocketChat.Livechat = { return { room, newRoom }; }, - sendMessage({ guest, message, roomInfo }) { - const { room, newRoom } = this.getRoom(guest, message, roomInfo); + sendMessage({ guest, message, roomInfo, agent }) { + const { room, newRoom } = this.getRoom(guest, message, roomInfo, agent); if (guest.name) { message.alias = guest.name; } diff --git a/packages/rocketchat-livechat/server/lib/QueueMethods.js b/packages/rocketchat-livechat/server/lib/QueueMethods.js index 318ce9918f76..849e056b4cc1 100644 --- a/packages/rocketchat-livechat/server/lib/QueueMethods.js +++ b/packages/rocketchat-livechat/server/lib/QueueMethods.js @@ -6,10 +6,12 @@ RocketChat.QueueMethods = { * default method where the agent with the least number * of open chats is paired with the incoming livechat */ - 'Least_Amount'(guest, message, roomInfo) { - const agent = RocketChat.Livechat.getNextAgent(guest.department); + 'Least_Amount'(guest, message, roomInfo, agent) { if (!agent) { - throw new Meteor.Error('no-agent-online', 'Sorry, no online agents'); + agent = RocketChat.Livechat.getNextAgent(guest.department); + if (!agent) { + throw new Meteor.Error('no-agent-online', 'Sorry, no online agents'); + } } const roomCode = RocketChat.models.Rooms.getNextLivechatRoomCode(); diff --git a/packages/rocketchat-livechat/server/methods/getNextAgent.js b/packages/rocketchat-livechat/server/methods/getNextAgent.js new file mode 100644 index 000000000000..a9d09f5c6332 --- /dev/null +++ b/packages/rocketchat-livechat/server/methods/getNextAgent.js @@ -0,0 +1,25 @@ +Meteor.methods({ + 'livechat:getNextAgent'({ token, department }) { + check(token, String); + + const room = RocketChat.models.Rooms.findOpenByVisitorToken(token).fetch(); + + if (room && room.length > 0) { + return; + } + + if (!department) { + const requireDeparment = RocketChat.Livechat.getRequiredDepartment(); + if (requireDeparment) { + department = requireDeparment._id; + } + } + + const agent = RocketChat.Livechat.getNextAgent(department); + if (!agent) { + return; + } + + return RocketChat.models.Users.getAgentInfo(agent.agentId); + } +}); diff --git a/packages/rocketchat-livechat/server/methods/sendMessageLivechat.js b/packages/rocketchat-livechat/server/methods/sendMessageLivechat.js index b34c80019d98..4a0b5e7aa3ba 100644 --- a/packages/rocketchat-livechat/server/methods/sendMessageLivechat.js +++ b/packages/rocketchat-livechat/server/methods/sendMessageLivechat.js @@ -1,5 +1,5 @@ Meteor.methods({ - sendMessageLivechat(message) { + sendMessageLivechat(message, agent) { check(message.rid, String); check(message.token, String); @@ -11,6 +11,19 @@ Meteor.methods({ } }); - return RocketChat.Livechat.sendMessage({ guest, message }); + if (agent && agent.username) { + const onlineAgents = RocketChat.models.Users.findOnlineUserFromList(agent.username); + + if (onlineAgents.count() === 0) { + agent = null; + } else { + agent = { + agentId: agent._id, + username: agent.username + }; + } + } + + return RocketChat.Livechat.sendMessage({ guest, message, agent }); } }); From f8785aeac5f14ee260e5d55c07a44769391d6d03 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 21 Dec 2017 16:38:15 -0200 Subject: [PATCH 2/3] Improve getRequiredDepartment code readability --- .../server/lib/Livechat.js | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/rocketchat-livechat/server/lib/Livechat.js b/packages/rocketchat-livechat/server/lib/Livechat.js index 6f014d0a9b52..299334f0a3ee 100644 --- a/packages/rocketchat-livechat/server/lib/Livechat.js +++ b/packages/rocketchat-livechat/server/lib/Livechat.js @@ -35,20 +35,17 @@ RocketChat.Livechat = { }, getRequiredDepartment(onlineRequired = true) { const departments = RocketChat.models.LivechatDepartment.findEnabledWithAgents(); - if (departments.count() > 0) { - return departments.fetch().find((dept) => { - if (dept.showOnRegistration) { - if (onlineRequired) { - const onlineAgents = RocketChat.models.LivechatDepartmentAgents.getOnlineForDepartment(dept._id); - if (onlineAgents.count() > 0) { - return true; - } - } else { - return true; - } - } - }); - } + + return departments.fetch().find((dept) => { + if (!dept.showOnRegistration) { + return false; + } + if (!onlineRequired) { + return true; + } + const onlineAgents = RocketChat.models.LivechatDepartmentAgents.getOnlineForDepartment(dept._id); + return onlineAgents.count() > 0; + }); }, getRoom(guest, message, roomInfo, agent) { let room = RocketChat.models.Rooms.findOneById(message.rid); From 507cfa4d619788ea270ada9b913b37ec8556ab5b Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Thu, 21 Dec 2017 16:43:57 -0200 Subject: [PATCH 3/3] Rejects if no sender agent for trigger --- packages/rocketchat-livechat/app/client/lib/triggers.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/rocketchat-livechat/app/client/lib/triggers.js b/packages/rocketchat-livechat/app/client/lib/triggers.js index d4ca929e6dea..762fe8ccd103 100644 --- a/packages/rocketchat-livechat/app/client/lib/triggers.js +++ b/packages/rocketchat-livechat/app/client/lib/triggers.js @@ -32,6 +32,8 @@ function getAgent(triggerAction) { resolve({ username: params.name }); + } else { + reject('Unknown sender'); } }); }