From 105846110538d219c301054a9f47f49877c413b1 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 2 Aug 2024 12:43:13 -0300 Subject: [PATCH] chore: create afterOmnichannelSaveMessage --- .../hooks/afterSaveOmnichannelMessage.ts | 16 ++++ .../app/livechat/server/hooks/leadCapture.ts | 10 +-- .../server/hooks/markRoomNotResponded.ts | 12 +-- .../server/hooks/markRoomResponded.ts | 10 +-- .../server/hooks/saveAnalyticsData.ts | 13 +--- .../server/hooks/saveLastMessageToInquiry.ts | 8 +- .../server/hooks/saveLastVisitorMessageTs.ts | 11 +-- .../app/livechat/server/hooks/sendToCRM.ts | 9 +-- apps/meteor/app/livechat/server/index.ts | 1 + .../app/livechat/server/sendMessageBySMS.ts | 8 +- .../server/hooks/resumeOnHold.ts | 63 ++++++++-------- .../server/hooks/scheduleAutoTransfer.ts | 74 +++++++++---------- .../setPredictedVisitorAbandonmentTime.ts | 10 +-- apps/meteor/lib/callbacks.ts | 1 + 14 files changed, 118 insertions(+), 128 deletions(-) create mode 100644 apps/meteor/app/livechat/server/hooks/afterSaveOmnichannelMessage.ts diff --git a/apps/meteor/app/livechat/server/hooks/afterSaveOmnichannelMessage.ts b/apps/meteor/app/livechat/server/hooks/afterSaveOmnichannelMessage.ts new file mode 100644 index 000000000000..8badfb07177e --- /dev/null +++ b/apps/meteor/app/livechat/server/hooks/afterSaveOmnichannelMessage.ts @@ -0,0 +1,16 @@ +import { isOmnichannelRoom } from '@rocket.chat/core-typings'; + +import { callbacks } from '../../../../lib/callbacks'; + +callbacks.add( + 'afterSaveMessage', + async (message, room) => { + // only call webhook if it is a livechat room + if (!isOmnichannelRoom(room)) { + return message; + } + return callbacks.run('afterOmnichannelSaveMessage', message, { room }); + }, + callbacks.priority.MEDIUM, + 'after-omnichannel-save-message', +); diff --git a/apps/meteor/app/livechat/server/hooks/leadCapture.ts b/apps/meteor/app/livechat/server/hooks/leadCapture.ts index 4b987c00c02e..6a3826b8ba11 100644 --- a/apps/meteor/app/livechat/server/hooks/leadCapture.ts +++ b/apps/meteor/app/livechat/server/hooks/leadCapture.ts @@ -1,5 +1,5 @@ import type { IMessage, IOmnichannelRoom } from '@rocket.chat/core-typings'; -import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatVisitors } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; @@ -31,12 +31,8 @@ function validateMessage(message: IMessage, room: IOmnichannelRoom) { } callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!isOmnichannelRoom(room)) { - return message; - } - + 'afterOmnichannelSaveMessage', + async (message, { room }) => { if (!validateMessage(message, room)) { return message; } diff --git a/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts b/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts index f0bfb8574e6a..23131cee60a2 100644 --- a/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts +++ b/apps/meteor/app/livechat/server/hooks/markRoomNotResponded.ts @@ -1,15 +1,11 @@ -import { isOmnichannelRoom, isEditedMessage } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatRooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!isOmnichannelRoom(room)) { - return message; - } - + 'afterOmnichannelSaveMessage', + async (message, { room }) => { // skips this callback if the message was edited if (!message || isEditedMessage(message)) { return message; @@ -21,7 +17,7 @@ callbacks.add( } // check if room is yet awaiting for response - if (typeof room.t !== 'undefined' && room.t === 'l' && room.waitingResponse) { + if (room.waitingResponse) { return message; } diff --git a/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts b/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts index 48ec985aa42c..3e9164554d47 100644 --- a/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts +++ b/apps/meteor/app/livechat/server/hooks/markRoomResponded.ts @@ -1,5 +1,5 @@ import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; -import { isOmnichannelRoom, isEditedMessage } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatRooms, LivechatVisitors, LivechatInquiry } from '@rocket.chat/models'; import moment from 'moment'; @@ -7,12 +7,8 @@ import { callbacks } from '../../../../lib/callbacks'; import { notifyOnLivechatInquiryChanged } from '../../../lib/server/lib/notifyListener'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!isOmnichannelRoom(room)) { - return message; - } - + 'afterOmnichannelSaveMessage', + async (message, { room }) => { // skips this callback if the message was edited if (!message || isEditedMessage(message)) { return message; diff --git a/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts b/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts index e92e6b4d940b..7c7bff067f9a 100644 --- a/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts +++ b/apps/meteor/app/livechat/server/hooks/saveAnalyticsData.ts @@ -1,24 +1,19 @@ -import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isEditedMessage, isMessageFromVisitor } from '@rocket.chat/core-typings'; import { LivechatRooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; import { normalizeMessageFileUpload } from '../../../utils/server/functions/normalizeMessageFileUpload'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { - // check if room is livechat - if (!isOmnichannelRoom(room)) { - return message; - } - + 'afterOmnichannelSaveMessage', + async (message, { room }) => { // skips this callback if the message was edited if (!message || isEditedMessage(message)) { return message; } // if the message has a token, it was sent by the visitor - if (message.token) { + if (isMessageFromVisitor(message)) { // When visitor sends a mesage, most metrics wont be calculated/served. // But, v.lq (last query) will be updated to the message time. This has to be done // As not doing it will cause the metrics to be crazy and not have real values. diff --git a/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts b/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts index e65f1d99b884..1925e135a562 100644 --- a/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts +++ b/apps/meteor/app/livechat/server/hooks/saveLastMessageToInquiry.ts @@ -1,4 +1,4 @@ -import { isOmnichannelRoom, isEditedMessage } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatInquiry } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; @@ -7,9 +7,9 @@ import { settings } from '../../../settings/server'; import { RoutingManager } from '../lib/RoutingManager'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!isOmnichannelRoom(room) || isEditedMessage(message) || message.t) { + 'afterOmnichannelSaveMessage', + async (message, { room }) => { + if (isEditedMessage(message) || message.t) { return message; } diff --git a/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts b/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts index 4bc28c3990ba..bdaedfed19f2 100644 --- a/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts +++ b/apps/meteor/app/livechat/server/hooks/saveLastVisitorMessageTs.ts @@ -1,18 +1,15 @@ -import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isMessageFromVisitor } from '@rocket.chat/core-typings'; import { LivechatRooms } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!(isOmnichannelRoom(room) && room.v.token)) { - return message; - } + 'afterOmnichannelSaveMessage', + async (message, { room }) => { if (message.t) { return message; } - if (!message.token) { + if (!isMessageFromVisitor(message)) { return message; } diff --git a/apps/meteor/app/livechat/server/hooks/sendToCRM.ts b/apps/meteor/app/livechat/server/hooks/sendToCRM.ts index 24e1d685a0e6..b3624bd3ecf6 100644 --- a/apps/meteor/app/livechat/server/hooks/sendToCRM.ts +++ b/apps/meteor/app/livechat/server/hooks/sendToCRM.ts @@ -261,13 +261,8 @@ callbacks.add( ); callbacks.add( - 'afterSaveMessage', - async (message, room) => { - // only call webhook if it is a livechat room - if (!isOmnichannelRoom(room) || !room?.v?.token) { - return message; - } - + 'afterOmnichannelSaveMessage', + async (message, { room }) => { // if the message has a token, it was sent from the visitor // if not, it was sent from the agent if (message.token && !settings.get('Livechat_webhook_on_visitor_message')) { diff --git a/apps/meteor/app/livechat/server/index.ts b/apps/meteor/app/livechat/server/index.ts index fc96f2a921a9..9a1f40238df5 100644 --- a/apps/meteor/app/livechat/server/index.ts +++ b/apps/meteor/app/livechat/server/index.ts @@ -16,6 +16,7 @@ import './hooks/saveContactLastChat'; import './hooks/saveLastMessageToInquiry'; import './hooks/afterUserActions'; import './hooks/afterAgentRemoved'; +import './hooks/afterSaveOmnichannelMessage'; import './methods/addAgent'; import './methods/addManager'; import './methods/changeLivechatStatus'; diff --git a/apps/meteor/app/livechat/server/sendMessageBySMS.ts b/apps/meteor/app/livechat/server/sendMessageBySMS.ts index 2557fcdeb83d..c7f88646158b 100644 --- a/apps/meteor/app/livechat/server/sendMessageBySMS.ts +++ b/apps/meteor/app/livechat/server/sendMessageBySMS.ts @@ -1,5 +1,5 @@ import { OmnichannelIntegration } from '@rocket.chat/core-services'; -import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatVisitors } from '@rocket.chat/models'; import { callbacks } from '../../../lib/callbacks'; @@ -8,8 +8,8 @@ import { normalizeMessageFileUpload } from '../../utils/server/functions/normali import { callbackLogger } from './lib/logger'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { + 'afterOmnichannelSaveMessage', + async (message, { room }) => { // skips this callback if the message was edited if (isEditedMessage(message)) { return message; @@ -20,7 +20,7 @@ callbacks.add( } // only send the sms by SMS if it is a livechat room with SMS set to true - if (!(isOmnichannelRoom(room) && room.sms && room.v && room.v.token)) { + if (!(room.sms && room.v && room.v.token)) { return message; } diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/resumeOnHold.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/resumeOnHold.ts index 8a04166e1b72..249f988c7684 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/resumeOnHold.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/resumeOnHold.ts @@ -1,6 +1,6 @@ import { OmnichannelEEService } from '@rocket.chat/core-services'; -import type { ILivechatVisitor, IMessage, IOmnichannelRoom, IRoom, IUser } from '@rocket.chat/core-typings'; -import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import type { ILivechatVisitor, IMessage, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings'; +import { isMessageFromVisitor, isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatRooms, LivechatVisitors, Users } from '@rocket.chat/models'; import { callbackLogger } from '../../../../../app/livechat/server/lib/logger'; @@ -16,7 +16,7 @@ const resumeOnHoldCommentAndUser = async (room: IOmnichannelRoom): Promise<{ com projection: { name: 1, username: 1 }, }); if (!visitor) { - callbackLogger.error(`[afterSaveMessage] Visitor Not found for room ${rid} while trying to resume on hold`); + callbackLogger.error(`[afterOmnichannelSaveMessage] Visitor Not found for room ${rid} while trying to resume on hold`); throw new Error('Visitor not found while trying to resume on hold'); } @@ -26,43 +26,46 @@ const resumeOnHoldCommentAndUser = async (room: IOmnichannelRoom): Promise<{ com const resumedBy = await Users.findOneById('rocket.cat'); if (!resumedBy) { - callbackLogger.error(`[afterSaveMessage] User Not found for room ${rid} while trying to resume on hold`); + callbackLogger.error(`[afterOmnichannelSaveMessage] User Not found for room ${rid} while trying to resume on hold`); throw new Error(`User not found while trying to resume on hold`); } return { comment: resumeChatComment, resumedBy }; }; -const handleAfterSaveMessage = async (message: IMessage, room: IRoom) => { - if (isEditedMessage(message) || message.t || !isOmnichannelRoom(room)) { - return message; - } +callbacks.add( + 'afterOmnichannelSaveMessage', + async (message: IMessage, { room }) => { + if (isEditedMessage(message) || message.t) { + return message; + } - const { _id: rid, v: roomVisitor } = room; + const { _id: rid, v: roomVisitor } = room; - if (!roomVisitor?._id) { - return message; - } - - // Need to read the room every time, the room object is not updated - const updatedRoom = await LivechatRooms.findOneById(rid); - if (!updatedRoom) { - return message; - } - - if (message.token && room.onHold) { - callbackLogger.debug(`[afterSaveMessage] Room ${rid} is on hold, resuming it now since visitor sent a message`); + if (!roomVisitor?._id) { + return message; + } - try { - const { comment: resumeChatComment, resumedBy } = await resumeOnHoldCommentAndUser(updatedRoom); - await OmnichannelEEService.resumeRoomOnHold(updatedRoom, resumeChatComment, resumedBy); - } catch (error) { - callbackLogger.error(`[afterSaveMessage] Error while resuming room ${rid} on hold: Error: `, error); + // Need to read the room every time, the room object is not updated + const updatedRoom = await LivechatRooms.findOneById(rid); + if (!updatedRoom) { return message; } - } - return message; -}; + if (isMessageFromVisitor(message) && room.onHold) { + callbackLogger.debug(`[afterOmnichannelSaveMessage] Room ${rid} is on hold, resuming it now since visitor sent a message`); + + try { + const { comment: resumeChatComment, resumedBy } = await resumeOnHoldCommentAndUser(updatedRoom); + await OmnichannelEEService.resumeRoomOnHold(updatedRoom, resumeChatComment, resumedBy); + } catch (error) { + callbackLogger.error(`[afterOmnichannelSaveMessage] Error while resuming room ${rid} on hold: Error: `, error); + return message; + } + } -callbacks.add('afterSaveMessage', handleAfterSaveMessage, callbacks.priority.HIGH, 'livechat-resume-on-hold'); + return message; + }, + callbacks.priority.HIGH, + 'livechat-resume-on-hold', +); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/scheduleAutoTransfer.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/scheduleAutoTransfer.ts index fdf980c311ab..c0f4b1b9da1d 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/scheduleAutoTransfer.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/scheduleAutoTransfer.ts @@ -1,5 +1,4 @@ -import type { IMessage, IOmnichannelRoom, IRoom } from '@rocket.chat/core-typings'; -import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import type { IMessage, IOmnichannelRoom } from '@rocket.chat/core-typings'; import type { CloseRoomParams } from '../../../../../app/livechat/server/lib/LivechatTyped'; import { settings } from '../../../../../app/settings/server'; @@ -14,40 +13,6 @@ type LivechatCloseCallbackParams = { let autoTransferTimeout = 0; -const handleAfterSaveMessage = async (message: IMessage, room: IRoom | undefined): Promise => { - if (!room || !isOmnichannelRoom(room)) { - return message; - } - - const { _id: rid, autoTransferredAt, autoTransferOngoing } = room; - const { token, t: messageType } = message; - - if (messageType) { - // ignore system messages - return message; - } - - if (!autoTransferTimeout || autoTransferTimeout <= 0) { - return message; - } - - if (!message || token) { - // ignore messages from visitors - return message; - } - - if (autoTransferredAt) { - return message; - } - - if (!autoTransferOngoing) { - return message; - } - - await AutoTransferChatScheduler.unscheduleRoom(rid); - return message; -}; - const handleAfterCloseRoom = async (params: LivechatCloseCallbackParams): Promise => { const { room } = params; @@ -73,7 +38,7 @@ settings.watch('Livechat_auto_transfer_chat_timeout', (value) => { autoTransferTimeout = value as number; if (!autoTransferTimeout || autoTransferTimeout === 0) { callbacks.remove('livechat.afterTakeInquiry', 'livechat-auto-transfer-job-inquiry'); - callbacks.remove('afterSaveMessage', 'livechat-cancel-auto-transfer-job-after-message'); + callbacks.remove('afterOmnichannelSaveMessage', 'livechat-cancel-auto-transfer-job-after-message'); callbacks.remove('livechat.closeRoom', 'livechat-cancel-auto-transfer-on-close-room'); return; } @@ -98,6 +63,39 @@ settings.watch('Livechat_auto_transfer_chat_timeout', (value) => { callbacks.priority.MEDIUM, 'livechat-auto-transfer-job-inquiry', ); - callbacks.add('afterSaveMessage', handleAfterSaveMessage, callbacks.priority.HIGH, 'livechat-cancel-auto-transfer-job-after-message'); + callbacks.add( + 'afterOmnichannelSaveMessage', + async (message: IMessage, { room }): Promise => { + const { _id: rid, autoTransferredAt, autoTransferOngoing } = room; + const { token, t: messageType } = message; + + if (messageType) { + // ignore system messages + return message; + } + + if (!autoTransferTimeout || autoTransferTimeout <= 0) { + return message; + } + + if (!message || token) { + // ignore messages from visitors + return message; + } + + if (autoTransferredAt) { + return message; + } + + if (!autoTransferOngoing) { + return message; + } + + await AutoTransferChatScheduler.unscheduleRoom(rid); + return message; + }, + callbacks.priority.HIGH, + 'livechat-cancel-auto-transfer-job-after-message', + ); callbacks.add('livechat.closeRoom', handleAfterCloseRoom, callbacks.priority.HIGH, 'livechat-cancel-auto-transfer-on-close-room'); }); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/setPredictedVisitorAbandonmentTime.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/setPredictedVisitorAbandonmentTime.ts index 38238763e0ca..c244022689dc 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/setPredictedVisitorAbandonmentTime.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/setPredictedVisitorAbandonmentTime.ts @@ -1,5 +1,5 @@ import type { IOmnichannelRoom } from '@rocket.chat/core-typings'; -import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isEditedMessage } from '@rocket.chat/core-typings'; import { LivechatRooms } from '@rocket.chat/models'; import moment from 'moment'; @@ -8,12 +8,8 @@ import { callbacks } from '../../../../../lib/callbacks'; import { setPredictedVisitorAbandonmentTime } from '../lib/Helper'; callbacks.add( - 'afterSaveMessage', - async (message, room) => { - if (!isOmnichannelRoom(room)) { - return message; - } - + 'afterOmnichannelSaveMessage', + async (message, { room }) => { if ( !settings.get('Livechat_abandoned_rooms_action') || settings.get('Livechat_abandoned_rooms_action') === 'none' || diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 02e9162c8330..044e70580e5b 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -50,6 +50,7 @@ interface EventLikeCallbackSignatures { 'afterFileUpload': (params: { user: IUser; room: IRoom; message: IMessage }) => void; 'afterRoomNameChange': (params: { rid: string; name: string; oldName: string }) => void; 'afterSaveMessage': (message: IMessage, room: IRoom, uid?: string) => void; + 'afterOmnichannelSaveMessage': (message: IMessage, constant: { room: IOmnichannelRoom }) => void; 'livechat.removeAgentDepartment': (params: { departmentId: ILivechatDepartmentRecord['_id']; agentsId: ILivechatAgent['_id'][] }) => void; 'livechat.saveAgentDepartment': (params: { departmentId: ILivechatDepartmentRecord['_id']; agentsId: ILivechatAgent['_id'][] }) => void; 'livechat.closeRoom': (params: { room: IOmnichannelRoom; options: CloseRoomParams['options'] }) => void;