diff --git a/apps/meteor/app/apps/server/bridges/livechat.ts b/apps/meteor/app/apps/server/bridges/livechat.ts index 4e2f8e69acaddb0a714fef2a13c9fe6fecffb585..ec5cff29a99be37d90ba040c950ffc52a783623f 100644 --- a/apps/meteor/app/apps/server/bridges/livechat.ts +++ b/apps/meteor/app/apps/server/bridges/livechat.ts @@ -246,7 +246,8 @@ export class AppLivechatBridge extends LivechatBridge { username, name, type, - }; + userType: 'user', + } as const; let userId; let transferredTo; diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index 199722a0d8961c24651a3f9fd01b933bcbb5b4ac..d7dae512e9a8f9ea89642532984c356fc1927bc9 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -1,3 +1,4 @@ +import { isMessageFromVisitor } from '@rocket.chat/core-typings'; import { Messages, Rooms, Users } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; @@ -107,16 +108,19 @@ export class AppMessagesConverter { return undefined; } - let user = await cache.get('user')(message.u._id); - - // When the sender of the message is a Guest (livechat) and not a user - if (!user) { - user = this.orch.getConverters().get('users').convertToApp(message.u); - } + // When the message contains token, means the message is from the visitor(omnichannel) + const user = await (isMessageFromVisitor(msgObj) + ? this.orch.getConverters().get('users').convertToApp(message.u) + : cache.get('user')(message.u._id)); delete message.u; - return user; + /** + * Old System Messages from visitor doesn't have the `token` field, to not return + * `sender` as undefined, so we need to add this fallback here. + */ + + return user || this.orch.getConverters().get('users').convertToApp(message.u); }, }; diff --git a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts index 44654428ae8fb5a9244dc709531f748c40abc6c2..49fcc0ea4725cbc97659a9752dcffac26f295b25 100644 --- a/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts +++ b/apps/meteor/app/lib/server/lib/sendNotificationsOnMessage.ts @@ -266,7 +266,7 @@ export async function sendMessageNotifications(message: IMessage, room: IRoom, u return; } - const sender = await roomCoordinator.getRoomDirectives(room.t).getMsgSender(message.u._id); + const sender = await roomCoordinator.getRoomDirectives(room.t).getMsgSender(message); if (!sender) { return message; } diff --git a/apps/meteor/app/livechat/server/api/v1/room.ts b/apps/meteor/app/livechat/server/api/v1/room.ts index 94674f801ad5ed1087a95bc505a25ffaecc91b64..b0f45a63ff87db165b8f99d8ac1a3895cfff60d1 100644 --- a/apps/meteor/app/livechat/server/api/v1/room.ts +++ b/apps/meteor/app/livechat/server/api/v1/room.ts @@ -293,8 +293,7 @@ API.v1.addRoute( throw new Error('error-invalid-visitor'); } - const transferedBy = this.user satisfies TransferByData; - transferData.transferredBy = normalizeTransferredByData(transferedBy, room); + transferData.transferredBy = normalizeTransferredByData(this.user, room); if (transferData.userId) { const userToTransfer = await Users.findOneById(transferData.userId); if (userToTransfer) { diff --git a/apps/meteor/app/livechat/server/lib/Helper.ts b/apps/meteor/app/livechat/server/lib/Helper.ts index 01bb2ff34e9aaabef7a4d266d18ef334002a16ac..c0e85a8c7c2b2977392f8c20fba0f855e33963f0 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.ts +++ b/apps/meteor/app/livechat/server/lib/Helper.ts @@ -142,7 +142,9 @@ export const createLivechatRoom = async < await callbacks.run('livechat.newRoom', room); - await sendMessage(guest, { t: 'livechat-started', msg: '', groupable: false }, room); + // TODO: replace with `Message.saveSystemMessage` + + await sendMessage(guest, { t: 'livechat-started', msg: '', groupable: false, token: guest.token }, room); return result.value as IOmnichannelRoom; }; @@ -646,6 +648,7 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi '', { _id, username }, { + ...(transferData.transferredBy.userType === 'visitor' && { token: room.v.token }), transferData: { ...transferData, prevDepartment: transferData.originalDepartmentName, @@ -681,18 +684,23 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi return true; }; -export const normalizeTransferredByData = (transferredBy: TransferByData, room: IOmnichannelRoom) => { +type MakePropertyOptional<T, K extends keyof T> = Omit<T, K> & { [P in K]?: T[P] }; + +export const normalizeTransferredByData = ( + transferredBy: MakePropertyOptional<TransferByData, 'userType'>, + room: IOmnichannelRoom, +): TransferByData => { if (!transferredBy || !room) { throw new Error('You must provide "transferredBy" and "room" params to "getTransferredByData"'); } const { servedBy: { _id: agentId } = {} } = room; const { _id, username, name, userType: transferType } = transferredBy; - const type = transferType || (_id === agentId ? 'agent' : 'user'); + const userType = transferType || (_id === agentId ? 'agent' : 'user'); return { _id, username, ...(name && { name }), - type, + userType, }; }; diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 800e32d85ab4b3c9632251325bdd6205571e91e9..82c4dff4c485898a2fa287f1378d93e44b934742 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -328,13 +328,6 @@ class LivechatClass { this.logger.debug(`DB updated for room ${room._id}`); - const message = { - t: 'livechat-close', - msg: comment, - groupable: false, - transcriptRequested: !!transcriptRequest, - }; - // Retrieve the closed room const newRoom = await LivechatRooms.findOneById(rid); @@ -343,7 +336,17 @@ class LivechatClass { } this.logger.debug(`Sending closing message to room ${room._id}`); - await sendMessage(chatCloser, message, newRoom); + await sendMessage( + chatCloser, + { + t: 'livechat-close', + msg: comment, + groupable: false, + transcriptRequested: !!transcriptRequest, + ...(isRoomClosedByVisitorParams(params) && { token: chatCloser.token }), + }, + newRoom, + ); await Message.saveSystemMessage('command', rid, 'promptTranscript', closeData.closedBy); @@ -769,7 +772,9 @@ class LivechatClass { visitorDataToUpdate.visitorEmails = [{ address: visitorEmail }]; } - if (department) { + const livechatVisitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); + + if (livechatVisitor?.department !== department && department) { Livechat.logger.debug(`Attempt to find a department with id/name ${department}`); const dep = await LivechatDepartment.findOneByIdOrName(department, { projection: { _id: 1 } }); if (!dep) { @@ -780,8 +785,6 @@ class LivechatClass { visitorDataToUpdate.department = dep._id; } - const livechatVisitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); - visitorDataToUpdate.token = livechatVisitor?.token || token; let existingUser = null; @@ -1324,7 +1327,7 @@ class LivechatClass { if (guest.name) { message.alias = guest.name; } - return Object.assign(await sendMessage(guest, message, room), { + return Object.assign(await sendMessage(guest, { ...message, token: guest.token }, room), { newRoom, showConnecting: this.showConnecting(), }); @@ -1443,7 +1446,7 @@ class LivechatClass { _id: String, username: String, name: Match.Maybe(String), - type: String, + userType: String, }), ); @@ -1451,34 +1454,31 @@ class LivechatClass { const scopeData = scope || (nextDepartment ? 'department' : 'agent'); this.logger.info(`Storing new chat transfer of ${room._id} [Transfered by: ${_id} to ${scopeData}]`); - const transfer = { - transferData: { - transferredBy, + await sendMessage( + transferredBy, + { + t: 'livechat_transfer_history', + rid: room._id, ts: new Date(), - scope: scopeData, - comment, - ...(previousDepartment && { previousDepartment }), - ...(nextDepartment && { nextDepartment }), - ...(transferredTo && { transferredTo }), - }, - }; - - const type = 'livechat_transfer_history'; - const transferMessage = { - t: type, - rid: room._id, - ts: new Date(), - msg: '', - u: { - _id, - username, + msg: '', + u: { + _id, + username, + }, + groupable: false, + ...(transferData.transferredBy.userType === 'visitor' && { token: room.v.token }), + transferData: { + transferredBy, + ts: new Date(), + scope: scopeData, + comment, + ...(previousDepartment && { previousDepartment }), + ...(nextDepartment && { nextDepartment }), + ...(transferredTo && { transferredTo }), + }, }, - groupable: false, - }; - - Object.assign(transferMessage, transfer); - - await sendMessage(transferredBy, transferMessage, room); + room, + ); } async saveGuest(guestData: Pick<ILivechatVisitor, '_id' | 'name' | 'livechatData'> & { email?: string; phone?: string }, userId: string) { diff --git a/apps/meteor/definition/IRoomTypeConfig.ts b/apps/meteor/definition/IRoomTypeConfig.ts index 8ac27be14caf527116061542f0239e660d44955a..12a4bea39f056d5c062177d4e012a3a40a213884 100644 --- a/apps/meteor/definition/IRoomTypeConfig.ts +++ b/apps/meteor/definition/IRoomTypeConfig.ts @@ -106,7 +106,7 @@ export interface IRoomTypeServerDirectives { notificationMessage: string, userId: string, ) => Promise<{ title: string | undefined; text: string; name: string | undefined }>; - getMsgSender: (senderId: IUser['_id']) => Promise<IUser | null>; + getMsgSender: (message: IMessage) => Promise<IUser | null>; includeInRoomSearch: () => boolean; getReadReceiptsExtraData: (message: IMessage) => Partial<ReadReceipt>; includeInDashboard: () => boolean; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/AutoTransferChatScheduler.ts b/apps/meteor/ee/app/livechat-enterprise/server/lib/AutoTransferChatScheduler.ts index 80af04df64728896bc6f95a295f7baa484bbfe66..d8fb08a3e28241de4bc0350e3fd827b1e6a05d0d 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/AutoTransferChatScheduler.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/AutoTransferChatScheduler.ts @@ -116,7 +116,7 @@ class AutoTransferChatSchedulerClass { await forwardRoomToAgent(room, { userId: agent.agentId, - transferredBy, + transferredBy: { ...transferredBy, userType: 'user' }, transferredTo: agent, scope: 'autoTransferUnansweredChatsToAgent', comment: timeoutDuration, diff --git a/apps/meteor/server/lib/rooms/roomCoordinator.ts b/apps/meteor/server/lib/rooms/roomCoordinator.ts index c640b8fec4b8297e10ae9f267a893ec8acdae24d..4f4242f5a34aab2bf6e41f3e3a024eb77602115e 100644 --- a/apps/meteor/server/lib/rooms/roomCoordinator.ts +++ b/apps/meteor/server/lib/rooms/roomCoordinator.ts @@ -50,8 +50,8 @@ class RoomCoordinatorServer extends RoomCoordinator { return { title, text, name: room.name }; }, - getMsgSender(senderId: IUser['_id']): Promise<IUser | null> { - return Users.findOneById(senderId); + getMsgSender(message: IMessage): Promise<IUser | null> { + return Users.findOneById(message.u._id); }, includeInRoomSearch(): boolean { return false; diff --git a/apps/meteor/server/lib/rooms/roomTypes/livechat.ts b/apps/meteor/server/lib/rooms/roomTypes/livechat.ts index a6b870042696110a9afe9e3ce8baebeb45fee1e3..7a00f75796c02e02fa5aaa352546fc70a25ca1b6 100644 --- a/apps/meteor/server/lib/rooms/roomTypes/livechat.ts +++ b/apps/meteor/server/lib/rooms/roomTypes/livechat.ts @@ -1,4 +1,5 @@ import type { AtLeast, ValueOf } from '@rocket.chat/core-typings'; +import { isMessageFromVisitor } from '@rocket.chat/core-typings'; import { LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; import { RoomSettingsEnum, RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; @@ -38,8 +39,10 @@ roomCoordinator.add(LivechatRoomType, { return { title, text, name: roomName }; }, - async getMsgSender(senderId) { - return LivechatVisitors.findOneEnabledById(senderId); + async getMsgSender(message) { + if (isMessageFromVisitor(message)) { + return LivechatVisitors.findOneEnabledById(message.u._id); + } }, getReadReceiptsExtraData(message) { diff --git a/apps/meteor/server/lib/rooms/roomTypes/voip.ts b/apps/meteor/server/lib/rooms/roomTypes/voip.ts index 7925cd65e30297f93cd489b4f961df22aaebba48..c7cb26b476b48635c2eb316dace46bfd5b97f4bd 100644 --- a/apps/meteor/server/lib/rooms/roomTypes/voip.ts +++ b/apps/meteor/server/lib/rooms/roomTypes/voip.ts @@ -19,7 +19,7 @@ roomCoordinator.add(VoipRoomType, { return { title, text, name: room.name }; }, - async getMsgSender(senderId) { - return Users.findOneById(senderId); + async getMsgSender(message) { + return Users.findOneById(message.u._id); }, } as AtLeast<IRoomTypeServerDirectives, 'roomName'>); diff --git a/packages/core-typings/src/IMessage/IMessage.ts b/packages/core-typings/src/IMessage/IMessage.ts index 1812dfb6dad362ee6baf0a9d02bafddca6962de8..694225dc71a47ffbc3fc8cfe0d5d98a6a2d3f299 100644 --- a/packages/core-typings/src/IMessage/IMessage.ts +++ b/packages/core-typings/src/IMessage/IMessage.ts @@ -401,3 +401,9 @@ export type IMessageWithPendingFileImport = IMessage & { downloaded?: boolean; }; }; + +export interface IMessageFromVisitor extends IMessage { + token: string; +} + +export const isMessageFromVisitor = (message: IMessage): message is IMessageFromVisitor => 'token' in message; diff --git a/packages/core-typings/src/omnichannel/routing.ts b/packages/core-typings/src/omnichannel/routing.ts index af27f221c2b17c95ab88231c5cfeaa0da042acd8..526c88eb3d5aba7c7965d5b732eb1988ee0f1272 100644 --- a/packages/core-typings/src/omnichannel/routing.ts +++ b/packages/core-typings/src/omnichannel/routing.ts @@ -27,10 +27,7 @@ export type TransferData = { userId?: string; departmentId?: string; department?: Pick<ILivechatDepartment, '_id' | 'name'>; - transferredBy: { - _id: string; - username?: string; - }; + transferredBy: TransferByData; transferredTo?: { username?: string; name?: string; @@ -47,5 +44,5 @@ export type TransferByData = { _id: string; username?: string; name?: string; - userType?: 'agent' | 'user' | 'visitor'; + userType: 'agent' | 'user' | 'visitor'; };