diff --git a/.changeset/big-tips-greet.md b/.changeset/big-tips-greet.md new file mode 100644 index 0000000000000..701e013dec0da --- /dev/null +++ b/.changeset/big-tips-greet.md @@ -0,0 +1,7 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/apps-engine": minor +"@rocket.chat/apps": minor +--- + +Allows apps to react to department status changes. diff --git a/apps/meteor/app/apps/server/bridges/listeners.js b/apps/meteor/app/apps/server/bridges/listeners.js index ebf57f7ccceb3..31aa2c0052695 100644 --- a/apps/meteor/app/apps/server/bridges/listeners.js +++ b/apps/meteor/app/apps/server/bridges/listeners.js @@ -50,6 +50,8 @@ export class AppListenerBridge { case AppInterface.IPostLivechatRoomTransferred: case AppInterface.IPostLivechatGuestSaved: case AppInterface.IPostLivechatRoomSaved: + case AppInterface.IPostLivechatDepartmentRemoved: + case AppInterface.IPostLivechatDepartmentDisabled: return 'livechatEvent'; case AppInterface.IPostUserCreated: case AppInterface.IPostUserUpdated: @@ -197,6 +199,16 @@ export class AppListenerBridge { .getManager() .getListenerManager() .executeListener(inte, await this.orch.getConverters().get('rooms').convertById(data)); + case AppInterface.IPostLivechatDepartmentDisabled: + return this.orch + .getManager() + .getListenerManager() + .executeListener(inte, await this.orch.getConverters().get('departments').convertDepartment(data)); + case AppInterface.IPostLivechatDepartmentRemoved: + return this.orch + .getManager() + .getListenerManager() + .executeListener(inte, await this.orch.getConverters().get('departments').convertDepartment(data)); default: const room = await this.orch.getConverters().get('rooms').convertRoom(data); diff --git a/apps/meteor/app/livechat/server/lib/departmentsLib.ts b/apps/meteor/app/livechat/server/lib/departmentsLib.ts index 7dec370768f0d..2a9a505064b25 100644 --- a/apps/meteor/app/livechat/server/lib/departmentsLib.ts +++ b/apps/meteor/app/livechat/server/lib/departmentsLib.ts @@ -1,3 +1,4 @@ +import { AppEvents, Apps } from '@rocket.chat/apps'; import type { LivechatDepartmentDTO, ILivechatDepartment, ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; import { LivechatDepartment, LivechatDepartmentAgents, LivechatVisitors, LivechatRooms } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; @@ -133,6 +134,10 @@ export async function saveDepartment( // Disable event if (department?.enabled && !departmentDB?.enabled) { await callbacks.run('livechat.afterDepartmentDisabled', departmentDB); + void Apps.self + ?.getBridges() + ?.getListenerBridge() + .livechatEvent(AppEvents.IPostLivechatDepartmentDisabled, { department: departmentDB }); } if (departmentUnit) { @@ -269,7 +274,7 @@ export async function removeDepartment(departmentId: string) { } }); - const { deletedCount } = await removeByDept; + const { deletedCount } = promiseResponses[0].status === 'fulfilled' ? promiseResponses[0].value : { deletedCount: 0 }; if (deletedCount > 0) { removedAgents.forEach(({ _id: docId, agentId }) => { @@ -285,6 +290,7 @@ export async function removeDepartment(departmentId: string) { } await callbacks.run('livechat.afterRemoveDepartment', { department, agentsIds: removedAgents.map(({ agentId }) => agentId) }); + void Apps.self?.getBridges()?.getListenerBridge().livechatEvent(AppEvents.IPostLivechatDepartmentRemoved, { department }); return ret; } diff --git a/packages/apps-engine/src/definition/livechat/ILivechatEventContext.ts b/packages/apps-engine/src/definition/livechat/ILivechatEventContext.ts index b94f07ef0250e..c7c554b8376b5 100644 --- a/packages/apps-engine/src/definition/livechat/ILivechatEventContext.ts +++ b/packages/apps-engine/src/definition/livechat/ILivechatEventContext.ts @@ -1,7 +1,12 @@ import type { IUser } from '../users'; +import type { IDepartment } from './IDepartment'; import type { ILivechatRoom } from './ILivechatRoom'; export interface ILivechatEventContext { agent: IUser; room: ILivechatRoom; } + +export interface ILivechatDepartmentEventContext { + department: IDepartment; +} diff --git a/packages/apps-engine/src/definition/livechat/IPostLivechatDepartmentDisabled.ts b/packages/apps-engine/src/definition/livechat/IPostLivechatDepartmentDisabled.ts new file mode 100644 index 0000000000000..a08b1d61e446f --- /dev/null +++ b/packages/apps-engine/src/definition/livechat/IPostLivechatDepartmentDisabled.ts @@ -0,0 +1,25 @@ +import type { IHttp, IModify, IPersistence, IRead } from '../accessors'; +import { AppMethod } from '../metadata'; +import type { ILivechatDepartmentEventContext } from './ILivechatEventContext'; + +/** + * Handler called after the disablement of a livechat department. + */ +export interface IPostLivechatDepartmentDisabled { + /** + * Handler called *after* the disablement of a livechat department. + * + * @param data the livechat context data which contains the department disabled + * @param read An accessor to the environment + * @param http An accessor to the outside world + * @param persis An accessor to the App's persistence + * @param modify An accessor to the modifier + */ + [AppMethod.EXECUTE_POST_LIVECHAT_DEPARTMENT_DISABLED]( + context: ILivechatDepartmentEventContext, + read: IRead, + http: IHttp, + persis: IPersistence, + modify?: IModify, + ): Promise; +} diff --git a/packages/apps-engine/src/definition/livechat/IPostLivechatDepartmentRemoved.ts b/packages/apps-engine/src/definition/livechat/IPostLivechatDepartmentRemoved.ts new file mode 100644 index 0000000000000..51a08d81eb88e --- /dev/null +++ b/packages/apps-engine/src/definition/livechat/IPostLivechatDepartmentRemoved.ts @@ -0,0 +1,25 @@ +import type { IHttp, IModify, IPersistence, IRead } from '../accessors'; +import { AppMethod } from '../metadata'; +import type { ILivechatDepartmentEventContext } from './ILivechatEventContext'; + +/** + * Handler called after the removal of a livechat department. + */ +export interface IPostLivechatDepartmentRemoved { + /** + * Handler called *after* the removal of a livechat department. + * + * @param data the livechat context data which contains the department removed + * @param read An accessor to the environment + * @param http An accessor to the outside world + * @param persis An accessor to the App's persistence + * @param modify An accessor to the modifier + */ + [AppMethod.EXECUTE_POST_LIVECHAT_DEPARTMENT_REMOVED]( + context: ILivechatDepartmentEventContext, + read: IRead, + http: IHttp, + persis: IPersistence, + modify?: IModify, + ): Promise; +} diff --git a/packages/apps-engine/src/definition/livechat/index.ts b/packages/apps-engine/src/definition/livechat/index.ts index c5045751d5bfd..4533df149fd20 100644 --- a/packages/apps-engine/src/definition/livechat/index.ts +++ b/packages/apps-engine/src/definition/livechat/index.ts @@ -8,6 +8,8 @@ import { ILivechatTransferData } from './ILivechatTransferData'; import { ILivechatTransferEventContext, LivechatTransferEventType } from './ILivechatTransferEventContext'; import { IPostLivechatAgentAssigned } from './IPostLivechatAgentAssigned'; import { IPostLivechatAgentUnassigned } from './IPostLivechatAgentUnassigned'; +import { IPostLivechatDepartmentDisabled } from './IPostLivechatDepartmentDisabled'; +import { IPostLivechatDepartmentRemoved } from './IPostLivechatDepartmentRemoved'; import { IPostLivechatGuestSaved } from './IPostLivechatGuestSaved'; import { IPostLivechatRoomClosed } from './IPostLivechatRoomClosed'; import { IPostLivechatRoomSaved } from './IPostLivechatRoomSaved'; @@ -39,4 +41,6 @@ export { IVisitorEmail, IVisitorPhone, LivechatTransferEventType, + IPostLivechatDepartmentRemoved, + IPostLivechatDepartmentDisabled, }; diff --git a/packages/apps-engine/src/definition/metadata/AppInterface.ts b/packages/apps-engine/src/definition/metadata/AppInterface.ts index 6c599bf56c153..79e73d26679da 100644 --- a/packages/apps-engine/src/definition/metadata/AppInterface.ts +++ b/packages/apps-engine/src/definition/metadata/AppInterface.ts @@ -49,6 +49,8 @@ export enum AppInterface { IPostLivechatRoomTransferred = 'IPostLivechatRoomTransferred', IPostLivechatGuestSaved = 'IPostLivechatGuestSaved', IPostLivechatRoomSaved = 'IPostLivechatRoomSaved', + IPostLivechatDepartmentRemoved = 'IPostLivechatDepartmentRemoved', + IPostLivechatDepartmentDisabled = 'IPostLivechatDepartmentDisabled', // FileUpload IPreFileUpload = 'IPreFileUpload', // Email diff --git a/packages/apps-engine/src/definition/metadata/AppMethod.ts b/packages/apps-engine/src/definition/metadata/AppMethod.ts index 50c4cde74f0d4..67e0611290f23 100644 --- a/packages/apps-engine/src/definition/metadata/AppMethod.ts +++ b/packages/apps-engine/src/definition/metadata/AppMethod.ts @@ -90,6 +90,8 @@ export enum AppMethod { EXECUTE_POST_LIVECHAT_ROOM_TRANSFERRED = 'executePostLivechatRoomTransferred', EXECUTE_POST_LIVECHAT_GUEST_SAVED = 'executePostLivechatGuestSaved', EXECUTE_POST_LIVECHAT_ROOM_SAVED = 'executePostLivechatRoomSaved', + EXECUTE_POST_LIVECHAT_DEPARTMENT_DISABLED = 'executePostLivechatDepartmentDisabled', + EXECUTE_POST_LIVECHAT_DEPARTMENT_REMOVED = 'executePostLivechatDepartmentRemoved', // FileUpload EXECUTE_PRE_FILE_UPLOAD = 'executePreFileUpload', // Email diff --git a/packages/apps-engine/src/server/managers/AppListenerManager.ts b/packages/apps-engine/src/server/managers/AppListenerManager.ts index 882a1fdc9d944..5236223be2987 100644 --- a/packages/apps-engine/src/server/managers/AppListenerManager.ts +++ b/packages/apps-engine/src/server/managers/AppListenerManager.ts @@ -3,6 +3,7 @@ import type { IEmailDescriptor, IPreEmailSentContext } from '../../definition/em import { EssentialAppDisabledException } from '../../definition/exceptions'; import type { IExternalComponent } from '../../definition/externalComponent'; import type { ILivechatEventContext, ILivechatRoom, ILivechatTransferEventContext, IVisitor } from '../../definition/livechat'; +import type { ILivechatDepartmentEventContext } from '../../definition/livechat/ILivechatEventContext'; import type { IMessage, IMessageDeleteContext, @@ -194,6 +195,14 @@ interface IListenerExecutor { args: [IVisitor]; result: void; }; + [AppInterface.IPostLivechatDepartmentRemoved]: { + args: [ILivechatDepartmentEventContext]; + result: void; + }; + [AppInterface.IPostLivechatDepartmentDisabled]: { + args: [ILivechatDepartmentEventContext]; + result: void; + }; // FileUpload [AppInterface.IPreFileUpload]: { args: [IFileUploadContext]; @@ -428,6 +437,10 @@ export class AppListenerManager { return this.executePostLivechatAgentUnassigned(data as ILivechatEventContext); case AppInterface.IPostLivechatRoomTransferred: return this.executePostLivechatRoomTransferred(data as ILivechatTransferEventContext); + case AppInterface.IPostLivechatDepartmentRemoved: + return this.executePostLivechatDepartmentRemoved(data as ILivechatDepartmentEventContext); + case AppInterface.IPostLivechatDepartmentDisabled: + return this.executePostLivechatDepartmentDisabled(data as ILivechatDepartmentEventContext); case AppInterface.IPostLivechatGuestSaved: return this.executePostLivechatGuestSaved(data as IVisitor); // FileUpload @@ -1137,6 +1150,22 @@ export class AppListenerManager { } } + private async executePostLivechatDepartmentRemoved(data: ILivechatDepartmentEventContext): Promise { + for (const appId of this.listeners.get(AppInterface.IPostLivechatDepartmentRemoved)) { + const app = this.manager.getOneById(appId); + + await app.call(AppMethod.EXECUTE_POST_LIVECHAT_DEPARTMENT_REMOVED, data); + } + } + + private async executePostLivechatDepartmentDisabled(data: ILivechatDepartmentEventContext): Promise { + for (const appId of this.listeners.get(AppInterface.IPostLivechatDepartmentDisabled)) { + const app = this.manager.getOneById(appId); + + await app.call(AppMethod.EXECUTE_POST_LIVECHAT_DEPARTMENT_DISABLED, data); + } + } + // FileUpload private async executePreFileUpload(data: IFileUploadContext): Promise { for (const appId of this.listeners.get(AppInterface.IPreFileUpload)) { diff --git a/packages/apps/src/bridges/IListenerBridge.ts b/packages/apps/src/bridges/IListenerBridge.ts index 264d86153dfe4..83c3910d152cc 100644 --- a/packages/apps/src/bridges/IListenerBridge.ts +++ b/packages/apps/src/bridges/IListenerBridge.ts @@ -29,7 +29,11 @@ declare module '@rocket.chat/apps-engine/server/bridges' { roomEvent(int: 'IPostRoomCreate' | 'IPostRoomDeleted', room: IRoom): Promise; livechatEvent( - int: 'IPostLivechatAgentAssigned' | 'IPostLivechatAgentUnassigned', + int: + | 'IPostLivechatAgentAssigned' + | 'IPostLivechatAgentUnassigned' + | 'IPostLivechatDepartmentRemoved' + | 'IPostLivechatDepartmentDisabled', data: { user: IUser; room: IOmnichannelRoom }, ): Promise; livechatEvent(