snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
263
apps/meteor/server/hooks/omnichannel/sendToCRM.ts
Normal file
263
apps/meteor/server/hooks/omnichannel/sendToCRM.ts
Normal file
@@ -0,0 +1,263 @@
|
||||
import type { IOmnichannelRoom, IOmnichannelSystemMessage, IMessage } from '@rocket.chat/core-typings';
|
||||
import { isEditedMessage, isOmnichannelRoom } from '@rocket.chat/core-typings';
|
||||
import { LivechatRooms, Messages } from '@rocket.chat/models';
|
||||
import type { Response } from '@rocket.chat/server-fetch';
|
||||
|
||||
import { callbacks } from '../../lib/callbacks';
|
||||
import { getLivechatRoomGuestInfo } from '../../lib/omnichannel/guests';
|
||||
import { sendRequest } from '../../lib/omnichannel/webhooks';
|
||||
import { normalizeMessageFileUpload } from '../../lib/utils/functions/normalizeMessageFileUpload';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
type AdditionalFields =
|
||||
| Record<string, unknown>
|
||||
| {
|
||||
departmentId: IOmnichannelRoom['departmentId'];
|
||||
}
|
||||
| {
|
||||
departmentId: IOmnichannelRoom['departmentId'];
|
||||
servedBy: IOmnichannelRoom['servedBy'];
|
||||
}
|
||||
| {
|
||||
departmentId: IOmnichannelRoom['departmentId'];
|
||||
servedBy: IOmnichannelRoom['servedBy'];
|
||||
oldDepartmentId: IOmnichannelRoom['departmentId'];
|
||||
oldServedBy: { _id: string; ts: Date; username?: string };
|
||||
}
|
||||
| {
|
||||
departmentId: IOmnichannelRoom['departmentId'];
|
||||
servedBy: IOmnichannelRoom['servedBy'];
|
||||
closedAt: IOmnichannelRoom['closedAt'];
|
||||
closedBy: IOmnichannelRoom['closedBy'];
|
||||
closer: IOmnichannelRoom['closer'];
|
||||
};
|
||||
|
||||
type OmnichannelRoomWithExtraFields = IOmnichannelRoom & {
|
||||
oldServedBy?: { _id: string; ts: Date; username?: string };
|
||||
oldDepartmentId?: IOmnichannelRoom['departmentId'];
|
||||
};
|
||||
|
||||
type CRMActions =
|
||||
| 'LivechatSessionStart'
|
||||
| 'LivechatSessionQueued'
|
||||
| 'LivechatSession'
|
||||
| 'LivechatSessionTaken'
|
||||
| 'LivechatSessionForwarded'
|
||||
| 'LivechatEdit'
|
||||
| 'Message'
|
||||
| 'LeadCapture';
|
||||
|
||||
const msgNavType = 'livechat_navigation_history';
|
||||
const msgClosingType = 'livechat-close';
|
||||
|
||||
export const isOmnichannelNavigationMessage = (message: IMessage): message is IOmnichannelSystemMessage => {
|
||||
return message.t === msgNavType;
|
||||
};
|
||||
|
||||
export const isOmnichannelClosingMessage = (message: IMessage): message is IOmnichannelSystemMessage => {
|
||||
return message.t === msgClosingType;
|
||||
};
|
||||
|
||||
export const sendMessageType = (msgType: string): boolean => {
|
||||
switch (msgType) {
|
||||
case msgClosingType:
|
||||
return true;
|
||||
case msgNavType:
|
||||
return (
|
||||
settings.get<boolean>('Livechat_Visitor_navigation_as_a_message') &&
|
||||
settings.get<boolean>('Send_visitor_navigation_history_livechat_webhook_request')
|
||||
);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const getAdditionalFieldsByType = (type: CRMActions, room: OmnichannelRoomWithExtraFields): AdditionalFields => {
|
||||
const { departmentId, servedBy, closedAt, closedBy, closer, oldServedBy, oldDepartmentId } = room;
|
||||
switch (type) {
|
||||
case 'LivechatSessionStart':
|
||||
case 'LivechatSessionQueued':
|
||||
return { departmentId };
|
||||
case 'LivechatSession':
|
||||
return { departmentId, servedBy, closedAt, closedBy, closer };
|
||||
case 'LivechatSessionTaken':
|
||||
return { departmentId, servedBy };
|
||||
case 'LivechatSessionForwarded':
|
||||
return { departmentId, servedBy, oldDepartmentId, oldServedBy };
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
export async function sendToCRM(
|
||||
type: CRMActions,
|
||||
room: OmnichannelRoomWithExtraFields,
|
||||
includeMessages: boolean | IOmnichannelSystemMessage[] = true,
|
||||
): Promise<OmnichannelRoomWithExtraFields> {
|
||||
if (!settings.get('Livechat_webhookUrl')) {
|
||||
return room;
|
||||
}
|
||||
|
||||
const postData: Awaited<ReturnType<typeof getLivechatRoomGuestInfo>> & {
|
||||
type: string;
|
||||
messages: IOmnichannelSystemMessage[];
|
||||
} = {
|
||||
...(await getLivechatRoomGuestInfo(room)),
|
||||
type,
|
||||
messages: [],
|
||||
};
|
||||
|
||||
let messages: IOmnichannelSystemMessage[] | null = null;
|
||||
if (typeof includeMessages === 'boolean' && includeMessages) {
|
||||
messages = await Messages.findVisibleByRoomId<IOmnichannelSystemMessage>(room._id, { sort: { ts: 1 } }).toArray();
|
||||
} else if (includeMessages instanceof Array) {
|
||||
messages = includeMessages;
|
||||
}
|
||||
|
||||
if (messages) {
|
||||
for (const message of messages) {
|
||||
if (message.t && !sendMessageType(message.t)) {
|
||||
continue;
|
||||
}
|
||||
const msg = {
|
||||
_id: message._id,
|
||||
username: message.u.username,
|
||||
msg: message.msg || JSON.stringify(message.blocks),
|
||||
...(message.blocks && message.blocks.length > 0 ? { blocks: message.blocks } : {}),
|
||||
ts: message.ts,
|
||||
rid: message.rid,
|
||||
...(isEditedMessage(message) && { editedAt: message.editedAt }),
|
||||
...(message.u.username !== postData.visitor.username && { agentId: message.u._id }),
|
||||
...(isOmnichannelNavigationMessage(message) && { navigation: message.navigation }),
|
||||
...(isOmnichannelClosingMessage(message) && { closingMessage: true }),
|
||||
...(message.file && { file: message.file, attachments: message.attachments }),
|
||||
};
|
||||
|
||||
const { u } = message;
|
||||
postData.messages.push({ ...(await normalizeMessageFileUpload({ u, ...msg })), ...{ _updatedAt: message._updatedAt } });
|
||||
}
|
||||
}
|
||||
|
||||
const additionalData = getAdditionalFieldsByType(type, room);
|
||||
const responseData = Object.assign(postData, additionalData);
|
||||
|
||||
// do not wait for the request to be answered
|
||||
// this will avoid blocking the process of saving the message
|
||||
void sendRequest(responseData, 5, async (response: Response) => {
|
||||
if (!response) {
|
||||
return;
|
||||
}
|
||||
const responseData = await response.text();
|
||||
await LivechatRooms.saveCRMDataByRoomId(room._id, responseData);
|
||||
});
|
||||
|
||||
return room;
|
||||
}
|
||||
|
||||
callbacks.add(
|
||||
'livechat.closeRoom',
|
||||
async (params) => {
|
||||
const { room } = params;
|
||||
if (!settings.get('Livechat_webhook_on_close')) {
|
||||
return params;
|
||||
}
|
||||
|
||||
await sendToCRM('LivechatSession', room);
|
||||
|
||||
return params;
|
||||
},
|
||||
callbacks.priority.MEDIUM,
|
||||
'livechat-send-crm-close-room',
|
||||
);
|
||||
|
||||
callbacks.add(
|
||||
'livechat.afterForwardChatToAgent',
|
||||
async (params) => {
|
||||
const { rid, oldServedBy } = params;
|
||||
if (!settings.get('Livechat_webhook_on_forward')) {
|
||||
return params;
|
||||
}
|
||||
|
||||
const originalRoom = await LivechatRooms.findOneById(rid);
|
||||
if (!originalRoom) {
|
||||
return params;
|
||||
}
|
||||
|
||||
const room = Object.assign(originalRoom, { oldServedBy });
|
||||
await sendToCRM('LivechatSessionForwarded', room);
|
||||
return params;
|
||||
},
|
||||
callbacks.priority.MEDIUM,
|
||||
'livechat-send-crm-room-forwarded-to-agent',
|
||||
);
|
||||
|
||||
callbacks.add(
|
||||
'livechat.afterForwardChatToDepartment',
|
||||
async (params) => {
|
||||
const { rid, oldDepartmentId } = params;
|
||||
if (!settings.get('Livechat_webhook_on_forward')) {
|
||||
return params;
|
||||
}
|
||||
|
||||
const originalRoom = await LivechatRooms.findOneById(rid);
|
||||
if (!originalRoom) {
|
||||
return params;
|
||||
}
|
||||
|
||||
const room = Object.assign(originalRoom, { oldDepartmentId });
|
||||
await sendToCRM('LivechatSessionForwarded', room);
|
||||
return params;
|
||||
},
|
||||
callbacks.priority.MEDIUM,
|
||||
'livechat-send-crm-room-forwarded-to-department',
|
||||
);
|
||||
|
||||
callbacks.add(
|
||||
'livechat.saveInfo',
|
||||
async (room) => {
|
||||
// Do not send to CRM if the chat is still open
|
||||
if (!isOmnichannelRoom(room) || room.open) {
|
||||
return room;
|
||||
}
|
||||
|
||||
return sendToCRM('LivechatEdit', room);
|
||||
},
|
||||
callbacks.priority.MEDIUM,
|
||||
'livechat-send-crm-save-info',
|
||||
);
|
||||
|
||||
callbacks.add(
|
||||
'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')) {
|
||||
return message;
|
||||
}
|
||||
if (!message.token && !settings.get('Livechat_webhook_on_agent_message')) {
|
||||
return message;
|
||||
}
|
||||
// if the message has a type means it is a special message (like the closing comment), so skips
|
||||
// unless the settings that handle with visitor navigation history are enabled
|
||||
if (message.t && !sendMessageType(message.t)) {
|
||||
return message;
|
||||
}
|
||||
|
||||
await sendToCRM('Message', room, [message]);
|
||||
return message;
|
||||
},
|
||||
callbacks.priority.MEDIUM,
|
||||
'livechat-send-crm-message',
|
||||
);
|
||||
|
||||
callbacks.add(
|
||||
'livechat.leadCapture',
|
||||
(room) => {
|
||||
if (!settings.get('Livechat_webhook_on_capture')) {
|
||||
return room;
|
||||
}
|
||||
return sendToCRM('LeadCapture', room, false);
|
||||
},
|
||||
callbacks.priority.MEDIUM,
|
||||
'livechat-send-crm-lead-capture',
|
||||
);
|
||||
Reference in New Issue
Block a user