diff --git a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts index d65897b72094..0474d01fdcb4 100644 --- a/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts +++ b/apps/meteor/app/cloud/server/functions/buildRegistrationData.ts @@ -31,6 +31,8 @@ type WorkspaceRegistrationData = { setupComplete: boolean; connectionDisable: boolean; npsEnabled: SettingValue; + activeContactsBillingMonth: unknown; + activeContactsYesterday: unknown; }; export async function buildWorkspaceRegistrationData(contactEmail: T): Promise> { @@ -78,5 +80,7 @@ export async function buildWorkspaceRegistrationData { + statistics.omnichannelContactsBySource = result || defaultValue; + }), + ); + + const monthAgo = moment.utc().subtract(30, 'days').toDate(); + const today = moment.utc().toDate(); + statsPms.push( + LivechatRooms.getMACStatisticsBetweenDates(monthAgo, today).then(([result]) => { + statistics.uniqueContactsOfLastMonth = result || defaultValue; + }), + ); + + const weekAgo = moment.utc().subtract(7, 'days').toDate(); + statsPms.push( + LivechatRooms.getMACStatisticsBetweenDates(weekAgo, today).then(([result]) => { + statistics.uniqueContactsOfLastWeek = result || defaultValue; + }), + ); + + const yesterday = moment.utc().subtract(1, 'days').toDate(); + statsPms.push( + LivechatRooms.getMACStatisticsBetweenDates(yesterday, today).then(([result]) => { + statistics.uniqueContactsOfYesterday = result || defaultValue; + }), + ); + // Message statistics statistics.totalChannelMessages = (await Rooms.findByType('c', { projection: { msgs: 1 } }).toArray()).reduce( function _countChannelMessages(num: number, room: IRoom) { diff --git a/apps/meteor/server/models/raw/LivechatRooms.ts b/apps/meteor/server/models/raw/LivechatRooms.ts index 40f6de9a2fba..925cced21a78 100644 --- a/apps/meteor/server/models/raw/LivechatRooms.ts +++ b/apps/meteor/server/models/raw/LivechatRooms.ts @@ -8,6 +8,7 @@ import type { ILivechatPriority, IOmnichannelServiceLevelAgreements, ReportResult, + MACStats, } from '@rocket.chat/core-typings'; import { UserStatus } from '@rocket.chat/core-typings'; import type { ILivechatRoomsModel } from '@rocket.chat/model-typings'; @@ -2466,6 +2467,126 @@ export class LivechatRoomsRaw extends BaseRaw implements ILive return this.updateOne(query, update); } + async getMACStatisticsForPeriod(period: string): Promise { + return this.col + .aggregate([ + { + $match: { + 't': 'l', + 'v.activity': period, + }, + }, + { + $group: { + _id: { + source: { + $ifNull: ['$source.alias', '$source.type'], + }, + }, + contactsCount: { + $addToSet: '$v._id', + }, + conversationsCount: { + $sum: 1, + }, + }, + }, + { + $group: { + _id: null, + sources: { + $push: { + source: '$_id.source', + contactsCount: { + $size: '$contactsCount', + }, + conversationsCount: '$conversationsCount', + }, + }, + totalContactsCount: { + $sum: { + $size: '$contactsCount', + }, + }, + totalConversationsCount: { + $sum: '$conversationsCount', + }, + }, + }, + { + $project: { + _id: 0, + contactsCount: '$totalContactsCount', + conversationsCount: '$totalConversationsCount', + sources: 1, + }, + }, + ]) + .toArray(); + } + + async getMACStatisticsBetweenDates(start: Date, end: Date): Promise { + return this.col + .aggregate([ + { + $match: { + 't': 'l', + 'v.activity': { $exists: true }, + 'ts': { + $gte: start, + $lt: end, + }, + }, + }, + { + $group: { + _id: { + source: { + $ifNull: ['$source.alias', '$source.type'], + }, + }, + contactsCount: { + $addToSet: '$v._id', + }, + conversationsCount: { + $sum: 1, + }, + }, + }, + { + $group: { + _id: null, + sources: { + $push: { + source: '$_id.source', + contactsCount: { + $size: '$contactsCount', + }, + conversationsCount: '$conversationsCount', + }, + }, + totalContactsCount: { + $sum: { + $size: '$contactsCount', + }, + }, + totalConversationsCount: { + $sum: '$conversationsCount', + }, + }, + }, + { + $project: { + _id: 0, + contactsCount: '$totalContactsCount', + conversationsCount: '$totalConversationsCount', + sources: 1, + }, + }, + ]) + .toArray(); + } + async unsetAllPredictedVisitorAbandonment(): Promise { throw new Error('Method not implemented.'); } diff --git a/apps/meteor/server/models/raw/LivechatVisitors.ts b/apps/meteor/server/models/raw/LivechatVisitors.ts index 21e155b4bab2..69a53d286a7c 100644 --- a/apps/meteor/server/models/raw/LivechatVisitors.ts +++ b/apps/meteor/server/models/raw/LivechatVisitors.ts @@ -440,6 +440,12 @@ export class LivechatVisitorsRaw extends BaseRaw implements IL }, ); } + + countVisitorsOnPeriod(period: string): Promise { + return this.countDocuments({ + activity: period, + }); + } } type DeepWriteable = { -readonly [P in keyof T]: DeepWriteable }; diff --git a/packages/core-typings/src/IStats.ts b/packages/core-typings/src/IStats.ts index 2ea8115a727c..0be2841f5191 100644 --- a/packages/core-typings/src/IStats.ts +++ b/packages/core-typings/src/IStats.ts @@ -3,6 +3,7 @@ import type { CpuInfo } from 'os'; import type { DeviceSessionAggregationResult, OSSessionAggregationResult, UserSessionAggregationResult } from './ISession'; import type { ISettingStatisticsObject } from './ISetting'; import type { ITeamStats } from './ITeam'; +import type { MACStats } from './omnichannel'; export interface IStats { _id: string; @@ -85,6 +86,10 @@ export interface IStats { mongoStorageEngine: string; pushQueue: number; omnichannelSources: { [key: string]: number | string }[]; + omnichannelContactsBySource: MACStats; + uniqueContactsOfLastMonth: MACStats; + uniqueContactsOfLastWeek: MACStats; + uniqueContactsOfYesterday: MACStats; departments: number; archivedDepartments: number; routingAlgorithm: string; diff --git a/packages/core-typings/src/omnichannel/index.ts b/packages/core-typings/src/omnichannel/index.ts index 0995968370e4..6fcd0a116d82 100644 --- a/packages/core-typings/src/omnichannel/index.ts +++ b/packages/core-typings/src/omnichannel/index.ts @@ -1,3 +1,4 @@ export * from './sms'; export * from './routing'; export * from './reports'; +export * from './mac'; diff --git a/packages/core-typings/src/omnichannel/mac.ts b/packages/core-typings/src/omnichannel/mac.ts new file mode 100644 index 000000000000..8591edbb0287 --- /dev/null +++ b/packages/core-typings/src/omnichannel/mac.ts @@ -0,0 +1,5 @@ +export type MACStats = { + contactsCount: number; + conversationsCount: number; + sources: { source: string; contactsCount: number; conversationsCount: number }[]; +}; diff --git a/packages/model-typings/src/models/ILivechatRoomsModel.ts b/packages/model-typings/src/models/ILivechatRoomsModel.ts index d460ad8382dd..36bc96d18e35 100644 --- a/packages/model-typings/src/models/ILivechatRoomsModel.ts +++ b/packages/model-typings/src/models/ILivechatRoomsModel.ts @@ -1,4 +1,11 @@ -import type { IMessage, IOmnichannelRoom, IOmnichannelRoomClosingInfo, ISetting, ILivechatVisitor } from '@rocket.chat/core-typings'; +import type { + IMessage, + IOmnichannelRoom, + IOmnichannelRoomClosingInfo, + ISetting, + ILivechatVisitor, + MACStats, +} from '@rocket.chat/core-typings'; import type { FindCursor, UpdateResult, AggregationCursor, Document, FindOptions, DeleteResult, Filter } from 'mongodb'; import type { FindPaginated } from '..'; @@ -235,4 +242,6 @@ export interface ILivechatRoomsModel extends IBaseModel { changeVisitorByRoomId(roomId: string, visitor: { _id: string; username: string; token: string }): Promise; unarchiveOneById(roomId: string): Promise; markVisitorActiveForPeriod(rid: string, period: string): Promise; + getMACStatisticsForPeriod(period: string): Promise; + getMACStatisticsBetweenDates(start: Date, end: Date): Promise; } diff --git a/packages/model-typings/src/models/ILivechatVisitorsModel.ts b/packages/model-typings/src/models/ILivechatVisitorsModel.ts index 8eca1dd8a829..b20579184c79 100644 --- a/packages/model-typings/src/models/ILivechatVisitorsModel.ts +++ b/packages/model-typings/src/models/ILivechatVisitorsModel.ts @@ -61,4 +61,6 @@ export interface ILivechatVisitorsModel extends IBaseModel { disableById(_id: string): Promise; findEnabled(query: Filter, options?: FindOptions): FindCursor; + + countVisitorsOnPeriod(period: string): Promise; }