diff --git a/apps/meteor/app/livechat/server/api/rest.ts b/apps/meteor/app/livechat/server/api/rest.ts index f9da6690185e4..2d48d6806c8cc 100644 --- a/apps/meteor/app/livechat/server/api/rest.ts +++ b/apps/meteor/app/livechat/server/api/rest.ts @@ -13,3 +13,4 @@ import './v1/contact'; import './v1/webhooks'; import './v1/integration'; import './v1/statistics'; +import './v1/mac'; diff --git a/apps/meteor/app/livechat/server/api/v1/mac.ts b/apps/meteor/app/livechat/server/api/v1/mac.ts new file mode 100644 index 0000000000000..48e4c143673ed --- /dev/null +++ b/apps/meteor/app/livechat/server/api/v1/mac.ts @@ -0,0 +1,15 @@ +import { Omnichannel } from '@rocket.chat/core-services'; + +import { API } from '../../../../api/server'; + +API.v1.addRoute( + 'omnichannel/mac/check', + { authRequired: true }, + { + async get() { + return API.v1.success({ + onLimit: await Omnichannel.checkMACLimit(), + }); + }, + }, +); diff --git a/apps/meteor/app/livechat/server/api/v1/message.ts b/apps/meteor/app/livechat/server/api/v1/message.ts index 104e2ece94d50..9f158b07c140a 100644 --- a/apps/meteor/app/livechat/server/api/v1/message.ts +++ b/apps/meteor/app/livechat/server/api/v1/message.ts @@ -1,3 +1,4 @@ +import { Omnichannel } from '@rocket.chat/core-services'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms, Messages } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; @@ -214,6 +215,10 @@ API.v1.addRoute( throw new Error('invalid-room'); } + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + let ls = undefined; if (this.queryParams.ls) { ls = new Date(this.queryParams.ls); diff --git a/apps/meteor/app/livechat/server/api/v1/room.ts b/apps/meteor/app/livechat/server/api/v1/room.ts index 86629e636bf84..be3932a85bd99 100644 --- a/apps/meteor/app/livechat/server/api/v1/room.ts +++ b/apps/meteor/app/livechat/server/api/v1/room.ts @@ -1,3 +1,4 @@ +import { Omnichannel } from '@rocket.chat/core-services'; import type { ILivechatAgent, IOmnichannelRoom, IUser, SelectedAgent, TransferByData } from '@rocket.chat/core-typings'; import { isOmnichannelRoom, OmnichannelSourceType } from '@rocket.chat/core-typings'; import { LivechatVisitors, Users, LivechatRooms, Subscriptions, Messages } from '@rocket.chat/models'; @@ -74,6 +75,10 @@ API.v1.addRoute('livechat/room', { }, }; + if (!(await Omnichannel.checkMACLimit())) { + throw new Error('error-mac-limit-reached'); + } + const newRoom = await getRoom({ guest, rid, agent, roomInfo, extraParams }); return API.v1.success(newRoom); } @@ -326,6 +331,10 @@ API.v1.addRoute( throw new Error('This_conversation_is_already_closed'); } + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + const guest = await LivechatVisitors.findOneEnabledById(room.v?._id); const transferedBy = this.user satisfies TransferByData; transferData.transferredBy = normalizeTransferredByData(transferedBy, room); @@ -403,6 +412,10 @@ API.v1.addRoute( throw new Error('error-invalid-room'); } + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + if (!(await canAccessRoomAsync(room, user))) { throw new Error('error-not-allowed'); } @@ -425,6 +438,10 @@ API.v1.addRoute( throw new Error('error-invalid-room'); } + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + if ( (!room.servedBy || room.servedBy._id !== this.userId) && !(await hasPermissionAsync(this.userId, 'save-others-livechat-room-info')) diff --git a/apps/meteor/app/livechat/server/api/v1/transcript.ts b/apps/meteor/app/livechat/server/api/v1/transcript.ts index 3eaa91c37c7cf..ca9ddd38046ec 100644 --- a/apps/meteor/app/livechat/server/api/v1/transcript.ts +++ b/apps/meteor/app/livechat/server/api/v1/transcript.ts @@ -1,3 +1,4 @@ +import { Omnichannel } from '@rocket.chat/core-services'; import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; import { LivechatRooms, Users } from '@rocket.chat/models'; import { isPOSTLivechatTranscriptParams, isPOSTLivechatTranscriptRequestParams } from '@rocket.chat/rest-typings'; @@ -34,8 +35,8 @@ API.v1.addRoute( { async delete() { const { rid } = this.urlParams; - const room = await LivechatRooms.findOneById>(rid, { - projection: { open: 1, transcriptRequest: 1 }, + const room = await LivechatRooms.findOneById>(rid, { + projection: { open: 1, transcriptRequest: 1, v: 1 }, }); if (!room?.open) { @@ -45,6 +46,10 @@ API.v1.addRoute( throw new Error('error-transcript-not-requested'); } + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + await LivechatRooms.unsetEmailTranscriptRequestedByRoomId(rid); return API.v1.success(); diff --git a/apps/meteor/app/livechat/server/api/v1/videoCall.ts b/apps/meteor/app/livechat/server/api/v1/videoCall.ts index 5ce0ddc4ca379..1663d12264d4f 100644 --- a/apps/meteor/app/livechat/server/api/v1/videoCall.ts +++ b/apps/meteor/app/livechat/server/api/v1/videoCall.ts @@ -1,4 +1,5 @@ -import { Message } from '@rocket.chat/core-services'; +import { Message, Omnichannel } from '@rocket.chat/core-services'; +import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; import { Messages, Settings, Rooms } from '@rocket.chat/models'; import { isGETWebRTCCall, isPUTWebRTCCallId } from '@rocket.chat/rest-typings'; @@ -27,6 +28,10 @@ API.v1.addRoute( throw new Error('invalid-room'); } + if (!(await Omnichannel.isRoomEnabled(room as IOmnichannelRoom))) { + throw new Error('error-mac-limit-reached'); + } + const webrtcCallingAllowed = rcSettings.get('WebRTC_Enabled') === true && rcSettings.get('Omnichannel_call_provider') === 'WebRTC'; if (!webrtcCallingAllowed) { throw new Error('webRTC calling not enabled'); @@ -79,6 +84,10 @@ API.v1.addRoute( throw new Error('invalid-room'); } + if (!(await Omnichannel.isRoomEnabled(room as IOmnichannelRoom))) { + throw new Error('error-mac-limit-reached'); + } + const call = await Messages.findOneById(callId); if (!call || call.t !== 'livechat_webrtc_video_call') { throw new Error('invalid-callId'); diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index ffd3a29b229f5..b2013851f2c7f 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -4,7 +4,7 @@ import dns from 'dns'; import util from 'util'; -import { Message, VideoConf, api } from '@rocket.chat/core-services'; +import { Message, VideoConf, api, Omnichannel } from '@rocket.chat/core-services'; import { Logger } from '@rocket.chat/logger'; import { LivechatVisitors, @@ -58,6 +58,11 @@ export const Livechat = { if (guest.name) { message.alias = guest.name; } + + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + return Object.assign(await sendMessage(guest, message, room), { newRoom, showConnecting: this.showConnecting(), @@ -813,6 +818,10 @@ export const Livechat = { throw new Meteor.Error('error-transcript-already-requested', 'Transcript already requested'); } + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + const { _id, username, name, utcOffset } = user; const transcriptRequest = { requestedAt: new Date(), diff --git a/apps/meteor/app/livechat/server/lib/RoutingManager.ts b/apps/meteor/app/livechat/server/lib/RoutingManager.ts index f2fd7010eb12e..7408f7b942c2a 100644 --- a/apps/meteor/app/livechat/server/lib/RoutingManager.ts +++ b/apps/meteor/app/livechat/server/lib/RoutingManager.ts @@ -156,6 +156,15 @@ export const RoutingManager: Routing = { await Promise.all([Message.saveSystemMessage('command', rid, 'connected', user), Message.saveSystemMessage('uj', rid, '', user)]); } + if (!room) { + logger.debug(`Cannot assign agent to inquiry ${inquiry._id}: Room not found`); + throw new Meteor.Error('error-room-not-found', 'Room not found'); + } + + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + await dispatchAgentDelegated(rid, agent.agentId); logger.debug(`Agent ${agent.agentId} assigned to inquriy ${inquiry._id}. Instances notified`); @@ -173,6 +182,10 @@ export const RoutingManager: Routing = { return false; } + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + if (departmentId && departmentId !== department) { logger.debug(`Switching department for inquiry ${inquiry._id} [Current: ${department} | Next: ${departmentId}]`); await updateChatDepartment({ @@ -223,6 +236,10 @@ export const RoutingManager: Routing = { return room; } + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + if (room.servedBy && room.servedBy._id === agent.agentId) { logger.debug(`Cannot take Inquiry ${inquiry._id}: Already taken by agent ${room.servedBy._id}`); return room; @@ -260,6 +277,11 @@ export const RoutingManager: Routing = { }, async transferRoom(room, guest, transferData) { + if (!(await Omnichannel.isRoomEnabled(room))) { + throw new Error('error-mac-limit-reached'); + } + + logger.debug(`Transfering room ${room._id} by ${transferData.transferredBy._id}`); if (transferData.departmentId) { logger.debug(`Transfering room ${room._id} to department ${transferData.departmentId}`); return forwardRoomToDepartment(room, guest, transferData); diff --git a/apps/meteor/server/services/omnichannel/mac.ts b/apps/meteor/server/services/omnichannel/mac.ts new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/apps/meteor/server/services/omnichannel/service.ts b/apps/meteor/server/services/omnichannel/service.ts index 7f35de104e1cd..123a8d6fea1c6 100644 --- a/apps/meteor/server/services/omnichannel/service.ts +++ b/apps/meteor/server/services/omnichannel/service.ts @@ -1,6 +1,6 @@ import { ServiceClassInternal } from '@rocket.chat/core-services'; import type { IOmnichannelService } from '@rocket.chat/core-services'; -import type { IOmnichannelQueue } from '@rocket.chat/core-typings'; +import type { AtLeast, IOmnichannelQueue, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { Livechat } from '../../../app/livechat/server'; import { RoutingManager } from '../../../app/livechat/server/lib/RoutingManager'; @@ -28,6 +28,17 @@ export class OmnichannelService extends ServiceClassInternal implements IOmnicha await Livechat.notifyAgentStatusChanged(user._id, user.status); } }); + + // TODO: Waiting for license definitions + /* this.onEvent('mac.limitreached', async (): Promise => { + // void Livechat.notifyMacLimitReached(); + await this.queueWorker.stop(); + }); + + this.onEvent('license.validated', async (): Promise => { + // void Livechat.notifyLicenseChanged(); + await this.queueWorker.shouldStart(); + }); */ } async started() { @@ -39,4 +50,15 @@ export class OmnichannelService extends ServiceClassInternal implements IOmnicha getQueueWorker(): IOmnichannelQueue { return this.queueWorker; } + + async isRoomEnabled(_room: AtLeast): Promise { + // const currentMonth = moment.utc().format('YYYY-MM'); + // return license.isMacOnLimit() || room.v.activity.includes(currentMonth) + return true; + } + + async checkMACLimit(): Promise { + // return license.isMacOnLimit(); + return true; + } } diff --git a/packages/core-services/src/types/IOmnichannelService.ts b/packages/core-services/src/types/IOmnichannelService.ts index fb3cc60d92434..a1b2d1a9d9617 100644 --- a/packages/core-services/src/types/IOmnichannelService.ts +++ b/packages/core-services/src/types/IOmnichannelService.ts @@ -1,7 +1,9 @@ -import type { IOmnichannelQueue } from '@rocket.chat/core-typings'; +import type { AtLeast, IOmnichannelQueue, IOmnichannelRoom } from '@rocket.chat/core-typings'; import type { IServiceClass } from './ServiceClass'; export interface IOmnichannelService extends IServiceClass { getQueueWorker(): IOmnichannelQueue; + isRoomEnabled(_room: AtLeast): Promise; + checkMACLimit(): Promise; }