diff --git a/apps/meteor/app/livechat/imports/server/rest/sms.js b/apps/meteor/app/livechat/imports/server/rest/sms.ts similarity index 53% rename from apps/meteor/app/livechat/imports/server/rest/sms.js rename to apps/meteor/app/livechat/imports/server/rest/sms.ts index 117407807dad..2551b79cc425 100644 --- a/apps/meteor/app/livechat/imports/server/rest/sms.js +++ b/apps/meteor/app/livechat/imports/server/rest/sms.ts @@ -1,4 +1,12 @@ import { OmnichannelIntegration } from '@rocket.chat/core-services'; +import type { + ILivechatVisitor, + IOmnichannelRoom, + IUpload, + MessageAttachment, + ServiceData, + FileAttachmentProps, +} from '@rocket.chat/core-typings'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; import { LivechatVisitors, LivechatRooms, LivechatDepartment } from '@rocket.chat/models'; @@ -6,14 +14,16 @@ import { Random } from '@rocket.chat/random'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { Meteor } from 'meteor/meteor'; +import { getFileExtension } from '../../../../../lib/utils/getFileExtension'; import { API } from '../../../../api/server'; import { FileUpload } from '../../../../file-upload/server'; import { settings } from '../../../../settings/server'; +import type { ILivechatMessage } from '../../../server/lib/LivechatTyped'; import { Livechat as LivechatTyped } from '../../../server/lib/LivechatTyped'; const logger = new Logger('SMS'); -const getUploadFile = async (details, fileUrl) => { +const getUploadFile = async (details: Omit, fileUrl: string) => { const response = await fetch(fileUrl); const content = Buffer.from(await response.arrayBuffer()); @@ -29,19 +39,19 @@ const getUploadFile = async (details, fileUrl) => { return fileStore.insert({ ...details, size: contentSize }, content); }; -const defineDepartment = async (idOrName) => { +const defineDepartment = async (idOrName?: string) => { if (!idOrName || idOrName === '') { return; } - const department = await LivechatDepartment.findOneByIdOrName(idOrName); - return department && department._id; + const department = await LivechatDepartment.findOneByIdOrName(idOrName, { projection: { _id: 1 } }); + return department?._id; }; -const defineVisitor = async (smsNumber, targetDepartment) => { +const defineVisitor = async (smsNumber: string, targetDepartment?: string) => { const visitor = await LivechatVisitors.findOneVisitorByPhone(smsNumber); - let data = { - token: (visitor && visitor.token) || Random.id(), + let data: { token: string; department?: string } = { + token: visitor?.token || Random.id(), }; if (!visitor) { @@ -61,7 +71,7 @@ const defineVisitor = async (smsNumber, targetDepartment) => { return LivechatVisitors.findOneEnabledById(id); }; -const normalizeLocationSharing = (payload) => { +const normalizeLocationSharing = (payload: ServiceData) => { const { extra: { fromLatitude: latitude, fromLongitude: longitude } = {} } = payload; if (!latitude || !longitude) { return; @@ -73,13 +83,14 @@ const normalizeLocationSharing = (payload) => { }; }; +// @ts-expect-error - this is an special endpoint that requires the return to not be wrapped as regular returns API.v1.addRoute('livechat/sms-incoming/:service', { async post() { if (!(await OmnichannelIntegration.isConfiguredSmsService(this.urlParams.service))) { return API.v1.failure('Invalid service'); } - const smsDepartment = settings.get('SMS_Default_Omnichannel_Department'); + const smsDepartment = settings.get('SMS_Default_Omnichannel_Department'); const SMSService = await OmnichannelIntegration.getSmsService(this.urlParams.service); const sms = SMSService.parse(this.bodyParams); const { department } = this.queryParams; @@ -89,34 +100,35 @@ API.v1.addRoute('livechat/sms-incoming/:service', { } const visitor = await defineVisitor(sms.from, targetDepartment); + if (!visitor) { + return API.v1.success(SMSService.error(new Error('Invalid visitor'))); + } + const { token } = visitor; const room = await LivechatRooms.findOneOpenByVisitorTokenAndDepartmentIdAndSource(token, targetDepartment, OmnichannelSourceType.SMS); const roomExists = !!room; const location = normalizeLocationSharing(sms); - const rid = (room && room._id) || Random.id(); + const rid = room?._id || Random.id(); - const sendMessage = { - guest: visitor, - roomInfo: { - sms: { - from: sms.to, - }, - source: { - type: OmnichannelSourceType.SMS, - alias: this.urlParams.service, - }, + const roomInfo = { + sms: { + from: sms.to, + }, + source: { + type: OmnichannelSourceType.SMS, + alias: this.urlParams.service, }, }; // create an empty room first place, so attachments have a place to live if (!roomExists) { - await LivechatTyped.getRoom(visitor, { rid, token, msg: '' }, sendMessage.roomInfo, undefined); + await LivechatTyped.getRoom(visitor, { rid, token, msg: '' }, roomInfo, undefined); } - let file; - let attachments; + let file: ILivechatMessage['file']; + const attachments: (MessageAttachment | undefined)[] = []; - const [media] = sms.media; + const [media] = sms?.media || []; if (media) { const { url: smsUrl, contentType } = media; const details = { @@ -126,40 +138,74 @@ API.v1.addRoute('livechat/sms-incoming/:service', { visitorToken: token, }; - let attachment; try { const uploadedFile = await getUploadFile(details, smsUrl); - file = { _id: uploadedFile._id, name: uploadedFile.name, type: uploadedFile.type }; - const fileUrl = FileUpload.getPath(`${file._id}/${encodeURI(file.name)}`); + file = { _id: uploadedFile._id, name: uploadedFile.name || 'file', type: uploadedFile.type }; + const fileUrl = FileUpload.getPath(`${file._id}/${encodeURI(file.name || 'file')}`); - attachment = { - title: file.name, - type: 'file', - description: file.description, - title_link: fileUrl, - }; + const fileType = file.type as string; + + if (/^image\/.+/.test(fileType)) { + const attachment: FileAttachmentProps = { + title: file.name, + type: 'file', + description: file.description, + title_link: fileUrl, + image_url: fileUrl, + image_type: fileType, + image_size: file.size, + }; + + if (file.identify?.size) { + attachment.image_dimensions = file?.identify.size; + } - if (/^image\/.+/.test(file.type)) { - attachment.image_url = fileUrl; - attachment.image_type = file.type; - attachment.image_size = file.size; - attachment.image_dimensions = file.identify != null ? file.identify.size : undefined; - } else if (/^audio\/.+/.test(file.type)) { - attachment.audio_url = fileUrl; - attachment.audio_type = file.type; - attachment.audio_size = file.size; - attachment.title_link_download = true; - } else if (/^video\/.+/.test(file.type)) { - attachment.video_url = fileUrl; - attachment.video_type = file.type; - attachment.video_size = file.size; - attachment.title_link_download = true; + attachments.push(attachment); + } else if (/^audio\/.+/.test(fileType)) { + const attachment: FileAttachmentProps = { + title: file.name, + type: 'file', + description: file.description, + title_link: fileUrl, + audio_url: fileUrl, + audio_type: fileType, + audio_size: file.size, + title_link_download: true, + }; + + attachments.push(attachment); + } else if (/^video\/.+/.test(fileType)) { + const attachment: FileAttachmentProps = { + title: file.name, + type: 'file', + description: file.description, + title_link: fileUrl, + video_url: fileUrl, + video_type: fileType, + video_size: file.size as number, + title_link_download: true, + }; + + attachments.push(attachment); } else { - attachment.title_link_download = true; + const attachment = { + title: file.name, + type: 'file', + description: file.description, + format: getFileExtension(file.name), + title_link: fileUrl, + title_link_download: true, + size: file.size as number, + }; + + attachments.push(attachment); } } catch (err) { logger.error({ msg: 'Attachment upload failed', err }); - attachment = { + const attachment = { + title: 'Attachment upload failed', + type: 'file', + description: 'An attachment was received, but upload to server failed', fields: [ { title: 'User upload failed', @@ -169,22 +215,35 @@ API.v1.addRoute('livechat/sms-incoming/:service', { ], color: 'yellow', }; + + attachments.push(attachment); } - attachments = [attachment]; } - sendMessage.message = { - _id: Random.id(), - rid, - token, - msg: sms.body, - ...(location && { location }), - ...(attachments && { attachments }), - ...(file && { file }), + const sendMessage: { + guest: ILivechatVisitor; + message: ILivechatMessage; + roomInfo: { + source?: IOmnichannelRoom['source']; + [key: string]: unknown; + }; + } = { + guest: visitor, + roomInfo, + message: { + _id: Random.id(), + rid, + token, + msg: sms.body, + ...(location && { location }), + ...(attachments && { attachments: attachments.filter((a: any): a is MessageAttachment => !!a) }), + ...(file && { file }), + }, }; try { - const msg = SMSService.response.call(this, await LivechatTyped.sendMessage(sendMessage)); + await LivechatTyped.sendMessage(sendMessage); + const msg = SMSService.response(); setImmediate(async () => { if (sms.extra) { if (sms.extra.fromCountry) { @@ -203,8 +262,8 @@ API.v1.addRoute('livechat/sms-incoming/:service', { }); return msg; - } catch (e) { - return SMSService.error.call(this, e); + } catch (e: any) { + return SMSService.error(e); } }, }); diff --git a/apps/meteor/app/livechat/server/api.ts b/apps/meteor/app/livechat/server/api.ts index 865b4308b941..9227a8508938 100644 --- a/apps/meteor/app/livechat/server/api.ts +++ b/apps/meteor/app/livechat/server/api.ts @@ -1,6 +1,6 @@ import '../imports/server/rest/agent'; import '../imports/server/rest/departments'; -import '../imports/server/rest/sms.js'; +import '../imports/server/rest/sms'; import '../imports/server/rest/users'; import '../imports/server/rest/upload'; import '../imports/server/rest/inquiries'; diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index d49bad222e3c..ea508b047882 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -103,29 +103,23 @@ type OfflineMessageData = { host?: string; }; +type UploadedFile = { + _id: string; + name?: string; + type?: string; + size?: number; + description?: string; + identify?: { size: { width: number; height: number } }; + format?: string; +}; + export interface ILivechatMessage { token: string; _id: string; rid: string; msg: string; - file?: { - _id: string; - name?: string; - type?: string; - size?: number; - description?: string; - identify?: { size: { width: number; height: number } }; - format?: string; - }; - files?: { - _id: string; - name?: string; - type?: string; - size?: number; - description?: string; - identify?: { size: { width: number; height: number } }; - format?: string; - }[]; + file?: UploadedFile; + files?: UploadedFile[]; attachments?: MessageAttachment[]; alias?: string; groupable?: boolean; @@ -368,7 +362,7 @@ class LivechatClass { async getRoom( guest: ILivechatVisitor, - message: Pick, + message: Pick, roomInfo: { source?: IOmnichannelRoom['source']; [key: string]: unknown; diff --git a/apps/meteor/app/livechat/server/lib/analytics/agents.js b/apps/meteor/app/livechat/server/lib/analytics/agents.ts similarity index 78% rename from apps/meteor/app/livechat/server/lib/analytics/agents.js rename to apps/meteor/app/livechat/server/lib/analytics/agents.ts index 5f89a4dc81e2..082404bd892e 100644 --- a/apps/meteor/app/livechat/server/lib/analytics/agents.js +++ b/apps/meteor/app/livechat/server/lib/analytics/agents.ts @@ -1,6 +1,12 @@ import { LivechatRooms, LivechatAgentActivity } from '@rocket.chat/models'; -export const findAllAverageServiceTimeAsync = async ({ start, end, options = {} }) => { +type Params = { + start: Date; + end: Date; + options?: any; +}; + +export const findAllAverageServiceTimeAsync = async ({ start, end, options = {} }: Params) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } @@ -19,7 +25,7 @@ export const findAllAverageServiceTimeAsync = async ({ start, end, options = {} }; }; -export const findAllServiceTimeAsync = async ({ start, end, options = {} }) => { +export const findAllServiceTimeAsync = async ({ start, end, options = {} }: Params) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } @@ -34,7 +40,17 @@ export const findAllServiceTimeAsync = async ({ start, end, options = {} }) => { }; }; -export const findAvailableServiceTimeHistoryAsync = async ({ start, end, fullReport, options = {} }) => { +export const findAvailableServiceTimeHistoryAsync = async ({ + start, + end, + fullReport, + options = {}, +}: { + start: string; + end: string; + fullReport: boolean; + options: { sort?: Record; offset?: number; count?: number }; +}) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } diff --git a/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts b/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts index cacf2c19d5e9..dd0d54970065 100644 --- a/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts +++ b/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts @@ -55,8 +55,8 @@ const getProductivityMetricsAsync = async ({ language: user?.language || settings.get('Language') || 'en', })) || []; const averageWaitingTime = await findAllAverageWaitingTimeAsync({ - start, - end, + start: new Date(start), + end: new Date(end), departmentId, }); @@ -95,8 +95,8 @@ const getAgentsProductivityMetricsAsync = async ({ }) )[0]; const averageOfServiceTime = await findAllAverageServiceTimeAsync({ - start, - end, + start: new Date(start), + end: new Date(end), departmentId, }); const totalizers = diff --git a/apps/meteor/app/livechat/server/lib/analytics/departments.js b/apps/meteor/app/livechat/server/lib/analytics/departments.ts similarity index 88% rename from apps/meteor/app/livechat/server/lib/analytics/departments.js rename to apps/meteor/app/livechat/server/lib/analytics/departments.ts index b8b8802bb85a..6d44752f16cf 100644 --- a/apps/meteor/app/livechat/server/lib/analytics/departments.js +++ b/apps/meteor/app/livechat/server/lib/analytics/departments.ts @@ -1,6 +1,13 @@ import { LivechatRooms, Messages } from '@rocket.chat/models'; -export const findAllRoomsAsync = async ({ start, end, answered, departmentId, options = {} }) => { +type Params = { + start: Date; + end: Date; + options?: any; + departmentId?: string; +}; + +export const findAllRoomsAsync = async ({ start, end, answered, departmentId, options = {} }: Params & { answered?: boolean }) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } @@ -23,7 +30,7 @@ export const findAllRoomsAsync = async ({ start, end, answered, departmentId, op }; }; -export const findAllAverageOfChatDurationTimeAsync = async ({ start, end, departmentId, options = {} }) => { +export const findAllAverageOfChatDurationTimeAsync = async ({ start, end, departmentId, options = {} }: Params) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } @@ -44,7 +51,7 @@ export const findAllAverageOfChatDurationTimeAsync = async ({ start, end, depart }; }; -export const findAllAverageServiceTimeAsync = async ({ start, end, departmentId, options = {} }) => { +export const findAllAverageServiceTimeAsync = async ({ start, end, departmentId, options = {} }: Params) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } @@ -65,7 +72,7 @@ export const findAllAverageServiceTimeAsync = async ({ start, end, departmentId, }; }; -export const findAllServiceTimeAsync = async ({ start, end, departmentId, options = {} }) => { +export const findAllServiceTimeAsync = async ({ start, end, departmentId, options = {} }: Params) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } @@ -86,7 +93,7 @@ export const findAllServiceTimeAsync = async ({ start, end, departmentId, option }; }; -export const findAllAverageWaitingTimeAsync = async ({ start, end, departmentId, options = {} }) => { +export const findAllAverageWaitingTimeAsync = async ({ start, end, departmentId, options = {} }: Params) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } @@ -107,7 +114,7 @@ export const findAllAverageWaitingTimeAsync = async ({ start, end, departmentId, }; }; -export const findAllNumberOfTransferredRoomsAsync = async ({ start, end, departmentId, options = {} }) => { +export const findAllNumberOfTransferredRoomsAsync = async ({ start, end, departmentId, options = {} }: Params) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } @@ -128,7 +135,7 @@ export const findAllNumberOfTransferredRoomsAsync = async ({ start, end, departm }; }; -export const findAllNumberOfAbandonedRoomsAsync = async ({ start, end, departmentId, options = {} }) => { +export const findAllNumberOfAbandonedRoomsAsync = async ({ start, end, departmentId, options = {} }: Params) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } @@ -139,7 +146,7 @@ export const findAllNumberOfAbandonedRoomsAsync = async ({ start, end, departmen }; }; -export const findPercentageOfAbandonedRoomsAsync = async ({ start, end, departmentId, options = {} }) => { +export const findPercentageOfAbandonedRoomsAsync = async ({ start, end, departmentId, options = {} }: Params) => { if (!start || !end) { throw new Error('"start" and "end" must be provided'); } diff --git a/apps/meteor/ee/app/livechat-enterprise/server/api/agents.ts b/apps/meteor/ee/app/livechat-enterprise/server/api/agents.ts index 2aa5b8878b6f..9eceae08e326 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/api/agents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/api/agents.ts @@ -94,17 +94,15 @@ API.v1.addRoute( if (isNaN(Date.parse(start))) { return API.v1.failure('The "start" query parameter must be a valid date.'); } - const startDate = new Date(start); if (isNaN(Date.parse(end))) { return API.v1.failure('The "end" query parameter must be a valid date.'); } - const endDate = new Date(end); const { agents, total } = await findAvailableServiceTimeHistoryAsync({ - start: startDate, - end: endDate, - fullReport: fullReport && fullReport === 'true', + start, + end, + fullReport: fullReport === 'true', options: { offset, count }, }); return API.v1.success({ diff --git a/apps/meteor/ee/app/livechat-enterprise/server/api/departments.ts b/apps/meteor/ee/app/livechat-enterprise/server/api/departments.ts index aac1e5c347dc..cd29d1b76c58 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/api/departments.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/api/departments.ts @@ -8,7 +8,6 @@ import { isLivechatAnalyticsDepartmentsTotalAbandonedChatsProps, isLivechatAnalyticsDepartmentsPercentageAbandonedChatsProps, } from '@rocket.chat/rest-typings'; -import { Match, check } from 'meteor/check'; import { API } from '../../../../../app/api/server'; import { getPaginationItems } from '../../../../../app/api/server/helpers/getPaginationItems'; @@ -45,7 +44,7 @@ API.v1.addRoute( const { departments, total } = await findAllRoomsAsync({ start: startDate, end: endDate, - answered: answered && answered === 'true', + answered: answered === 'true', departmentId, options: { offset, count }, }); @@ -189,10 +188,6 @@ API.v1.addRoute( const { start, end } = this.queryParams; const { departmentId } = this.queryParams; - check(start, String); - check(end, String); - check(departmentId, Match.Maybe(String)); - if (isNaN(Date.parse(start))) { return API.v1.failure('The "start" query parameter must be a valid date.'); } diff --git a/apps/meteor/server/models/raw/LivechatAgentActivity.ts b/apps/meteor/server/models/raw/LivechatAgentActivity.ts index 63563f571436..acf84bc39355 100644 --- a/apps/meteor/server/models/raw/LivechatAgentActivity.ts +++ b/apps/meteor/server/models/raw/LivechatAgentActivity.ts @@ -155,6 +155,22 @@ export class LivechatAgentActivityRaw extends BaseRaw im .toArray(); } + findAvailableServiceTimeHistory(p: { + start: string; + end: string; + fullReport: boolean; + onlyCount: true; + options?: { sort?: Record; offset?: number; count?: number }; + }): AggregationCursor<{ total: number }>; + + findAvailableServiceTimeHistory(p: { + start: string; + end: string; + fullReport: boolean; + onlyCount?: false; + options?: { sort?: Record; offset?: number; count?: number }; + }): AggregationCursor; + findAvailableServiceTimeHistory({ start, end, @@ -165,9 +181,9 @@ export class LivechatAgentActivityRaw extends BaseRaw im start: string; end: string; fullReport: boolean; - onlyCount: boolean; - options: any; - }): AggregationCursor { + onlyCount?: boolean; + options?: { sort?: Record; offset?: number; count?: number }; + }): AggregationCursor | AggregationCursor<{ total: number }> { const match = { $match: { date: { @@ -209,7 +225,7 @@ export class LivechatAgentActivityRaw extends BaseRaw im const params = [match, lookup, unwind, group, project, sort] as object[]; if (onlyCount) { params.push({ $count: 'total' }); - return this.col.aggregate(params); + return this.col.aggregate<{ total: number }>(params); } if (options.offset) { params.push({ $skip: options.offset }); @@ -217,6 +233,6 @@ export class LivechatAgentActivityRaw extends BaseRaw im if (options.count) { params.push({ $limit: options.count }); } - return this.col.aggregate(params, { allowDiskUse: true, readPreference: readSecondaryPreferred() }); + return this.col.aggregate(params, { allowDiskUse: true, readPreference: readSecondaryPreferred() }); } } diff --git a/apps/meteor/server/models/raw/LivechatRooms.ts b/apps/meteor/server/models/raw/LivechatRooms.ts index 94adf40569fd..1423476a708b 100644 --- a/apps/meteor/server/models/raw/LivechatRooms.ts +++ b/apps/meteor/server/models/raw/LivechatRooms.ts @@ -1891,7 +1891,7 @@ export class LivechatRoomsRaw extends BaseRaw implements ILive findOneOpenByVisitorTokenAndDepartmentIdAndSource( visitorToken: string, - departmentId: string, + departmentId?: string, source?: string, options: FindOptions = {}, ) { diff --git a/apps/meteor/server/models/raw/Messages.ts b/apps/meteor/server/models/raw/Messages.ts index 93ef8982a604..464555c94780 100644 --- a/apps/meteor/server/models/raw/Messages.ts +++ b/apps/meteor/server/models/raw/Messages.ts @@ -150,6 +150,34 @@ export class MessagesRaw extends BaseRaw implements IMessagesModel { return this.findPaginated(query, options); } + findAllNumberOfTransferredRooms({ + start, + end, + departmentId, + onlyCount, + options, + }: { + start: Date; + end: Date; + departmentId?: ILivechatDepartment['_id']; + onlyCount: true; + options?: PaginatedRequest; + }): AggregationCursor<{ total: number }>; + + findAllNumberOfTransferredRooms({ + start, + end, + departmentId, + onlyCount, + options, + }: { + start: Date; + end: Date; + departmentId?: ILivechatDepartment['_id']; + onlyCount?: false; + options?: PaginatedRequest; + }): AggregationCursor<{ _id: string | null; numberOfTransferredRooms: number }>; + findAllNumberOfTransferredRooms({ start, end, @@ -157,12 +185,12 @@ export class MessagesRaw extends BaseRaw implements IMessagesModel { onlyCount = false, options = {}, }: { - start: string; - end: string; - departmentId: ILivechatDepartment['_id']; - onlyCount: boolean; - options: PaginatedRequest; - }): AggregationCursor { + start: Date; + end: Date; + departmentId?: ILivechatDepartment['_id']; + onlyCount?: boolean; + options?: PaginatedRequest; + }): AggregationCursor<{ total: number }> | AggregationCursor<{ _id: string | null; numberOfTransferredRooms: number }> { // FIXME: aggregation type definitions const match = { $match: { @@ -211,7 +239,7 @@ export class MessagesRaw extends BaseRaw implements IMessagesModel { const params = [...firstParams, group, project, sort]; if (onlyCount) { params.push({ $count: 'total' }); - return this.col.aggregate(params, { readPreference: readSecondaryPreferred() }); + return this.col.aggregate<{ total: number }>(params, { readPreference: readSecondaryPreferred() }); } if (options.offset) { params.push({ $skip: options.offset }); @@ -219,7 +247,10 @@ export class MessagesRaw extends BaseRaw implements IMessagesModel { if (options.count) { params.push({ $limit: options.count }); } - return this.col.aggregate(params, { allowDiskUse: true, readPreference: readSecondaryPreferred() }); + return this.col.aggregate<{ _id: string | null; numberOfTransferredRooms: number }>(params, { + allowDiskUse: true, + readPreference: readSecondaryPreferred(), + }); } getTotalOfMessagesSentByDate({ start, end, options = {} }: { start: Date; end: Date; options?: PaginatedRequest }): Promise { diff --git a/packages/model-typings/src/models/ILivechatAgentActivityModel.ts b/packages/model-typings/src/models/ILivechatAgentActivityModel.ts index 9fb4d90fbe0f..8d14e3b6e423 100644 --- a/packages/model-typings/src/models/ILivechatAgentActivityModel.ts +++ b/packages/model-typings/src/models/ILivechatAgentActivityModel.ts @@ -26,11 +26,19 @@ export interface ILivechatAgentActivityModel extends IBaseModel; - findAvailableServiceTimeHistory(params: { + findAvailableServiceTimeHistory(p: { start: string; end: string; fullReport: boolean; - onlyCount: boolean; - options: any; + onlyCount: true; + options?: { sort?: Record; offset?: number; count?: number }; + }): AggregationCursor<{ total: number }>; + + findAvailableServiceTimeHistory(p: { + start: string; + end: string; + fullReport: boolean; + onlyCount?: false; + options?: { sort?: Record; offset?: number; count?: number }; }): AggregationCursor; } diff --git a/packages/model-typings/src/models/ILivechatRoomsModel.ts b/packages/model-typings/src/models/ILivechatRoomsModel.ts index 2b380505c38a..a228a4fea864 100644 --- a/packages/model-typings/src/models/ILivechatRoomsModel.ts +++ b/packages/model-typings/src/models/ILivechatRoomsModel.ts @@ -176,7 +176,7 @@ export interface ILivechatRoomsModel extends IBaseModel { findOneOpenByVisitorToken(visitorToken: string, options?: FindOptions): Promise; findOneOpenByVisitorTokenAndDepartmentIdAndSource( visitorToken: string, - departmentId: string, + departmentId?: string, source?: string, options?: FindOptions, ): Promise; diff --git a/packages/model-typings/src/models/IMessagesModel.ts b/packages/model-typings/src/models/IMessagesModel.ts index 9b7680b56120..143080799671 100644 --- a/packages/model-typings/src/models/IMessagesModel.ts +++ b/packages/model-typings/src/models/IMessagesModel.ts @@ -23,6 +23,13 @@ import type { import type { FindPaginated, IBaseModel } from './IBaseModel'; +type PaginatedRequest = { + count?: number; + offset?: number; + sort?: `{ "${S}": ${1 | -1} }` | string; + /* deprecated */ + query?: string; +}; export interface IMessagesModel extends IBaseModel { findPaginatedVisibleByMentionAndRoomId( username: IUser['username'], @@ -44,13 +51,21 @@ export interface IMessagesModel extends IBaseModel { findDiscussionsByRoomAndText(rid: IRoom['_id'], text: string, options?: FindOptions): FindPaginated>; - findAllNumberOfTransferredRooms(params: { - start: string; - end: string; - departmentId: ILivechatDepartment['_id']; - onlyCount: boolean; - options: any; - }): AggregationCursor; + findAllNumberOfTransferredRooms(p: { + start: Date; + end: Date; + departmentId?: ILivechatDepartment['_id']; + onlyCount: true; + options?: PaginatedRequest; + }): AggregationCursor<{ total: number }>; + + findAllNumberOfTransferredRooms(p: { + start: Date; + end: Date; + departmentId?: ILivechatDepartment['_id']; + onlyCount?: false; + options?: PaginatedRequest; + }): AggregationCursor<{ _id: string | null; numberOfTransferredRooms: number }>; getTotalOfMessagesSentByDate(params: { start: Date; end: Date; options?: any }): Promise; diff --git a/packages/rest-typings/src/v1/omnichannel.ts b/packages/rest-typings/src/v1/omnichannel.ts index 22fc7e837f7e..d0338e3ea986 100644 --- a/packages/rest-typings/src/v1/omnichannel.ts +++ b/packages/rest-typings/src/v1/omnichannel.ts @@ -25,6 +25,7 @@ import type { ILivechatTriggerAction, ReportResult, ReportWithUnmatchingElements, + SMSProviderResponse, } from '@rocket.chat/core-typings'; import { ILivechatAgentStatus } from '@rocket.chat/core-typings'; import Ajv from 'ajv'; @@ -3717,6 +3718,9 @@ export type OmnichannelEndpoints = { value: string | number; }[]; }; + '/v1/livechat/sms-incoming/:service': { + POST: (params: unknown) => SMSProviderResponse; + }; } & { // EE '/v1/livechat/analytics/agents/average-service-time': {