Refactor few methods to improve Omnichannel flow (#22321)

pull/22369/head^2
Guilherme Gazzo 4 years ago committed by GitHub
parent 79ee45a02b
commit d70f3158aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      app/livechat/imports/server/rest/upload.js
  2. 16
      app/livechat/server/api/lib/livechat.js
  3. 24
      app/livechat/server/api/v1/config.js
  4. 6
      app/livechat/server/api/v1/room.js
  5. 119
      app/livechat/server/lib/Livechat.js
  6. 4
      app/models/server/models/LivechatDepartment.js
  7. 16
      app/models/server/models/LivechatDepartmentAgents.js
  8. 65
      app/models/server/models/Users.js
  9. 5
      ee/app/livechat-enterprise/server/hooks/onLoadConfigApi.js

@ -3,7 +3,7 @@ import filesize from 'filesize';
import { settings } from '../../../../settings';
import { Settings, LivechatRooms, LivechatVisitors } from '../../../../models';
import { fileUploadIsValidContentType } from '../../../../utils';
import { fileUploadIsValidContentType } from '../../../../utils/server';
import { FileUpload } from '../../../../file-upload';
import { API } from '../../../../api/server';
import { getUploadFormData } from '../../../../api/server/lib/getUploadFormData';

@ -1,8 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import _ from 'underscore';
import { LivechatRooms, LivechatVisitors, LivechatDepartment, LivechatTrigger } from '../../../../models';
import { LivechatRooms, LivechatVisitors, LivechatDepartment, LivechatTrigger, EmojiCustom } from '../../../../models/server';
import { Livechat } from '../../lib/Livechat';
import { callbacks } from '../../../../callbacks/server';
import { normalizeAgent } from '../../lib/Helper';
@ -12,11 +11,13 @@ export function online(department) {
}
export function findTriggers() {
return LivechatTrigger.findEnabled().fetch().map((trigger) => _.pick(trigger, '_id', 'actions', 'conditions', 'runOnce'));
return LivechatTrigger.findEnabled().fetch().map(({ _id, actions, conditions, runOnce }) => ({ _id, actions, conditions, runOnce }));
}
export function findDepartments() {
return LivechatDepartment.findEnabledWithAgents().fetch().map((department) => _.pick(department, '_id', 'name', 'showOnRegistration', 'showOnOfflineForm'));
return LivechatDepartment.findEnabledWithAgents({
_id: 1, name: 1, showOnRegistration: 1, showOnOfflineForm: 1,
}).fetch().map(({ _id, name, showOnRegistration, showOnOfflineForm }) => ({ _id, name, showOnRegistration, showOnOfflineForm }));
}
export function findGuest(token) {
@ -57,13 +58,10 @@ export function findOpenRoom(token, departmentId) {
},
};
let room;
const rooms = departmentId ? LivechatRooms.findOpenByVisitorTokenAndDepartmentId(token, departmentId, options).fetch() : LivechatRooms.findOpenByVisitorToken(token, options).fetch();
if (rooms && rooms.length > 0) {
room = rooms[0];
return rooms[0];
}
return room;
}
export function getRoom({ guest, rid, roomInfo, agent, extraParams }) {
@ -93,7 +91,7 @@ export function settings() {
const triggers = findTriggers();
const departments = findDepartments();
const sound = `${ Meteor.absoluteUrl() }sounds/chime.mp3`;
const emojis = Meteor.call('listEmojiCustom');
const emojis = EmojiCustom.find().fetch();
return {
enabled: initSettings.Livechat_enabled,
settings: {

@ -1,7 +1,8 @@
import { Match, check } from 'meteor/check';
import { API } from '../../../../api/server';
import { findGuest, settings, online, findOpenRoom, getExtraConfigInfo, findAgent } from '../lib/livechat';
import { Livechat } from '../../lib/Livechat';
import { settings, findOpenRoom, getExtraConfigInfo, findAgent } from '../lib/livechat';
API.v1.addRoute('livechat/config', {
get() {
@ -10,28 +11,25 @@ API.v1.addRoute('livechat/config', {
token: Match.Maybe(String),
department: Match.Maybe(String),
});
const enabled = Livechat.enabled();
const config = settings();
if (!config.enabled) {
if (!enabled) {
return API.v1.success({ config: { enabled: false } });
}
const config = settings();
const { token, department } = this.queryParams;
const status = online(department);
const guest = token && findGuest(token);
const status = Livechat.online(department);
const guest = token && Livechat.findGuest(token);
let room;
let agent;
const room = guest && findOpenRoom(token);
const agent = guest && room && room.servedBy && findAgent(room.servedBy._id);
if (guest) {
room = findOpenRoom(token);
agent = room && room.servedBy && findAgent(room.servedBy._id);
}
const extra = Promise.await(getExtraConfigInfo(room));
const { config: extraConfig = {} } = extra || {};
Object.assign(config, { online: status, guest, room, agent }, { ...extraConfig });
return API.v1.success({ config });
return API.v1.success({ config: { ...config, online: status, guest, room, agent, ...extraConfig } });
} catch (e) {
return API.v1.failure(e);
}

@ -36,7 +36,7 @@ API.v1.addRoute('livechat/room', {
const agentObj = agentId && findAgent(agentId);
if (agentObj) {
const { username } = agentObj;
agent = Object.assign({}, { agentId, username });
agent = { agentId, username };
}
const rid = roomId || Random.id();
@ -199,12 +199,12 @@ API.v1.addRoute('livechat/room.visitor', { authRequired: true }, {
throw new Meteor.Error('invalid-visitor');
}
let room = LivechatRooms.findOneById(rid, { _id: 1 });
let room = LivechatRooms.findOneById(rid, { _id: 1 }); // TODO: check _id
if (!room) {
throw new Meteor.Error('invalid-room');
}
const { v: { _id: roomVisitorId } = {} } = room;
const { v: { _id: roomVisitorId } = {} } = room; // TODO: v it will be undefined
if (roomVisitorId !== oldVisitorId) {
throw new Meteor.Error('invalid-room-visitor');
}

@ -13,8 +13,8 @@ import UAParser from 'ua-parser-js';
import { QueueManager } from './QueueManager';
import { RoutingManager } from './RoutingManager';
import { Analytics } from './Analytics';
import { settings } from '../../../settings';
import { callbacks } from '../../../callbacks';
import { settings } from '../../../settings/server';
import { callbacks } from '../../../callbacks/server';
import {
Users,
LivechatRooms,
@ -27,7 +27,7 @@ import {
LivechatCustomField,
LivechatVisitors,
LivechatInquiry,
} from '../../../models';
} from '../../../models/server';
import { Logger } from '../../../logger';
import { addUserRoles, hasPermission, hasRole, removeUserFromRoles, canAccessRoom } from '../../../authorization';
import * as Mailer from '../../../mailer';
@ -50,6 +50,19 @@ export const Livechat = {
},
}),
findGuest(token) {
return LivechatVisitors.getVisitorByToken(token, {
fields: {
name: 1,
username: 1,
token: 1,
visitorEmails: 1,
department: 1,
},
});
},
online(department) {
if (settings.get('Livechat_accept_chats_with_no_agents')) {
return true;
@ -57,13 +70,12 @@ export const Livechat = {
if (settings.get('Livechat_assign_new_conversation_to_bot')) {
const botAgents = Livechat.getBotAgents(department);
if (botAgents && botAgents.count() > 0) {
if (botAgents.count() > 0) {
return true;
}
}
const onlineAgents = Livechat.getOnlineAgents(department);
return onlineAgents && onlineAgents.count() > 0;
return Livechat.checkOnlineAgents(department);
},
getNextAgent(department) {
@ -89,6 +101,18 @@ export const Livechat = {
return Users.findOnlineAgents();
},
checkOnlineAgents(department, agent) {
if (agent?.agentId) {
return Users.checkOnlineAgents(agent.agentId);
}
if (department) {
return LivechatDepartmentAgents.checkOnlineForDepartment(department);
}
return Users.checkOnlineAgents();
},
getBotAgents(department) {
if (department) {
return LivechatDepartmentAgents.getBotsForDepartment(department);
@ -383,8 +407,8 @@ export const Livechat = {
*/
Apps.getBridges().getListenerBridge().livechatEvent(AppEvents.ILivechatRoomClosedHandler, room);
Apps.getBridges().getListenerBridge().livechatEvent(AppEvents.IPostLivechatRoomClosed, room);
callbacks.runAsync('livechat.closeRoom', room);
});
callbacks.runAsync('livechat.closeRoom', room);
return true;
},
@ -427,6 +451,10 @@ export const Livechat = {
return LivechatVisitors.updateLivechatDataByToken(token, key, value, overwrite);
},
enabled() {
return settings.get('Livechat_enabled');
},
getInitSettings() {
const rcSettings = {};
@ -464,9 +492,7 @@ export const Livechat = {
rcSettings[setting._id] = setting.value;
});
settings.get('Livechat_history_monitor_type', (key, value) => {
rcSettings[key] = value;
});
rcSettings.Livechat_history_monitor_type = settings.get('Livechat_history_monitor_type');
rcSettings.Livechat_Show_Connecting = this.showConnecting();
@ -501,8 +527,8 @@ export const Livechat = {
Meteor.defer(() => {
Apps.triggerEvent(AppEvents.IPostLivechatRoomSaved, roomData._id);
callbacks.run('livechat.saveRoom', roomData);
});
callbacks.runAsync('livechat.saveRoom', roomData);
if (!_.isEmpty(guestData.name)) {
const { _id: rid } = roomData;
@ -533,30 +559,31 @@ export const Livechat = {
},
savePageHistory(token, roomId, pageInfo) {
if (pageInfo.change === Livechat.historyMonitorType) {
const user = Users.findOneById('rocket.cat');
const pageTitle = pageInfo.title;
const pageUrl = pageInfo.location.href;
const extraData = {
navigation: {
page: pageInfo,
token,
},
};
if (pageInfo.change !== Livechat.historyMonitorType) {
return;
}
const user = Users.findOneById('rocket.cat');
if (!roomId) {
// keep history of unregistered visitors for 1 month
const keepHistoryMiliseconds = 2592000000;
extraData.expireAt = new Date().getTime() + keepHistoryMiliseconds;
}
const pageTitle = pageInfo.title;
const pageUrl = pageInfo.location.href;
const extraData = {
navigation: {
page: pageInfo,
token,
},
};
if (!settings.get('Livechat_Visitor_navigation_as_a_message')) {
extraData._hidden = true;
}
if (!roomId) {
// keep history of unregistered visitors for 1 month
const keepHistoryMiliseconds = 2592000000;
extraData.expireAt = new Date().getTime() + keepHistoryMiliseconds;
}
return Messages.createNavigationHistoryWithRoomIdMessageAndUser(roomId, `${ pageTitle } - ${ pageUrl }`, user, extraData);
if (!settings.get('Livechat_Visitor_navigation_as_a_message')) {
extraData._hidden = true;
}
return Messages.createNavigationHistoryWithRoomIdMessageAndUser(roomId, `${ pageTitle } - ${ pageUrl }`, user, extraData);
},
saveTransferHistory(room, transferData) {
@ -638,31 +665,27 @@ export const Livechat = {
throw new Meteor.Error('error-returning-inquiry', 'Error returning inquiry to the queue', { method: 'livechat:returnRoomAsInquiry' });
}
Meteor.defer(() => {
callbacks.run('livechat:afterReturnRoomAsInquiry', { room });
});
callbacks.runAsync('livechat:afterReturnRoomAsInquiry', { room });
return true;
},
sendRequest(postData, callback, trying = 1) {
sendRequest(postData, callback, attempts = 10) {
if (!attempts) {
return;
}
const secretToken = settings.get('Livechat_secret_token');
const headers = { headers: { 'X-RocketChat-Livechat-Token': secretToken } };
const options = { data: postData, ...secretToken !== '' && secretToken !== undefined && { headers } };
try {
const options = { data: postData };
const secretToken = settings.get('Livechat_secret_token');
if (secretToken !== '' && secretToken !== undefined) {
Object.assign(options, { headers: { 'X-RocketChat-Livechat-Token': secretToken } });
}
return HTTP.post(settings.get('Livechat_webhookUrl'), options);
} catch (e) {
Livechat.logger.webhook.error(`Response error on ${ trying } try ->`, e);
Livechat.logger.webhook.error(`Response error on ${ 11 - attempts } try ->`, e);
// try 10 times after 10 seconds each
if (trying < 10) {
Livechat.logger.webhook.warn('Will try again in 10 seconds ...');
trying++;
setTimeout(Meteor.bindEnvironment(() => {
Livechat.sendRequest(postData, callback, trying);
}), 10000);
}
Livechat.logger.webhook.warn('Will try again in 10 seconds ...');
setTimeout(Meteor.bindEnvironment(function() {
Livechat.sendRequest(postData, callback, attempts--);
}), 10000);
}
},

@ -92,12 +92,12 @@ export class LivechatDepartment extends Base {
return this.remove(query);
}
findEnabledWithAgents() {
findEnabledWithAgents(fields = null) {
const query = {
numAgents: { $gt: 0 },
enabled: true,
};
return this.find(query);
return this.find(query, fields && { fields });
}
findOneByIdOrName(_idOrName, options) {

@ -85,9 +85,8 @@ export class LivechatDepartmentAgents extends Base {
};
const collectionObj = this.model.rawCollection();
const findAndModify = Meteor.wrapAsync(collectionObj.findAndModify, collectionObj);
const agent = findAndModify(query, sort, update);
const agent = Promise.await(collectionObj.findAndModify(query, sort, update));
if (agent && agent.value) {
return {
agentId: agent.value.agentId,
@ -97,6 +96,19 @@ export class LivechatDepartmentAgents extends Base {
return null;
}
checkOnlineForDepartment(departmentId) {
const agents = this.findByDepartmentId(departmentId).fetch();
if (agents.length === 0) {
return false;
}
const onlineUser = Users.findOneOnlineAgentByUsername(_.pluck(agents, 'username'));
return Boolean(onlineUser);
}
getOnlineForDepartment(departmentId) {
const agents = this.findByDepartmentId(departmentId).fetch();

@ -8,23 +8,16 @@ import { Base } from './_Base';
import Subscriptions from './Subscriptions';
import { settings } from '../../../settings/server/functions/settings';
const queryStatusAgentOnline = (extraFilters = {}) => {
if (settings.get('Livechat_enabled_when_agent_idle') === false) {
extraFilters = Object.assign(extraFilters, { statusConnection: { $ne: 'away' } });
}
const query = {
status: {
$exists: true,
$ne: 'offline',
},
statusLivechat: 'available',
roles: 'livechat-agent',
...extraFilters,
};
return query;
};
const queryStatusAgentOnline = (extraFilters = {}) => ({
status: {
$exists: true,
$ne: 'offline',
},
statusLivechat: 'available',
roles: 'livechat-agent',
...extraFilters,
...settings.get('Livechat_enabled_when_agent_idle') === false && { statusConnection: { $ne: 'away' } },
});
export class Users extends Base {
constructor(...args) {
super(...args);
@ -95,7 +88,7 @@ export class Users extends Base {
return this.findOne(query);
}
setOperator(_id, operator) {
setOperator(_id, operator) { // TODO:: Create class Agent
const update = {
$set: {
operator,
@ -105,13 +98,19 @@ export class Users extends Base {
return this.update(_id, update);
}
findOnlineAgents(agentId) {
checkOnlineAgents(agentId) { // TODO:: Create class Agent
const query = queryStatusAgentOnline(agentId && { _id: agentId });
return Boolean(this.findOne(query));
}
findOnlineAgents(agentId) { // TODO:: Create class Agent
const query = queryStatusAgentOnline(agentId && { _id: agentId });
return this.find(query);
}
findBotAgents(usernameList) {
findBotAgents(usernameList) { // TODO:: Create class Agent
const query = {
roles: {
$all: ['bot', 'livechat-agent'],
@ -126,7 +125,7 @@ export class Users extends Base {
return this.find(query);
}
findOneBotAgent() {
findOneBotAgent() { // TODO:: Create class Agent
const query = {
roles: {
$all: ['bot', 'livechat-agent'],
@ -136,19 +135,19 @@ export class Users extends Base {
return this.findOne(query);
}
findOneOnlineAgentByUsername(username, options) {
findOneOnlineAgentByUsername(username, options) { // TODO:: Create class Agent
const query = queryStatusAgentOnline({ username });
return this.findOne(query, options);
}
findOneOnlineAgentById(_id) {
findOneOnlineAgentById(_id) { // TODO: Create class Agent
const query = queryStatusAgentOnline({ _id });
return this.findOne(query);
}
findOneAgentById(_id, options) {
findOneAgentById(_id, options) { // TODO: Create class Agent
const query = {
_id,
roles: 'livechat-agent',
@ -157,7 +156,7 @@ export class Users extends Base {
return this.findOne(query, options);
}
findAgents() {
findAgents() { // TODO: Create class Agent
const query = {
roles: 'livechat-agent',
};
@ -165,7 +164,7 @@ export class Users extends Base {
return this.find(query);
}
findOnlineUserFromList(userList) {
findOnlineUserFromList(userList) { // TODO: Create class Agent
const username = {
$in: [].concat(userList),
};
@ -175,7 +174,7 @@ export class Users extends Base {
return this.find(query);
}
getNextAgent(ignoreAgentId) {
getNextAgent(ignoreAgentId) { // TODO: Create class Agent
const extraFilters = {
...ignoreAgentId && { _id: { $ne: ignoreAgentId } },
};
@ -205,7 +204,7 @@ export class Users extends Base {
return null;
}
getNextBotAgent(ignoreAgentId) {
getNextBotAgent(ignoreAgentId) { // TODO: Create class Agent
const query = {
roles: {
$all: ['bot', 'livechat-agent'],
@ -237,7 +236,7 @@ export class Users extends Base {
return null;
}
setLivechatStatus(userId, status) {
setLivechatStatus(userId, status) { // TODO: Create class Agent
const query = {
_id: userId,
};
@ -251,7 +250,7 @@ export class Users extends Base {
return this.update(query, update);
}
setLivechatData(userId, data = {}) {
setLivechatData(userId, data = {}) { // TODO: Create class Agent
const query = {
_id: userId,
};
@ -265,15 +264,15 @@ export class Users extends Base {
return this.update(query, update);
}
closeOffice() {
closeOffice() { // TODO: Create class Agent
this.findAgents().forEach((agent) => this.setLivechatStatus(agent._id, 'not-available'));
}
openOffice() {
openOffice() { // TODO: Create class Agent
this.findAgents().forEach((agent) => this.setLivechatStatus(agent._id, 'available'));
}
getAgentInfo(agentId) {
getAgentInfo(agentId) { // TODO: Create class Agent
const query = {
_id: agentId,
};

@ -7,10 +7,9 @@ callbacks.add('livechat.onLoadConfigApi', async (options = {}) => {
const queueInfo = await getLivechatQueueInfo(room);
const customFields = getLivechatCustomFields();
const config = {
return {
...queueInfo && { queueInfo },
...customFields && { customFields },
...options,
};
return Object.assign({ config }, options);
}, callbacks.priority.MEDIUM, 'livechat-on-load-config-api');

Loading…
Cancel
Save