Skip to content

Commit

Permalink
Merge branch 'develop' into fix-threads-read-receipts
Browse files Browse the repository at this point in the history
  • Loading branch information
sampaiodiego committed Feb 14, 2023
2 parents 9460eb1 + 7898e49 commit 2418212
Show file tree
Hide file tree
Showing 185 changed files with 7,342 additions and 706 deletions.
5 changes: 5 additions & 0 deletions .hygen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
helpers: {
random: () => Math.floor(3000 + (5000 - 3000) * Math.random()),
},
};
5 changes: 4 additions & 1 deletion _templates/service/new/package.json.ejs.t
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ to: ee/apps/<%= name %>/package.json
"main": "./dist/ee/apps/<%= name %>/src/service.js",
"files": [
"/dist"
]
],
"volta": {
"node": "14.19.3"
}
}

2 changes: 1 addition & 1 deletion _templates/service/new/service.ejs.t
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { broker } from '../../../../apps/meteor/ee/server/startup/broker';
import { Collections, getCollection, getConnection } from '../../../../apps/meteor/ee/server/services/mongo';
import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels';

const PORT = process.env.PORT || 3034;
const PORT = process.env.PORT || <%= h.random() %>;

(async () => {
const db = await getConnection();
Expand Down
5 changes: 4 additions & 1 deletion apps/meteor/app/apps/server/bridges/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getRoom } from '../../../livechat/server/api/lib/livechat';
import { Livechat } from '../../../livechat/server/lib/Livechat';
import { Users, LivechatDepartment, LivechatRooms } from '../../../models/server';
import type { AppServerOrchestrator } from '../orchestrator';
import { Livechat as LivechatTyped } from '../../../livechat/server/lib/LivechatTyped';

export class AppLivechatBridge extends LivechatBridge {
// eslint-disable-next-line no-empty-function
Expand Down Expand Up @@ -122,7 +123,9 @@ export class AppLivechatBridge extends LivechatBridge {
...(visitor && { visitor }),
};

return Livechat.closeRoom(closeData);
await LivechatTyped.closeRoom(closeData);

return true;
}

protected async findRooms(visitor: IVisitor, departmentId: string | null, appId: string): Promise<Array<ILivechatRoom>> {
Expand Down
5 changes: 5 additions & 0 deletions apps/meteor/app/file-upload/server/lib/FileUpload.js
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,11 @@ export class FileUploadClass {
streamOrBuffer = Promise.await(streamToBuffer(streamOrBuffer));
}

if (streamOrBuffer instanceof Uint8Array) {
// Services compat :)
streamOrBuffer = Buffer.from(streamOrBuffer);
}

// Check if the fileData matches store filter
const filter = this.store.getFilter();
if (filter && filter.check) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ import type { IUser } from '@rocket.chat/core-typings';

import { LivechatRooms } from '../../../models/server';
import { settings } from '../../../settings/server';
import { Livechat } from '../../../livechat/server/lib/Livechat';
import { Livechat } from '../../../livechat/server/lib/LivechatTyped';

type SubscribedRooms = {
rid: string;
t: string;
};

export const closeOmnichannelConversations = (user: IUser, subscribedRooms: SubscribedRooms[]): void => {
export const closeOmnichannelConversations = async (user: IUser, subscribedRooms: SubscribedRooms[]): Promise<void> => {
const roomsInfo = LivechatRooms.findByIds(subscribedRooms.map(({ rid }) => rid));
const language = settings.get<string>('Language') || 'en';
const comment = TAPi18n.__('Agent_deactivated', { lng: language });

const promises: Promise<void>[] = [];
roomsInfo.forEach((room: any) => {
Livechat.closeRoom({ user, visitor: {}, room, comment });
promises.push(Livechat.closeRoom({ user, room, comment }));
});

await Promise.all(promises);
};
9 changes: 7 additions & 2 deletions apps/meteor/app/lib/server/functions/setUserActiveStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,13 @@ export function setUserActiveStatus(userId: string, active: boolean, confirmReli
throw new Meteor.Error('user-last-owner', '', rooms);
}

closeOmnichannelConversations(user, livechatSubscribedRooms);
Promise.await(relinquishRoomOwnerships(user, chatSubscribedRooms, false));
Promise.await(
// We don't want one killing the other :)
Promise.allSettled([
closeOmnichannelConversations(user, livechatSubscribedRooms),
relinquishRoomOwnerships(user, chatSubscribedRooms, false),
]),
);
}

if (active && !user.active) {
Expand Down
6 changes: 6 additions & 0 deletions apps/meteor/app/lib/server/startup/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,12 @@ settingsRegistry.addGroup('Accounts', function () {
public: true,
i18nLabel: 'Notifications_Sound_Volume',
});

this.add('Accounts_Default_User_Preferences_omnichannelTranscriptEmail', false, {
type: 'boolean',
public: true,
i18nLabel: 'Omnichannel_transcript_email',
});
});

this.section('Avatar', function () {
Expand Down
121 changes: 116 additions & 5 deletions apps/meteor/app/livechat/server/api/v1/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Random } from 'meteor/random';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import type { ILivechatAgent, IOmnichannelRoom } from '@rocket.chat/core-typings';
import type { ILivechatAgent, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings';
import { isOmnichannelRoom, OmnichannelSourceType } from '@rocket.chat/core-typings';
import { LivechatVisitors, Users } from '@rocket.chat/models';
import { LivechatVisitors, Users, LivechatRooms as LivechatRoomsRaw, Subscriptions } from '@rocket.chat/models';
import {
isLiveChatRoomForwardProps,
isPOSTLivechatRoomCloseParams,
Expand All @@ -13,20 +13,24 @@ import {
isLiveChatRoomJoinProps,
isPUTLivechatRoomVisitorParams,
isLiveChatRoomSaveInfoProps,
isPOSTLivechatRoomCloseByUserParams,
} from '@rocket.chat/rest-typings';

import { settings as rcSettings } from '../../../../settings/server';
import { Messages, LivechatRooms } from '../../../../models/server';
import { API } from '../../../../api/server';
import { findGuest, findRoom, getRoom, settings, findAgent, onCheckRoomParams } from '../lib/livechat';
import { Livechat } from '../../lib/Livechat';
import { Livechat as LivechatTyped } from '../../lib/LivechatTyped';
import { normalizeTransferredByData } from '../../lib/Helper';
import { findVisitorInfo } from '../lib/visitors';
import { canAccessRoom, hasPermission } from '../../../../authorization/server';
import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission';
import { addUserToRoom } from '../../../../lib/server/functions';
import { apiDeprecationLogger } from '../../../../lib/server/lib/deprecationWarningLogger';
import { deprecationWarning } from '../../../../api/server/helpers/deprecationWarning';
import { callbacks } from '../../../../../lib/callbacks';
import type { CloseRoomParams } from '../../lib/LivechatTyped.d';

const isAgentWithInfo = (agentObj: ILivechatAgent | { hiddenInfo: true }): agentObj is ILivechatAgent => !('hiddenInfo' in agentObj);

Expand Down Expand Up @@ -86,6 +90,8 @@ API.v1.addRoute('livechat/room', {
},
});

// Note: use this route if a visitor is closing a room
// If a RC user(like eg agent) is closing a room, use the `livechat/room.closeByUser` route
API.v1.addRoute(
'livechat/room.close',
{ validateParams: isPOSTLivechatRoomCloseParams },
Expand All @@ -110,16 +116,121 @@ API.v1.addRoute(
const language = rcSettings.get<string>('Language') || 'en';
const comment = TAPi18n.__('Closed_by_visitor', { lng: language });

// @ts-expect-error -- typings on closeRoom are wrong
if (!Livechat.closeRoom({ visitor, room, comment })) {
return API.v1.failure();
const options: CloseRoomParams['options'] = {};
if (room.servedBy) {
const servingAgent: Pick<IUser, '_id' | 'name' | 'username' | 'utcOffset' | 'settings' | 'language'> | null =
await Users.findOneById(room.servedBy._id, {
projection: {
name: 1,
username: 1,
utcOffset: 1,
settings: 1,
language: 1,
},
});

if (servingAgent?.settings?.preferences?.omnichannelTranscriptPDF) {
options.pdfTranscript = {
requestedBy: servingAgent._id,
};
}

// We'll send the transcript by email only if the setting is disabled (that means, we're not asking the user if he wants to receive the transcript by email)
// And the agent has the preference enabled to send the transcript by email and the visitor has an email address
// When Livechat_enable_transcript is enabled, the email will be sent via livechat/transcript route
if (
!rcSettings.get<boolean>('Livechat_enable_transcript') &&
servingAgent?.settings?.preferences?.omnichannelTranscriptEmail &&
visitor.visitorEmails?.length &&
visitor.visitorEmails?.[0]?.address
) {
const visitorEmail = visitor.visitorEmails?.[0]?.address;

const language = servingAgent.language || rcSettings.get<string>('Language') || 'en';
const t = (s: string): string => TAPi18n.__(s, { lng: language });
const subject = t('Transcript_of_your_livechat_conversation');

options.emailTranscript = {
sendToVisitor: true,
requestData: {
email: visitorEmail,
requestedAt: new Date(),
requestedBy: servingAgent,
subject,
},
};
}
}

await LivechatTyped.closeRoom({ visitor, room, comment, options });

return API.v1.success({ rid, comment });
},
},
);

API.v1.addRoute(
'livechat/room.closeByUser',
{
validateParams: isPOSTLivechatRoomCloseByUserParams,
authRequired: true,
permissionsRequired: ['close-livechat-room'],
},
{
async post() {
const { rid, comment, tags, generateTranscriptPdf, transcriptEmail } = this.bodyParams;

const room = await LivechatRoomsRaw.findOneById(rid);
if (!room || !isOmnichannelRoom(room)) {
throw new Error('error-invalid-room');
}

if (!room.open) {
throw new Error('error-room-already-closed');
}

const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, this.userId, { projection: { _id: 1 } });
if (!subscription && !(await hasPermissionAsync(this.userId, 'close-others-livechat-room'))) {
throw new Error('error-not-authorized');
}

const options: CloseRoomParams['options'] = {
clientAction: true,
tags,
...(generateTranscriptPdf && { pdfTranscript: { requestedBy: this.userId } }),
...(transcriptEmail && {
...(transcriptEmail.sendToVisitor
? {
emailTranscript: {
sendToVisitor: true,
requestData: {
email: transcriptEmail.requestData.email,
subject: transcriptEmail.requestData.subject,
requestedAt: new Date(),
requestedBy: this.user,
},
},
}
: {
emailTranscript: {
sendToVisitor: false,
},
}),
}),
};

await LivechatTyped.closeRoom({
room,
user: this.user,
options,
comment,
});

return API.v1.success();
},
},
);

API.v1.addRoute(
'livechat/room.transfer',
{ validateParams: isPOSTLivechatRoomTransferParams },
Expand Down
46 changes: 0 additions & 46 deletions apps/meteor/app/livechat/server/hooks/beforeCloseRoom.js

This file was deleted.

10 changes: 6 additions & 4 deletions apps/meteor/app/livechat/server/hooks/processRoomAbandonment.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,22 @@ const getSecondsSinceLastAgentResponse = async (room, agentLastMessage) => {

callbacks.add(
'livechat.closeRoom',
(room) => {
(params) => {
const { room } = params;

const closedByAgent = room.closer !== 'visitor';
const wasTheLastMessageSentByAgent = room.lastMessage && !room.lastMessage.token;
if (!closedByAgent || !wasTheLastMessageSentByAgent) {
return;
return params;
}
const agentLastMessage = Messages.findAgentLastMessageByVisitorLastMessageTs(room._id, room.v.lastMessageTs);
if (!agentLastMessage) {
return;
return params;
}
const secondsSinceLastAgentResponse = Promise.await(getSecondsSinceLastAgentResponse(room, agentLastMessage));
LivechatRooms.setVisitorInactivityInSecondsById(room._id, secondsSinceLastAgentResponse);

return room;
return params;
},
callbacks.priority.HIGH,
'process-room-abandonment',
Expand Down

0 comments on commit 2418212

Please sign in to comment.