refactor: Move units check outside of model for finds (#29253)

pull/29455/head^2
Kevin Aleman 3 years ago committed by GitHub
parent 956dbb5520
commit 7832a40a6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .changeset/breezy-drinks-switch.md
  2. 7
      apps/meteor/app/apps/server/bridges/livechat.ts
  3. 8
      apps/meteor/app/lib/server/functions/closeOmnichannelConversations.ts
  4. 5
      apps/meteor/app/livechat/server/api/lib/livechat.ts
  5. 4
      apps/meteor/app/livechat/server/api/lib/rooms.ts
  6. 16
      apps/meteor/app/livechat/server/api/lib/visitors.ts
  7. 4
      apps/meteor/app/livechat/server/api/v1/message.ts
  8. 51
      apps/meteor/app/livechat/server/api/v1/visitor.ts
  9. 88
      apps/meteor/app/livechat/server/lib/Analytics.js
  10. 4
      apps/meteor/app/livechat/server/lib/Contacts.ts
  11. 6
      apps/meteor/app/livechat/server/lib/Livechat.js
  12. 4
      apps/meteor/app/livechat/server/methods/getNextAgent.ts
  13. 4
      apps/meteor/app/livechat/server/methods/registerGuest.ts
  14. 4
      apps/meteor/app/livechat/server/methods/removeAllClosedRooms.ts
  15. 17
      apps/meteor/ee/app/livechat-enterprise/server/hooks/applyDepartmentRestrictions.ts
  16. 33
      apps/meteor/ee/app/livechat-enterprise/server/hooks/applyRoomRestrictions.ts
  17. 1
      apps/meteor/ee/app/livechat-enterprise/server/hooks/index.ts
  18. 7
      apps/meteor/ee/app/livechat-enterprise/server/lib/Helper.ts
  19. 5
      apps/meteor/ee/app/livechat-enterprise/server/lib/SlaHelper.ts
  20. 4
      apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts
  21. 15
      apps/meteor/ee/app/livechat-enterprise/server/lib/query.helper.js
  22. 13
      apps/meteor/ee/server/lib/audit/methods.ts
  23. 67
      apps/meteor/ee/server/models/raw/LivechatRooms.ts
  24. 1
      apps/meteor/lib/callbacks.ts
  25. 54
      apps/meteor/server/models/raw/LivechatRooms.ts
  26. 41
      packages/model-typings/src/models/ILivechatRoomsModel.ts

@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/model-typings": patch
---
refactor: Move units check outside of model for finds

@ -18,6 +18,7 @@ import { getRoom } from '../../../livechat/server/api/lib/livechat';
import { Livechat } from '../../../livechat/server/lib/Livechat';
import type { AppServerOrchestrator } from '../../../../ee/server/apps/orchestrator';
import { Livechat as LivechatTyped } from '../../../livechat/server/lib/LivechatTyped';
import { callbacks } from '../../../../lib/callbacks';
import { deasyncPromise } from '../../../../server/deasync/deasync';
export class AppLivechatBridge extends LivechatBridge {
@ -143,10 +144,12 @@ export class AppLivechatBridge extends LivechatBridge {
let result;
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
if (departmentId) {
result = await LivechatRooms.findOpenByVisitorTokenAndDepartmentId(visitor.token, departmentId, {}).toArray();
result = await LivechatRooms.findOpenByVisitorTokenAndDepartmentId(visitor.token, departmentId, {}, extraQuery).toArray();
} else {
result = await LivechatRooms.findOpenByVisitorToken(visitor.token, {}).toArray();
result = await LivechatRooms.findOpenByVisitorToken(visitor.token, {}, extraQuery).toArray();
}
return Promise.all((result as unknown as ILivechatRoom[]).map((room) => this.orch.getConverters()?.get('rooms').convertRoom(room)));

@ -4,6 +4,7 @@ import { LivechatRooms } from '@rocket.chat/models';
import { settings } from '../../../settings/server';
import { Livechat } from '../../../livechat/server/lib/LivechatTyped';
import { i18n } from '../../../../server/lib/i18n';
import { callbacks } from '../../../../lib/callbacks';
type SubscribedRooms = {
rid: string;
@ -11,7 +12,12 @@ type SubscribedRooms = {
};
export const closeOmnichannelConversations = async (user: IUser, subscribedRooms: SubscribedRooms[]): Promise<void> => {
const roomsInfo = await LivechatRooms.findByIds(subscribedRooms.map(({ rid }) => rid));
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const roomsInfo = await LivechatRooms.findByIds(
subscribedRooms.map(({ rid }) => rid),
{},
extraQuery,
);
const language = settings.get<string>('Language') || 'en';
const comment = i18n.t('Agent_deactivated', { lng: language });

@ -90,9 +90,10 @@ export async function findOpenRoom(token: string, departmentId?: string): Promis
},
};
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const rooms = departmentId
? await LivechatRooms.findOpenByVisitorTokenAndDepartmentId(token, departmentId, options).toArray()
: await LivechatRooms.findOpenByVisitorToken(token, options).toArray();
? await LivechatRooms.findOpenByVisitorTokenAndDepartmentId(token, departmentId, options, extraQuery).toArray()
: await LivechatRooms.findOpenByVisitorToken(token, options, extraQuery).toArray();
if (rooms && rooms.length > 0) {
return rooms[0];
}

@ -2,6 +2,8 @@ import type { ILivechatDepartment, IOmnichannelRoom } from '@rocket.chat/core-ty
import { LivechatRooms, LivechatDepartment } from '@rocket.chat/models';
import type { PaginatedResult } from '@rocket.chat/rest-typings';
import { callbacks } from '../../../../../lib/callbacks';
export async function findRooms({
agents,
roomName,
@ -31,6 +33,7 @@ export async function findRooms({
onhold?: string | boolean;
options: { offset: number; count: number; fields: Record<string, number>; sort: Record<string, number> };
}): Promise<PaginatedResult<{ rooms: Array<IOmnichannelRoom> }>> {
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const { cursor, totalCount } = LivechatRooms.findRoomsWithCriteria({
agents,
roomName,
@ -47,6 +50,7 @@ export async function findRooms({
count,
fields,
},
extraQuery,
});
const [rooms, total] = await Promise.all([cursor.toArray(), totalCount]);

@ -3,6 +3,7 @@ import { LivechatVisitors, Messages, LivechatRooms, LivechatCustomField } from '
import type { FindOptions } from 'mongodb';
import { canAccessRoomAsync } from '../../../../authorization/server/functions/canAccessRoom';
import { callbacks } from '../../../../../lib/callbacks';
export async function findVisitorInfo({ visitorId }: { visitorId: IVisitor['_id'] }) {
const visitor = await LivechatVisitors.findOneById(visitorId);
@ -61,11 +62,16 @@ export async function findChatHistory({
throw new Error('error-not-allowed');
}
const { cursor, totalCount } = LivechatRooms.findPaginatedByVisitorId(visitorId, {
sort: sort || { ts: -1 },
skip: offset,
limit: count,
});
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const { cursor, totalCount } = LivechatRooms.findPaginatedByVisitorId(
visitorId,
{
sort: sort || { ts: -1 },
skip: offset,
limit: count,
},
extraQuery,
);
const [history, total] = await Promise.all([cursor.toArray(), totalCount]);

@ -19,6 +19,7 @@ import { normalizeMessageFileUpload } from '../../../../utils/server/functions/n
import { settings } from '../../../../settings/server';
import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems';
import { isWidget } from '../../../../api/server/helpers/isWidget';
import { callbacks } from '../../../../../lib/callbacks';
API.v1.addRoute(
'livechat/message',
@ -254,7 +255,8 @@ API.v1.addRoute(
let visitor = await LivechatVisitors.getVisitorByToken(visitorToken, {});
let rid: string;
if (visitor) {
const rooms = await LivechatRooms.findOpenByVisitorToken(visitorToken).toArray();
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const rooms = await LivechatRooms.findOpenByVisitorToken(visitorToken, {}, extraQuery).toArray();
if (rooms && rooms.length > 0) {
rid = rooms[0]._id;
} else {

@ -8,6 +8,7 @@ import { findGuest, normalizeHttpHeaderData } from '../lib/livechat';
import { Livechat } from '../../lib/Livechat';
import { Livechat as LivechatTyped } from '../../lib/LivechatTyped';
import { settings } from '../../../../settings/server';
import { callbacks } from '../../../../../lib/callbacks';
API.v1.addRoute('livechat/visitor', {
async post() {
@ -46,8 +47,9 @@ API.v1.addRoute('livechat/visitor', {
let visitor = await VisitorsRaw.findOneById(visitorId, {});
if (visitor) {
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
// If it's updating an existing visitor, it must also update the roomInfo
const rooms = await LivechatRooms.findOpenByVisitorToken(visitor?.token).toArray();
const rooms = await LivechatRooms.findOpenByVisitorToken(visitor?.token, {}, extraQuery).toArray();
await Promise.all(rooms.map((room: IRoom) => Livechat.saveRoomInfo(room, visitor)));
}
@ -97,17 +99,21 @@ API.v1.addRoute('livechat/visitor/:token', {
if (!visitor) {
throw new Meteor.Error('invalid-token');
}
const rooms = await LivechatRooms.findOpenByVisitorToken(this.urlParams.token, {
projection: {
name: 1,
t: 1,
cl: 1,
u: 1,
usernames: 1,
servedBy: 1,
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const rooms = await LivechatRooms.findOpenByVisitorToken(
this.urlParams.token,
{
projection: {
name: 1,
t: 1,
cl: 1,
u: 1,
usernames: 1,
servedBy: 1,
},
},
}).toArray();
extraQuery,
).toArray();
// if gdpr is enabled, bypass rooms check
if (rooms?.length && !settings.get('Livechat_Allow_collect_and_store_HTTP_header_informations')) {
@ -134,16 +140,21 @@ API.v1.addRoute(
{ authRequired: true, permissionsRequired: ['view-livechat-manager'] },
{
async get() {
const rooms = await LivechatRooms.findOpenByVisitorToken(this.urlParams.token, {
projection: {
name: 1,
t: 1,
cl: 1,
u: 1,
usernames: 1,
servedBy: 1,
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const rooms = await LivechatRooms.findOpenByVisitorToken(
this.urlParams.token,
{
projection: {
name: 1,
t: 1,
cl: 1,
u: 1,
usernames: 1,
servedBy: 1,
},
},
}).toArray();
extraQuery,
).toArray();
return API.v1.success({ rooms });
},
},

@ -5,6 +5,7 @@ import { secondsToHHMMSS } from '../../../utils/server';
import { getTimezone } from '../../../utils/server/lib/getTimezone';
import { Logger } from '../../../logger/server';
import { i18n } from '../../../../server/lib/i18n';
import { callbacks } from '../../../../lib/callbacks';
const HOURS_IN_DAY = 24;
const logger = new Logger('OmnichannelAnalytics');
@ -54,7 +55,8 @@ export const Analytics = {
return;
}
return this.AgentOverviewData[name](from, to, departmentId);
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
return this.AgentOverviewData[name](from, to, departmentId, extraQuery);
},
async getAnalyticsChartData(options) {
@ -90,6 +92,7 @@ export const Analytics = {
dataPoints: [],
};
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
if (isSameDay) {
// data for single day
const m = moment(from);
@ -106,7 +109,7 @@ export const Analytics = {
lt: moment(m).add(1, 'hours'),
};
data.dataPoints.push(await this.ChartData[name](date, departmentId));
data.dataPoints.push(await this.ChartData[name](date, departmentId, extraQuery));
}
} else {
for await (const m of dayIterator(from, to)) {
@ -117,7 +120,7 @@ export const Analytics = {
lt: moment(m).add(1, 'days'),
};
data.dataPoints.push(await this.ChartData[name](date, departmentId));
data.dataPoints.push(await this.ChartData[name](date, departmentId, extraQuery));
}
}
@ -144,7 +147,8 @@ export const Analytics = {
const t = (s) => i18n.t(s, { lng: language });
return this.OverviewData[name](from, to, departmentId, timezone, t);
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
return this.OverviewData[name](from, to, departmentId, timezone, t, extraQuery);
},
ChartData: {
@ -154,15 +158,15 @@ export const Analytics = {
*
* @returns {Integer}
*/
Total_conversations(date, departmentId) {
return LivechatRooms.getTotalConversationsBetweenDate('l', date, { departmentId });
Total_conversations(date, departmentId, extraQuery) {
return LivechatRooms.getTotalConversationsBetweenDate('l', date, { departmentId }, extraQuery);
},
async Avg_chat_duration(date, departmentId) {
async Avg_chat_duration(date, departmentId, extraQuery) {
let total = 0;
let count = 0;
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics }) => {
if (metrics && metrics.chatDuration) {
total += metrics.chatDuration;
count++;
@ -173,7 +177,7 @@ export const Analytics = {
return Math.round(avgCD * 100) / 100;
},
async Total_messages(date, departmentId) {
async Total_messages(date, departmentId, extraQuery) {
let total = 0;
// we don't want to count visitor messages
@ -183,6 +187,7 @@ export const Analytics = {
date,
{ departmentId },
extraFilter,
extraQuery,
).toArray();
allConversations.map(({ msgs }) => {
if (msgs) {
@ -200,10 +205,10 @@ export const Analytics = {
*
* @returns {Double}
*/
async Avg_first_response_time(date, departmentId) {
async Avg_first_response_time(date, departmentId, extraQuery) {
let frt = 0;
let count = 0;
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics }) => {
if (metrics && metrics.response && metrics.response.ft) {
frt += metrics.response.ft;
count++;
@ -220,10 +225,10 @@ export const Analytics = {
*
* @returns {Double}
*/
async Best_first_response_time(date, departmentId) {
async Best_first_response_time(date, departmentId, extraQuery) {
let maxFrt;
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics }) => {
if (metrics && metrics.response && metrics.response.ft) {
maxFrt = maxFrt ? Math.min(maxFrt, metrics.response.ft) : metrics.response.ft;
}
@ -242,10 +247,10 @@ export const Analytics = {
*
* @returns {Double}
*/
async Avg_response_time(date, departmentId) {
async Avg_response_time(date, departmentId, extraQuery) {
let art = 0;
let count = 0;
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics }) => {
if (metrics && metrics.response && metrics.response.avg) {
art += metrics.response.avg;
count++;
@ -263,10 +268,10 @@ export const Analytics = {
*
* @returns {Double}
*/
async Avg_reaction_time(date, departmentId) {
async Avg_reaction_time(date, departmentId, extraQuery) {
let arnt = 0;
let count = 0;
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics }) => {
if (metrics && metrics.reaction && metrics.reaction.ft) {
arnt += metrics.reaction.ft;
count++;
@ -307,7 +312,7 @@ export const Analytics = {
*
* @returns {Array[Object]}
*/
async Conversations(from, to, departmentId, timezone, t = (v) => v) {
async Conversations(from, to, departmentId, timezone, t = (v) => v, extraQuery) {
// TODO: most calls to db here can be done in one single call instead of one per day/hour
let totalConversations = 0; // Total conversations
let openConversations = 0; // open conversations
@ -337,7 +342,7 @@ export const Analytics = {
lt: m.add(1, 'days'),
};
// eslint-disable-next-line no-await-in-loop
const result = await LivechatRooms.getAnalyticsBetweenDate(date, { departmentId }).toArray();
const result = await LivechatRooms.getAnalyticsBetweenDate(date, { departmentId }, extraQuery).toArray();
totalConversations += result.length;
result.forEach(summarize(clonedDate));
@ -359,7 +364,7 @@ export const Analytics = {
gte: h.clone(),
lt: h.add(1, 'hours'),
};
(await LivechatRooms.getAnalyticsBetweenDate(date, { departmentId }).toArray()).forEach(({ msgs }) => {
(await LivechatRooms.getAnalyticsBetweenDate(date, { departmentId }, extraQuery).toArray()).forEach(({ msgs }) => {
const dayHour = h.format('H'); // @int : 0, 1, ... 23
totalMessagesInHour.set(dayHour, totalMessagesInHour.has(dayHour) ? totalMessagesInHour.get(dayHour) + msgs : msgs);
});
@ -371,7 +376,7 @@ export const Analytics = {
to: utcBusiestHour >= 0 ? moment.utc().set({ hour: utcBusiestHour }).tz(timezone).format('hA') : '-',
from: utcBusiestHour >= 0 ? moment.utc().set({ hour: utcBusiestHour }).subtract(1, 'hour').tz(timezone).format('hA') : '',
};
const onHoldConversations = await LivechatRooms.getOnHoldConversationsBetweenDate(from, to, departmentId);
const onHoldConversations = await LivechatRooms.getOnHoldConversationsBetweenDate(from, to, departmentId, extraQuery);
return [
{
@ -412,7 +417,7 @@ export const Analytics = {
*
* @returns {Array[Object]}
*/
async Productivity(from, to, departmentId) {
async Productivity(from, to, departmentId, extraQuery) {
let avgResponseTime = 0;
let firstResponseTime = 0;
let avgReactionTime = 0;
@ -423,7 +428,7 @@ export const Analytics = {
lt: to.add(1, 'days'),
};
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics }) => {
if (metrics && metrics.response && metrics.reaction) {
avgResponseTime += metrics.response.avg;
firstResponseTime += metrics.response.ft;
@ -491,7 +496,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
async Total_conversations(from, to, departmentId) {
async Total_conversations(from, to, departmentId, extraQuery) {
let total = 0;
const agentConversations = new Map(); // stores total conversations for each agent
const date = {
@ -511,9 +516,15 @@ export const Analytics = {
data: [],
};
const allConversations = await LivechatRooms.getAnalyticsMetricsBetweenDateWithMessages('l', date, {
departmentId,
}).toArray();
const allConversations = await LivechatRooms.getAnalyticsMetricsBetweenDateWithMessages(
'l',
date,
{
departmentId,
},
{},
extraQuery,
).toArray();
allConversations.map((room) => {
if (room.servedBy) {
this.updateMap(agentConversations, room.servedBy.username, 1);
@ -548,7 +559,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
async Avg_chat_duration(from, to, departmentId) {
async Avg_chat_duration(from, to, departmentId, extraQuery) {
const agentChatDurations = new Map(); // stores total conversations for each agent
const date = {
gte: from,
@ -567,7 +578,7 @@ export const Analytics = {
data: [],
};
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics, servedBy }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics, servedBy }) => {
if (servedBy && metrics && metrics.chatDuration) {
if (agentChatDurations.has(servedBy.username)) {
agentChatDurations.set(servedBy.username, {
@ -609,7 +620,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
async Total_messages(from, to, departmentId) {
async Total_messages(from, to, departmentId, extraQuery) {
const agentMessages = new Map(); // stores total conversations for each agent
const date = {
gte: from,
@ -635,6 +646,7 @@ export const Analytics = {
date,
{ departmentId },
extraFilter,
extraQuery,
).toArray();
allConversations.map(({ servedBy, msgs }) => {
if (servedBy) {
@ -663,7 +675,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
async Avg_first_response_time(from, to, departmentId) {
async Avg_first_response_time(from, to, departmentId, extraQuery) {
const agentAvgRespTime = new Map(); // stores avg response time for each agent
const date = {
gte: from,
@ -682,7 +694,7 @@ export const Analytics = {
data: [],
};
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics, servedBy }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics, servedBy }) => {
if (servedBy && metrics && metrics.response && metrics.response.ft) {
if (agentAvgRespTime.has(servedBy.username)) {
agentAvgRespTime.set(servedBy.username, {
@ -724,7 +736,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
async Best_first_response_time(from, to, departmentId) {
async Best_first_response_time(from, to, departmentId, extraQuery) {
const agentFirstRespTime = new Map(); // stores avg response time for each agent
const date = {
gte: from,
@ -743,7 +755,7 @@ export const Analytics = {
data: [],
};
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics, servedBy }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics, servedBy }) => {
if (servedBy && metrics && metrics.response && metrics.response.ft) {
if (agentFirstRespTime.has(servedBy.username)) {
agentFirstRespTime.set(servedBy.username, Math.min(agentFirstRespTime.get(servedBy.username), metrics.response.ft));
@ -777,7 +789,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
async Avg_response_time(from, to, departmentId) {
async Avg_response_time(from, to, departmentId, extraQuery) {
const agentAvgRespTime = new Map(); // stores avg response time for each agent
const date = {
gte: from,
@ -796,7 +808,7 @@ export const Analytics = {
data: [],
};
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics, servedBy }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics, servedBy }) => {
if (servedBy && metrics && metrics.response && metrics.response.avg) {
if (agentAvgRespTime.has(servedBy.username)) {
agentAvgRespTime.set(servedBy.username, {
@ -838,7 +850,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
async Avg_reaction_time(from, to, departmentId) {
async Avg_reaction_time(from, to, departmentId, extraQuery) {
const agentAvgReactionTime = new Map(); // stores avg reaction time for each agent
const date = {
gte: from,
@ -857,7 +869,7 @@ export const Analytics = {
data: [],
};
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics, servedBy }) => {
await LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }, extraQuery).forEach(({ metrics, servedBy }) => {
if (servedBy && metrics && metrics.reaction && metrics.reaction.ft) {
if (agentAvgReactionTime.has(servedBy.username)) {
agentAvgReactionTime.set(servedBy.username, {

@ -4,6 +4,7 @@ import type { MatchKeysAndValues, OnlyFieldsOfType } from 'mongodb';
import { LivechatVisitors, Users, LivechatRooms, LivechatCustomField, LivechatInquiry, Rooms, Subscriptions } from '@rocket.chat/models';
import type { ILivechatCustomField, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings';
import { callbacks } from '../../../../lib/callbacks';
import { trim } from '../../../../lib/utils/stringUtils';
import { i18n } from '../../../utils/lib/i18n';
@ -125,7 +126,8 @@ export const Contacts = {
await LivechatVisitors.updateOne({ _id: contactId }, updateUser);
const rooms: IOmnichannelRoom[] = await LivechatRooms.findByVisitorId(contactId, {}).toArray();
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const rooms: IOmnichannelRoom[] = await LivechatRooms.findByVisitorId(contactId, {}, extraQuery).toArray();
if (rooms?.length) {
for await (const room of rooms) {

@ -285,7 +285,8 @@ export const Livechat = {
Livechat.logger.debug(`Closing open chats for user ${userId}`);
const user = await Users.findOneById(userId);
const openChats = LivechatRooms.findOpenByAgent(userId);
const extraQuery = await callbacks.run('livechat.applyDepartmentRestrictions', {});
const openChats = LivechatRooms.findOpenByAgent(userId, extraQuery);
const promises = [];
await openChats.forEach((room) => {
promises.push(LivechatTyped.closeRoom({ user, room, comment }));
@ -635,7 +636,8 @@ export const Livechat = {
const { token } = guest;
check(token, String);
const cursor = LivechatRooms.findByVisitorToken(token);
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const cursor = LivechatRooms.findByVisitorToken(token, extraQuery);
for await (const room of cursor) {
await FileUpload.removeFilesByRoomId(room._id);
await Messages.removeByRoomId(room._id);

@ -6,6 +6,7 @@ import type { ILivechatAgent } from '@rocket.chat/core-typings';
import { Livechat } from '../lib/LivechatTyped';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { callbacks } from '../../../../lib/callbacks';
declare module '@rocket.chat/ui-contexts' {
// eslint-disable-next-line @typescript-eslint/naming-convention
@ -22,7 +23,8 @@ Meteor.methods<ServerMethods>({
methodDeprecationLogger.method('livechat:getNextAgent', '7.0.0');
check(token, String);
const room = await LivechatRooms.findOpenByVisitorToken(token).toArray();
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const room = await LivechatRooms.findOpenByVisitorToken(token, {}, extraQuery).toArray();
if (room && room.length > 0) {
return;

@ -6,6 +6,7 @@ import type { ILivechatVisitor, IRoom } from '@rocket.chat/core-typings';
import { Livechat } from '../lib/Livechat';
import { Livechat as LivechatTyped } from '../lib/LivechatTyped';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { callbacks } from '../../../../lib/callbacks';
declare module '@rocket.chat/ui-contexts' {
// eslint-disable-next-line @typescript-eslint/naming-convention
@ -57,8 +58,9 @@ Meteor.methods<ServerMethods>({
},
});
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
// If it's updating an existing visitor, it must also update the roomInfo
const rooms: IRoom[] = await LivechatRooms.findOpenByVisitorToken(token).toArray();
const rooms: IRoom[] = await LivechatRooms.findOpenByVisitorToken(token, {}, extraQuery).toArray();
await Promise.all(rooms.map((room) => Livechat.saveRoomInfo(room, visitor)));
if (customFields && customFields instanceof Array) {

@ -5,6 +5,7 @@ import { LivechatRooms } from '@rocket.chat/models';
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { Livechat } from '../lib/LivechatTyped';
import { callbacks } from '../../../../lib/callbacks';
declare module '@rocket.chat/ui-contexts' {
// eslint-disable-next-line @typescript-eslint/naming-convention
@ -26,8 +27,9 @@ Meteor.methods<ServerMethods>({
// These are not debug logs since we want to know when the action is performed
Livechat.logger.info(`User ${Meteor.userId()} is removing all closed rooms`);
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const promises: Promise<void>[] = [];
await LivechatRooms.findClosedRooms(departmentIds).forEach(({ _id }: IOmnichannelRoom) => {
await LivechatRooms.findClosedRooms(departmentIds, {}, extraQuery).forEach(({ _id }: IOmnichannelRoom) => {
promises.push(Livechat.removeRoom(_id));
});
await Promise.all(promises);

@ -1,14 +1,25 @@
import type { FilterOperators } from 'mongodb';
import type { ILivechatDepartmentRecord } from '@rocket.chat/core-typings';
import type { ILivechatDepartment } from '@rocket.chat/core-typings';
import { callbacks } from '../../../../../lib/callbacks';
import { addQueryRestrictionsToDepartmentsModel } from '../lib/query.helper';
import { hasRoleAsync } from '../../../../../app/authorization/server/functions/hasRole';
import { cbLogger } from '../lib/logger';
import { getUnitsFromUser } from '../lib/units';
export const addQueryRestrictionsToDepartmentsModel = async (originalQuery: FilterOperators<ILivechatDepartment> = {}) => {
const query: FilterOperators<ILivechatDepartment> = { ...originalQuery, type: { $ne: 'u' } };
const units = await getUnitsFromUser();
if (Array.isArray(units)) {
query.ancestors = { $in: units };
}
return query;
};
callbacks.add(
'livechat.applyDepartmentRestrictions',
async (originalQuery: FilterOperators<ILivechatDepartmentRecord> = {}, { userId }: { userId?: string | null } = { userId: null }) => {
async (originalQuery: FilterOperators<ILivechatDepartment> = {}, { userId }: { userId?: string | null } = { userId: null }) => {
if (!userId || !(await hasRoleAsync(userId, 'livechat-monitor'))) {
cbLogger.debug('Skipping callback. No user id provided or user is not a monitor');
return originalQuery;

@ -0,0 +1,33 @@
import type { FilterOperators } from 'mongodb';
import type { IOmnichannelRoom } from '@rocket.chat/core-typings';
import { callbacks } from '../../../../../lib/callbacks';
import { cbLogger } from '../lib/logger';
import { getUnitsFromUser } from '../lib/units';
export const restrictQuery = async (originalQuery: FilterOperators<IOmnichannelRoom> = {}) => {
const query = { ...originalQuery };
const units = await getUnitsFromUser();
if (!Array.isArray(units)) {
return query;
}
const expressions = query.$and || [];
const condition = {
$or: [{ departmentAncestors: { $in: units } }, { departmentId: { $in: units } }],
};
query.$and = [condition, ...expressions];
return query;
};
callbacks.add(
'livechat.applyRoomRestrictions',
async (originalQuery: FilterOperators<IOmnichannelRoom> = {}) => {
cbLogger.debug('Applying room query restrictions');
return restrictQuery(originalQuery);
},
callbacks.priority.HIGH,
'livechat-apply-room-restrictions',
);

@ -24,3 +24,4 @@ import './afterForwardChatToAgent';
import './applySimultaneousChatsRestrictions';
import './afterInquiryQueued';
import './sendPdfTranscriptOnClose';
import './applyRoomRestrictions';

@ -19,6 +19,7 @@ import { logger, helperLogger } from './logger';
import { OmnichannelQueueInactivityMonitor } from './QueueInactivityMonitor';
import { getInquirySortMechanismSetting } from '../../../../../app/livechat/server/lib/settings';
import { updateInquiryQueueSla } from './SlaHelper';
import { callbacks } from '../../../../../lib/callbacks';
type QueueInfo = {
message: {
@ -202,8 +203,9 @@ export const updatePredictedVisitorAbandonment = async () => {
await LivechatRooms.unsetAllPredictedVisitorAbandonment();
} else {
// Eng day: use a promise queue to update the predicted visitor abandonment time instead of all at once
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const promisesArray: Promise<void>[] = [];
await LivechatRooms.findOpen().forEach((room) => {
await LivechatRooms.findOpen(extraQuery).forEach((room) => {
promisesArray.push(setPredictedVisitorAbandonmentTime(room));
});
@ -238,7 +240,8 @@ export const updateSLAInquiries = async (sla?: Pick<IOmnichannelServiceLevelAgre
const { _id: slaId } = sla;
const promises: Promise<void>[] = [];
await LivechatRooms.findOpenBySlaId(slaId, {}).forEach((room) => {
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
await LivechatRooms.findOpenBySlaId(slaId, {}, extraQuery).forEach((room) => {
promises.push(updateInquiryQueueSla(room._id, sla));
});
await Promise.allSettled(promises);

@ -2,8 +2,11 @@ import type { IOmnichannelServiceLevelAgreements, IUser } from '@rocket.chat/cor
import { LivechatInquiry, LivechatRooms } from '@rocket.chat/models';
import { Message } from '@rocket.chat/core-services';
import { callbacks } from '../../../../../lib/callbacks';
export const removeSLAFromRooms = async (slaId: string) => {
const openRooms = await LivechatRooms.findOpenBySlaId(slaId, { projection: { _id: 1 } }).toArray();
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const openRooms = await LivechatRooms.findOpenBySlaId(slaId, { projection: { _id: 1 } }, extraQuery).toArray();
if (openRooms.length) {
const openRoomIds: string[] = openRooms.map(({ _id }) => _id);
await LivechatInquiry.bulkUnsetSla(openRoomIds);

@ -6,6 +6,7 @@ import { settings } from '../../../../../app/settings/server';
import { Livechat } from '../../../../../app/livechat/server/lib/LivechatTyped';
import { LivechatEnterprise } from './LivechatEnterprise';
import { i18n } from '../../../../../server/lib/i18n';
import { callbacks } from '../../../../../lib/callbacks';
import { schedulerLogger } from './logger';
import type { MainLogger } from '../../../../../server/lib/logger/getPino';
@ -141,8 +142,9 @@ export class VisitorInactivityMonitor {
return;
}
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const promises: Promise<void>[] = [];
await LivechatRooms.findAbandonedOpenRooms(new Date()).forEach((room) => {
await LivechatRooms.findAbandonedOpenRooms(new Date(), extraQuery).forEach((room) => {
switch (action) {
case 'close': {
this.logger.debug(`Closing room ${room._id}`);

@ -2,10 +2,10 @@ import { getUnitsFromUser } from './units';
// TODO: We need to add a new index in the departmentAncestors field
export const addQueryRestrictionsToRoomsModel = (originalQuery = {}) => {
export const addQueryRestrictionsToRoomsModel = async (originalQuery = {}) => {
const query = { ...originalQuery };
const units = Promise.await(getUnitsFromUser());
const units = await getUnitsFromUser();
if (!Array.isArray(units)) {
return query;
}
@ -17,14 +17,3 @@ export const addQueryRestrictionsToRoomsModel = (originalQuery = {}) => {
query.$and = [condition, ...expressions];
return query;
};
export const addQueryRestrictionsToDepartmentsModel = async (originalQuery = {}) => {
const query = { ...originalQuery, type: { $ne: 'u' } };
const units = await getUnitsFromUser();
if (Array.isArray(units)) {
query.ancestors = { $in: units };
}
return query;
};

@ -11,6 +11,7 @@ import { hasPermissionAsync } from '../../../../app/authorization/server/functio
import { updateCounter } from '../../../../app/statistics/server';
import { isTruthy } from '../../../../lib/isTruthy';
import { i18n } from '../../../../server/lib/i18n';
import { callbacks } from '../../../../lib/callbacks';
const getValue = (room: IRoom | null) => room && { rids: [room._id], name: room.name };
@ -43,9 +44,15 @@ const getRoomInfoByAuditParams = async ({
if (type === 'l') {
console.warn('Deprecation Warning! This method will be removed in the next version (4.0.0)');
const rooms: IRoom[] = await LivechatRooms.findByVisitorIdAndAgentId(visitor, agent, {
projection: { _id: 1 },
}).toArray();
const extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {});
const rooms: IRoom[] = await LivechatRooms.findByVisitorIdAndAgentId(
visitor,
agent,
{
projection: { _id: 1 },
},
extraQuery,
).toArray();
return rooms?.length ? { rids: rooms.map(({ _id }) => _id), name: i18n.t('Omnichannel') } : undefined;
}
};

@ -6,7 +6,7 @@ import type {
} from '@rocket.chat/core-typings';
import { LivechatPriorityWeight, DEFAULT_SLA_CONFIG } from '@rocket.chat/core-typings';
import type { ILivechatRoomsModel } from '@rocket.chat/model-typings';
import type { FindCursor, UpdateResult, Document, FindOptions, Db, Collection, UpdateOptions, Filter, UpdateFilter } from 'mongodb';
import type { FindCursor, UpdateResult, Document, FindOptions, Db, Collection, Filter } from 'mongodb';
import { LivechatRoomsRaw } from '../../../../server/models/raw/LivechatRooms';
import { queriesLogger } from '../../../app/livechat-enterprise/server/lib/logger';
@ -18,20 +18,23 @@ declare module '@rocket.chat/model-typings' {
removeUnitAssociationFromRooms: (unit: string) => Promise<void>;
updateDepartmentAncestorsById: (rid: string, ancestors?: string[]) => Promise<UpdateResult>;
unsetPredictedVisitorAbandonmentByRoomId(rid: string): Promise<UpdateResult>;
findAbandonedOpenRooms(date: Date): FindCursor<IOmnichannelRoom>;
findAbandonedOpenRooms(date: Date, extraQuery?: Filter<IOmnichannelRoom>): FindCursor<IOmnichannelRoom>;
setPredictedVisitorAbandonmentByRoomId(roomId: string, date: Date): Promise<UpdateResult>;
unsetAllPredictedVisitorAbandonment(): Promise<void>;
setOnHoldByRoomId(roomId: string): Promise<UpdateResult>;
unsetOnHoldByRoomId(roomId: string): Promise<UpdateResult>;
unsetOnHoldAndPredictedVisitorAbandonmentByRoomId(roomId: string): Promise<UpdateResult>;
findOpenRoomsByPriorityId(priorityId: string): FindCursor<IOmnichannelRoom>;
setSlaForRoomById(
roomId: string,
sla: Pick<IOmnichannelServiceLevelAgreements, '_id' | 'dueTimeInMinutes'>,
): Promise<UpdateResult | Document>;
removeSlaFromRoomById(roomId: string): Promise<UpdateResult | Document>;
bulkRemoveSlaFromRoomsById(slaId: string): Promise<UpdateResult | Document>;
findOpenBySlaId(slaId: string, options: FindOptions<IOmnichannelRoom>): FindCursor<IOmnichannelRoom>;
findOpenBySlaId(
slaId: string,
options: FindOptions<IOmnichannelRoom>,
extraQuery?: Filter<IOmnichannelRoom>,
): FindCursor<IOmnichannelRoom>;
setPriorityByRoomId(roomId: string, priority: Pick<ILivechatPriority, '_id' | 'sortItem'>): Promise<UpdateResult>;
unsetPriorityByRoomId(roomId: string): Promise<UpdateResult>;
}
@ -128,11 +131,16 @@ export class LivechatRoomsRawEE extends LivechatRoomsRaw implements ILivechatRoo
);
}
findOpenBySlaId(slaId: string, options: FindOptions<IOmnichannelRoom>): FindCursor<IOmnichannelRoom> {
const query: Filter<IOmnichannelRoom> = {
t: 'l',
findOpenBySlaId(
slaId: string,
options: FindOptions<IOmnichannelRoom>,
extraQuery?: Filter<IOmnichannelRoom>,
): FindCursor<IOmnichannelRoom> {
const query = {
t: 'l' as const,
open: true,
slaId,
...extraQuery,
};
return this.find(query, options);
@ -158,16 +166,6 @@ export class LivechatRoomsRawEE extends LivechatRoomsRaw implements ILivechatRoo
);
}
findOpenRoomsByPriorityId(priorityId: string): FindCursor<IOmnichannelRoom> {
const query: Filter<IOmnichannelRoom> = {
t: 'l',
open: true,
priorityId,
};
return this.find(query);
}
setPredictedVisitorAbandonmentByRoomId(rid: string, willBeAbandonedAt: Date): Promise<UpdateResult> {
const query = {
_id: rid,
@ -181,12 +179,13 @@ export class LivechatRoomsRawEE extends LivechatRoomsRaw implements ILivechatRoo
return this.updateOne(query, update);
}
findAbandonedOpenRooms(date: Date): FindCursor<IOmnichannelRoom> {
findAbandonedOpenRooms(date: Date, extraQuery?: Filter<IOmnichannelRoom>): FindCursor<IOmnichannelRoom> {
return this.find({
'omnichannel.predictedVisitorAbandonmentAt': { $lte: date },
'waitingResponse': { $exists: false },
'closedAt': { $exists: false },
'open': true,
...extraQuery,
});
}
@ -266,34 +265,16 @@ export class LivechatRoomsRawEE extends LivechatRoomsRaw implements ILivechatRoo
return this.updateOne(query, update);
}
find(...args: Parameters<LivechatRoomsRaw['find']>): FindCursor<IOmnichannelRoom> {
const [query, ...restArgs] = args;
const restrictedQuery = addQueryRestrictionsToRoomsModel(query);
queriesLogger.debug({ msg: 'LivechatRoomsRawEE.find', query: restrictedQuery });
return super.find<IOmnichannelRoom>(restrictedQuery, ...restArgs);
}
findPaginated(...args: Parameters<LivechatRoomsRaw['findPaginated']>): any {
const [query, ...restArgs] = args;
const restrictedQuery = addQueryRestrictionsToRoomsModel(query);
queriesLogger.debug({ msg: 'LivechatRoomsRawEE.findPaginated', query: restrictedQuery });
return super.findPaginated<IOmnichannelRoom>(restrictedQuery, ...restArgs);
}
/** @deprecated Use updateOne or updateMany instead */
update(...args: Parameters<LivechatRoomsRaw['update']>): ReturnType<LivechatRoomsRaw['update']> {
async update(...args: Parameters<LivechatRoomsRaw['update']>) {
const [query, ...restArgs] = args;
const restrictedQuery = addQueryRestrictionsToRoomsModel(query);
const restrictedQuery = await addQueryRestrictionsToRoomsModel(query);
queriesLogger.debug({ msg: 'LivechatRoomsRawEE.update', query: restrictedQuery });
return super.update(restrictedQuery, ...restArgs);
}
updateOne(
query: Filter<IOmnichannelRoom>,
update: UpdateFilter<IOmnichannelRoom>,
opts?: UpdateOptions,
extraOpts?: { bypassUnits?: boolean },
): ReturnType<LivechatRoomsRaw['updateOne']> {
async updateOne(...args: [...Parameters<LivechatRoomsRaw['updateOne']>, { bypassUnits?: boolean }?]) {
const [query, update, opts, extraOpts] = args;
if (extraOpts?.bypassUnits) {
// When calling updateOne from a service, we cannot call the meteor code inside the query restrictions
// So the solution now is to pass a bypassUnits flag to the updateOne method which prevents checking
@ -301,14 +282,14 @@ export class LivechatRoomsRawEE extends LivechatRoomsRaw implements ILivechatRoo
// We need to find a way of remove the meteor dependency when fetching units, and then, we can remove this flag
return super.updateOne(query, update, opts);
}
const restrictedQuery = addQueryRestrictionsToRoomsModel(query);
const restrictedQuery = await addQueryRestrictionsToRoomsModel(query);
queriesLogger.debug({ msg: 'LivechatRoomsRawEE.updateOne', query: restrictedQuery });
return super.updateOne(restrictedQuery, update, opts);
}
updateMany(...args: Parameters<LivechatRoomsRaw['updateMany']>): ReturnType<LivechatRoomsRaw['updateMany']> {
async updateMany(...args: Parameters<LivechatRoomsRaw['updateMany']>) {
const [query, ...restArgs] = args;
const restrictedQuery = addQueryRestrictionsToRoomsModel(query);
const restrictedQuery = await addQueryRestrictionsToRoomsModel(query);
queriesLogger.debug({ msg: 'LivechatRoomsRawEE.updateMany', query: restrictedQuery });
return super.updateMany(restrictedQuery, ...restArgs);
}

@ -173,6 +173,7 @@ type ChainedCallbackSignatures = {
query: FilterOperators<ILivechatDepartmentRecord>,
params: { userId: IUser['_id'] },
) => FilterOperators<ILivechatDepartmentRecord>;
'livechat.applyRoomRestrictions': (query: FilterOperators<IOmnichannelRoom>) => FilterOperators<IOmnichannelRoom>;
'livechat.onMaxNumberSimultaneousChatsReached': (inquiry: ILivechatInquiryRecord) => ILivechatInquiryRecord;
'on-business-hour-start': (params: { BusinessHourBehaviorClass: { new (): IBusinessHourBehavior } }) => {
BusinessHourBehaviorClass: { new (): IBusinessHourBehavior };

@ -1084,18 +1084,20 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
return this.col.aggregate(params, { readPreference: readSecondaryPreferred() });
}
findByVisitorId(visitorId: string, options: FindOptions<IOmnichannelRoom>) {
findByVisitorId(visitorId: string, options: FindOptions<IOmnichannelRoom>, extraQuery: Filter<IOmnichannelRoom> = {}) {
const query: Filter<IOmnichannelRoom> = {
't': 'l',
'v._id': visitorId,
...extraQuery,
};
return this.find(query, options);
}
findPaginatedByVisitorId(visitorId: string, options: FindOptions<IOmnichannelRoom>) {
findPaginatedByVisitorId(visitorId: string, options: FindOptions<IOmnichannelRoom>, extraQuery: Filter<IOmnichannelRoom> = {}) {
const query: Filter<IOmnichannelRoom> = {
't': 'l',
'v._id': visitorId,
...extraQuery,
};
return this.findPaginated(query, options);
}
@ -1203,6 +1205,7 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
roomIds,
onhold,
options = {},
extraQuery = {},
}: {
agents?: string[];
roomName?: string;
@ -1217,9 +1220,11 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
roomIds?: string[];
onhold?: boolean;
options?: { offset?: number; count?: number; sort?: { [k: string]: SortDirection } };
extraQuery?: Filter<IOmnichannelRoom>;
}) {
const query: Filter<IOmnichannelRoom> = {
t: 'l',
...extraQuery,
...(agents && {
$or: [{ 'servedBy._id': { $in: agents } }, { 'servedBy.username': { $in: agents } }],
}),
@ -1407,8 +1412,8 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
return this.updateOne({ _id: roomId }, { $set: { departmentId } });
}
findOpen() {
return this.find({ t: 'l', open: true });
findOpen(extraQuery = {}) {
return this.find({ t: 'l', open: true, ...extraQuery });
}
setAutoTransferOngoingById(roomId: string) {
@ -1745,7 +1750,7 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
return this.find(query, options);
}
findByIds(ids: string[], fields: FindOptions<IOmnichannelRoom>['projection']) {
findByIds(ids: string[], fields: FindOptions<IOmnichannelRoom>['projection'], extraQuery: Filter<IOmnichannelRoom> = {}) {
const options: FindOptions<IOmnichannelRoom> = {};
if (fields) {
@ -1755,6 +1760,7 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
const query: Filter<IOmnichannelRoom> = {
t: 'l',
_id: { $in: ids },
...extraQuery,
};
return this.find(query, options);
@ -1869,11 +1875,12 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
return livechatCount.value;
}
findOpenByVisitorToken(visitorToken: string, options: FindOptions<IOmnichannelRoom> = {}) {
findOpenByVisitorToken(visitorToken: string, options: FindOptions<IOmnichannelRoom> = {}, extraQuery: Filter<IOmnichannelRoom> = {}) {
const query: Filter<IOmnichannelRoom> = {
't': 'l',
'open': true,
'v.token': visitorToken,
...extraQuery,
};
return this.find(query, options);
@ -1906,31 +1913,44 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
return this.findOne(query, options);
}
findOpenByVisitorTokenAndDepartmentId(visitorToken: string, departmentId: string, options: FindOptions<IOmnichannelRoom> = {}) {
findOpenByVisitorTokenAndDepartmentId(
visitorToken: string,
departmentId: string,
options: FindOptions<IOmnichannelRoom> = {},
extraQuery: Filter<IOmnichannelRoom> = {},
) {
const query: Filter<IOmnichannelRoom> = {
't': 'l',
'open': true,
'v.token': visitorToken,
departmentId,
...extraQuery,
};
return this.find(query, options);
}
findByVisitorToken(visitorToken: string) {
findByVisitorToken(visitorToken: string, extraQuery: Filter<IOmnichannelRoom> = {}) {
const query: Filter<IOmnichannelRoom> = {
't': 'l',
'v.token': visitorToken,
...extraQuery,
};
return this.find(query);
}
findByVisitorIdAndAgentId(visitorId?: string, agentId?: string, options: FindOptions<IOmnichannelRoom> = {}) {
findByVisitorIdAndAgentId(
visitorId?: string,
agentId?: string,
options: FindOptions<IOmnichannelRoom> = {},
extraQuery: Filter<IOmnichannelRoom> = {},
) {
const query: Filter<IOmnichannelRoom> = {
t: 'l',
...(visitorId && { 'v._id': visitorId }),
...(agentId && { 'servedBy._id': agentId }),
...extraQuery,
};
return this.find(query, options);
@ -1947,12 +1967,13 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
return this.findOne(query, options);
}
findClosedRooms(departmentIds?: string[], options: FindOptions<IOmnichannelRoom> = {}) {
findClosedRooms(departmentIds?: string[], options: FindOptions<IOmnichannelRoom> = {}, extraQuery: Filter<IOmnichannelRoom> = {}) {
const query: Filter<IOmnichannelRoom> = {
t: 'l',
open: { $exists: false },
closedAt: { $exists: true },
...(Array.isArray(departmentIds) && departmentIds.length > 0 && { departmentId: { $in: departmentIds } }),
...extraQuery,
};
return this.find(query, options);
@ -2071,7 +2092,12 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
return this.col.countDocuments(query);
}
getAnalyticsMetricsBetweenDate(t: 'l', date: { gte: Date; lt: Date }, { departmentId }: { departmentId?: string } = {}) {
getAnalyticsMetricsBetweenDate(
t: 'l',
date: { gte: Date; lt: Date },
{ departmentId }: { departmentId?: string } = {},
extraQuery: Document = {},
) {
const query: Filter<IOmnichannelRoom> = {
t,
ts: {
@ -2079,6 +2105,7 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
$lt: new Date(date.lt), // ISODate, ts < date.lt
},
...(departmentId && departmentId !== 'undefined' && { departmentId }),
...extraQuery,
};
return this.find(query, {
@ -2091,6 +2118,7 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
date: { gte: Date; lt: Date },
{ departmentId }: { departmentId?: string } = {},
extraQuery: Document = {},
extraMatchers: Document = {},
) {
return this.col.aggregate<Pick<IOmnichannelRoom, '_id' | 'ts' | 'departmentId' | 'open' | 'servedBy' | 'metrics' | 'msgs'>>(
[
@ -2102,6 +2130,7 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
$lt: new Date(date.lt), // ISODate, ts < date.lt
},
...(departmentId && departmentId !== 'undefined' && { departmentId }),
...extraMatchers,
},
},
{ $addFields: { roomId: '$_id' } },
@ -2246,11 +2275,12 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
);
}
findOpenByAgent(userId: string) {
findOpenByAgent(userId: string, extraQuery: Filter<IOmnichannelRoom> = {}) {
const query: Filter<IOmnichannelRoom> = {
't': 'l',
'open': true,
'servedBy._id': userId,
...extraQuery,
};
return this.find(query);

@ -1,5 +1,5 @@
import type { IMessage, IOmnichannelRoom, IOmnichannelRoomClosingInfo, ISetting, IVisitor } from '@rocket.chat/core-typings';
import type { FindCursor, UpdateResult, AggregationCursor, Document, FindOptions, DeleteResult } from 'mongodb';
import type { FindCursor, UpdateResult, AggregationCursor, Document, FindOptions, DeleteResult, Filter } from 'mongodb';
import type { FindPaginated } from '..';
import type { IBaseModel } from './IBaseModel';
@ -62,9 +62,9 @@ export interface ILivechatRoomsModel extends IBaseModel<IOmnichannelRoom> {
findAllAverageOfServiceTime(params: Period & WithDepartment & WithOnlyCount & WithOptions): any;
findByVisitorId(visitorId: any, options: any): any;
findByVisitorId(visitorId: any, options: any, extraQuery?: any): any;
findPaginatedByVisitorId(visitorId: any, options: any): any;
findPaginatedByVisitorId(visitorId: any, options: any, extraQuery?: any): any;
findRoomsByVisitorIdAndMessageWithCriteria(params: {
visitorId: any;
@ -90,6 +90,7 @@ export interface ILivechatRoomsModel extends IBaseModel<IOmnichannelRoom> {
roomIds?: any;
onhold: any;
options?: any;
extraQuery?: any;
}): FindPaginated<FindCursor<IOmnichannelRoom>>;
getOnHoldConversationsBetweenDate(from: any, to: any, departmentId: any): any;
@ -100,7 +101,7 @@ export interface ILivechatRoomsModel extends IBaseModel<IOmnichannelRoom> {
setDepartmentByRoomId(roomId: any, departmentId: any): any;
findOpen(): FindCursor<IOmnichannelRoom>;
findOpen(extraQuery?: Filter<IOmnichannelRoom>): FindCursor<IOmnichannelRoom>;
setAutoTransferOngoingById(roomId: string): Promise<UpdateResult>;
@ -130,7 +131,11 @@ export interface ILivechatRoomsModel extends IBaseModel<IOmnichannelRoom> {
data: { _id: string; topic: string; tags: string[]; livechatData?: Record<string, any> } & Record<string, unknown>,
): Promise<UpdateResult | undefined>;
findById(_id: string, fields?: FindOptions<IOmnichannelRoom>['projection']): FindCursor<IOmnichannelRoom>;
findByIds(ids: string[], fields?: FindOptions<IOmnichannelRoom>['projection']): FindCursor<IOmnichannelRoom>;
findByIds(
ids: string[],
fields?: FindOptions<IOmnichannelRoom>['projection'],
extraQuery?: Filter<IOmnichannelRoom>,
): FindCursor<IOmnichannelRoom>;
findOneByIdAndVisitorToken(
_id: string,
visitorToken: string,
@ -156,7 +161,11 @@ export interface ILivechatRoomsModel extends IBaseModel<IOmnichannelRoom> {
findOneLastServedAndClosedByVisitorToken(visitorToken: string, options?: FindOptions<IOmnichannelRoom>): Promise<IOmnichannelRoom | null>;
findOneByVisitorToken(visitorToken: string, fields?: FindOptions<IOmnichannelRoom>['projection']): Promise<IOmnichannelRoom | null>;
updateRoomCount(): Promise<ISetting | null>;
findOpenByVisitorToken(visitorToken: string, options?: FindOptions<IOmnichannelRoom>): FindCursor<IOmnichannelRoom>;
findOpenByVisitorToken(
visitorToken: string,
options?: FindOptions<IOmnichannelRoom>,
extraQuery?: Filter<IOmnichannelRoom>,
): FindCursor<IOmnichannelRoom>;
findOneOpenByVisitorToken(visitorToken: string, options?: FindOptions<IOmnichannelRoom>): Promise<IOmnichannelRoom | null>;
findOneOpenByVisitorTokenAndDepartmentIdAndSource(
visitorToken: string,
@ -168,15 +177,25 @@ export interface ILivechatRoomsModel extends IBaseModel<IOmnichannelRoom> {
visitorToken: string,
departmentId: string,
options?: FindOptions<IOmnichannelRoom>,
extraQuery?: Filter<IOmnichannelRoom>,
): FindCursor<IOmnichannelRoom>;
findByVisitorToken(visitorToken: string, extraQuery?: Filter<IOmnichannelRoom>): FindCursor<IOmnichannelRoom>;
findByVisitorIdAndAgentId(
visitorId?: string,
agentId?: string,
options?: FindOptions<IOmnichannelRoom>,
extraQuery?: Filter<IOmnichannelRoom>,
): FindCursor<IOmnichannelRoom>;
findByVisitorToken(visitorToken: string): FindCursor<IOmnichannelRoom>;
findByVisitorIdAndAgentId(visitorId?: string, agentId?: string, options?: FindOptions<IOmnichannelRoom>): FindCursor<IOmnichannelRoom>;
findOneOpenByRoomIdAndVisitorToken(
roomId: string,
visitorToken: string,
options?: FindOptions<IOmnichannelRoom>,
): Promise<IOmnichannelRoom | null>;
findClosedRooms(departmentIds?: string[], options?: FindOptions<IOmnichannelRoom>): FindCursor<IOmnichannelRoom>;
findClosedRooms(
departmentIds?: string[],
options?: FindOptions<IOmnichannelRoom>,
extraQuery?: Filter<IOmnichannelRoom>,
): FindCursor<IOmnichannelRoom>;
setResponseByRoomId(roomId: string, response: { user: { _id: string; username: string } }): Promise<UpdateResult>;
setNotResponseByRoomId(roomId: string): Promise<UpdateResult>;
setAgentLastMessageTs(roomId: string): Promise<UpdateResult>;
@ -190,18 +209,20 @@ export interface ILivechatRoomsModel extends IBaseModel<IOmnichannelRoom> {
t: 'l',
date: { gte: Date; lt: Date },
data?: { departmentId: string },
extraQuery?: Filter<IOmnichannelRoom>,
): FindCursor<Pick<IOmnichannelRoom, 'ts' | 'departmentId' | 'open' | 'servedBy' | 'metrics' | 'msgs'>>;
getAnalyticsMetricsBetweenDateWithMessages(
t: string,
date: { gte: Date; lt: Date },
data?: { departmentId: string },
extraQuery?: Document,
extraMatchers?: Document,
): AggregationCursor<Pick<IOmnichannelRoom, '_id' | 'ts' | 'departmentId' | 'open' | 'servedBy' | 'metrics' | 'msgs'>>;
getAnalyticsBetweenDate(
date: { gte: Date; lt: Date },
data?: { departmentId: string },
): AggregationCursor<Pick<IOmnichannelRoom, 'ts' | 'departmentId' | 'open' | 'servedBy' | 'metrics' | 'msgs' | 'onHold'>>;
findOpenByAgent(userId: string): FindCursor<IOmnichannelRoom>;
findOpenByAgent(userId: string, extraQuery?: Filter<IOmnichannelRoom>): FindCursor<IOmnichannelRoom>;
changeAgentByRoomId(roomId: string, newAgent: { agentId: string; username: string }): Promise<UpdateResult>;
changeDepartmentIdByRoomId(roomId: string, departmentId: string): Promise<UpdateResult>;
saveCRMDataByRoomId(roomId: string, crmData: unknown): Promise<UpdateResult>;

Loading…
Cancel
Save