diff --git a/src/definition/metadata/AppMethod.ts b/src/definition/metadata/AppMethod.ts index b0ca31ad4..c5d2f32bf 100644 --- a/src/definition/metadata/AppMethod.ts +++ b/src/definition/metadata/AppMethod.ts @@ -49,4 +49,11 @@ export enum AppMethod { UIKIT_VIEW_CLOSE = 'executeViewClosedHandler', // Livechat EXECUTE_LIVECHAT_ROOM_CLOSED_HANDLER = 'executeLivechatRoomClosedHandler', + + // User + CHECK_PRE_USER_JOIN_PREVENT = 'checkPreUserJoinPrevent', + EXECUTE_PRE_USER_JOIN_PREVENT = 'executePreUserJoinPrevent', + + CHECK_POST_USER_JOIN = 'checkPostUserJoin', + EXECUTE_POST_USER_JOIN = 'executePostUserJoin', } diff --git a/src/definition/uikit/UIKitInteractionResponder.ts b/src/definition/uikit/UIKitInteractionResponder.ts index 7e2ef14b2..89fd9c3cd 100644 --- a/src/definition/uikit/UIKitInteractionResponder.ts +++ b/src/definition/uikit/UIKitInteractionResponder.ts @@ -39,6 +39,14 @@ export class UIKitInteractionResponder { }; } + public closeModalViewResponse(viewData: IUIKitModalViewParam): IUIKitModalResponse { + const { appId, triggerId } = this.baseContext; + return { + success: true, + ...formatModalInteraction(viewData, { appId, triggerId, type: UIKitInteractionType.MODAL_CLOSE }), + }; + } + public viewErrorResponse(errorInteraction: IUIKitErrorInteractionParam): IUIKitErrorResponse { const { appId, triggerId } = this.baseContext; diff --git a/src/definition/users/IPostUserJoinRoom.ts b/src/definition/users/IPostUserJoinRoom.ts new file mode 100644 index 000000000..7b500ff3b --- /dev/null +++ b/src/definition/users/IPostUserJoinRoom.ts @@ -0,0 +1,31 @@ +import { IUser } from '.'; +import { IHttp, IPersistence, IRead } from '../accessors'; +import { IRoom } from '../rooms'; + +/** Handler which is called to determine whether a user is allowed to send a message or not. */ +export interface IPostUserJoinRoom { + /** + * Enables the handler to signal to the Apps framework whether + * this handler should actually be executed for the message + * about to be sent. + * + * @param user The user which is being joined + * @param room The room the room + * @param read An accessor to the environment + * @param http An accessor to the outside world + * @returns whether to run the prevent or not + */ + checkPostUserJoin?(user: IUser, room: IRoom, read: IRead, http: IHttp): Promise; + + /** + * Method which is to be used to prevent a message from being sent. + * + * @param user The user which is being joined + * @param room The room the room + * @param read An accessor to the environment + * @param http An accessor to the outside world + * @param persistence An accessor to the App's persistence storage + * @returns whether to prevent the message from being sent + */ + executePostUserJoin(user: IUser, room: IRoom, read: IRead, http: IHttp, persistence: IPersistence): Promise; +} diff --git a/src/definition/users/IPreUserJoinRoomPrevent.ts b/src/definition/users/IPreUserJoinRoomPrevent.ts new file mode 100644 index 000000000..8a83ac4b0 --- /dev/null +++ b/src/definition/users/IPreUserJoinRoomPrevent.ts @@ -0,0 +1,31 @@ +import { IUser } from '.'; +import { IHttp, IPersistence, IRead } from '../accessors'; +import { IRoom } from '../rooms'; + +/** Handler which is called to determine whether a user is allowed to send a message or not. */ +export interface IPreUserJoinRoomPrevent { + /** + * Enables the handler to signal to the Apps framework whether + * this handler should actually be executed for the message + * about to be sent. + * + * @param user The user which is being joined + * @param room The room the room + * @param read An accessor to the environment + * @param http An accessor to the outside world + * @returns whether to run the prevent or not + */ + checkPreUserJoinPrevent?(user: IUser, room: IRoom, read: IRead, http: IHttp): Promise; + + /** + * Method which is to be used to prevent a message from being sent. + * + * @param user The user which is being joined + * @param room The room the room + * @param read An accessor to the environment + * @param http An accessor to the outside world + * @param persistence An accessor to the App's persistence storage + * @returns whether to prevent the message from being sent + */ + executePreUserJoinPrevent(user: IUser, room: IRoom, read: IRead, http: IHttp, persistence: IPersistence): Promise; +} diff --git a/src/definition/users/index.ts b/src/definition/users/index.ts index 008dd2b87..006affcf8 100644 --- a/src/definition/users/index.ts +++ b/src/definition/users/index.ts @@ -5,3 +5,6 @@ import { UserStatusConnection } from './UserStatusConnection'; import { UserType } from './UserType'; export { IUser, IUserEmail, IUserCreationOptions, UserStatusConnection, UserType }; + +export * from './IPreUserJoinRoomPrevent'; +export * from './IPostUserJoinRoom'; diff --git a/src/server/compiler/AppImplements.ts b/src/server/compiler/AppImplements.ts index 3a8e84ebe..3401d7b74 100644 --- a/src/server/compiler/AppImplements.ts +++ b/src/server/compiler/AppImplements.ts @@ -23,6 +23,10 @@ export enum AppInterface { IUIKitInteractionHandler = 'IUIKitInteractionHandler', // Livechat ILivechatRoomClosedHandler = 'ILivechatRoomClosedHandler', + // Users + + IPreUserJoinRoomPrevent= 'IPreUserJoinRoomPrevent', + IPostUserJoinRoom = 'IPostUserJoinRoom', } export class AppImplements { diff --git a/src/server/managers/AppListenerManager.ts b/src/server/managers/AppListenerManager.ts index 944d8f2e4..e569e59d6 100644 --- a/src/server/managers/AppListenerManager.ts +++ b/src/server/managers/AppListenerManager.ts @@ -20,6 +20,7 @@ import { Message } from '../messages/Message'; import { Utilities } from '../misc/Utilities'; import { ProxiedApp } from '../ProxiedApp'; import { Room } from '../rooms/Room'; +import { User } from '../users/User'; import { AppAccessorManager } from './AppAccessorManager'; export class AppListenerManager { @@ -108,14 +109,80 @@ export class AppListenerManager { return this.executeUIKitInteraction(data as IUIKitIncomingInteraction); // Livechat case AppInterface.ILivechatRoomClosedHandler: - this.executeLivechatRoomClosed(data as ILivechatRoom); - return; + return this.executeLivechatRoomClosed(data as ILivechatRoom); + // User + case AppInterface.IPreUserJoinRoomPrevent: + return this.executePreUserJoinPrevent(data); + case AppInterface.IPostUserJoinRoom: + return this.executePostUserJoin(data); default: console.warn('Unimplemented (or invalid) AppInterface was just tried to execute.'); return; } } + private async executePreUserJoinPrevent(data: any) { + let prevented = false; + const cfUser = new User(Utilities.deepCloneAndFreeze(data.user)); + const cfRoom = new User(Utilities.deepCloneAndFreeze(data.room)); + + for (const appId of this.listeners.get(AppInterface.IPreMessageSentPrevent)) { + const app = this.manager.getOneById(appId); + + let continueOn = true; + if (app.hasMethod(AppMethod.CHECK_PRE_USER_JOIN_PREVENT)) { + continueOn = await app.call(AppMethod.CHECK_PRE_USER_JOIN_PREVENT, + cfUser, + cfRoom, + this.am.getReader(appId), + this.am.getHttp(appId), + ) as boolean; + } + if (continueOn && app.hasMethod(AppMethod.EXECUTE_PRE_USER_JOIN_PREVENT)) { + prevented = await app.call(AppMethod.EXECUTE_PRE_USER_JOIN_PREVENT, + cfUser, + cfRoom, + this.am.getReader(appId), + this.am.getHttp(appId), + this.am.getPersistence(appId), + ) as boolean; + + if (prevented) { + return prevented; + } + } + } + + return prevented; + } + + private async executePostUserJoin(data: any) { + const cfUser = new User(Utilities.deepCloneAndFreeze(data.user)); + const cfRoom = new Room(Utilities.deepCloneAndFreeze(data.room), this.manager); + + for (const appId of this.listeners.get(AppInterface.IPostRoomDeleted)) { + const app = this.manager.getOneById(appId); + + let continueOn = true; + if (app.hasMethod(AppMethod.CHECK_POST_USER_JOIN)) { + continueOn = await app.call(AppMethod.CHECK_POST_USER_JOIN, + cfUser, + cfRoom, + this.am.getReader(appId), + this.am.getHttp(appId), + ) as boolean; + } + if (continueOn && app.hasMethod(AppMethod.EXECUTE_POST_USER_JOIN)) { + await app.call(AppMethod.EXECUTE_POST_USER_JOIN, + cfUser, + cfRoom, + this.am.getReader(appId), + this.am.getHttp(appId), + this.am.getPersistence(appId), + ); + } + } + } // Messages private async executePreMessageSentPrevent(data: IMessage): Promise { let prevented = false; diff --git a/src/server/users/User.ts b/src/server/users/User.ts new file mode 100644 index 000000000..13477f21d --- /dev/null +++ b/src/server/users/User.ts @@ -0,0 +1,21 @@ +import { IUser, IUserEmail, UserStatusConnection, UserType } from '../../definition/users'; +export class User implements IUser { + public id: string; + public username: string; + public emails: Array; + public type: UserType; + public isEnabled: boolean; + public name: string; + public roles: Array; + public status: string; + public statusConnection: UserStatusConnection; + public utcOffset: number; + public createdAt: Date; + public updatedAt: Date; + public lastLoginAt: Date; + public appId?: string; + + public constructor(user: IUser) { + Object.assign(this, user); + } +}