Skip to content
This repository was archived by the owner on Nov 5, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/definition/metadata/AppMethod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}
8 changes: 8 additions & 0 deletions src/definition/uikit/UIKitInteractionResponder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
31 changes: 31 additions & 0 deletions src/definition/users/IPostUserJoinRoom.ts
Original file line number Diff line number Diff line change
@@ -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<boolean>;

/**
* 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<boolean>;
}
31 changes: 31 additions & 0 deletions src/definition/users/IPreUserJoinRoomPrevent.ts
Original file line number Diff line number Diff line change
@@ -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<boolean>;

/**
* 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<boolean>;
}
3 changes: 3 additions & 0 deletions src/definition/users/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ import { UserStatusConnection } from './UserStatusConnection';
import { UserType } from './UserType';

export { IUser, IUserEmail, IUserCreationOptions, UserStatusConnection, UserType };

export * from './IPreUserJoinRoomPrevent';
export * from './IPostUserJoinRoom';
4 changes: 4 additions & 0 deletions src/server/compiler/AppImplements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export enum AppInterface {
IUIKitInteractionHandler = 'IUIKitInteractionHandler',
// Livechat
ILivechatRoomClosedHandler = 'ILivechatRoomClosedHandler',
// Users

IPreUserJoinRoomPrevent= 'IPreUserJoinRoomPrevent',
IPostUserJoinRoom = 'IPostUserJoinRoom',
}

export class AppImplements {
Expand Down
71 changes: 69 additions & 2 deletions src/server/managers/AppListenerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<boolean> {
let prevented = false;
Expand Down
21 changes: 21 additions & 0 deletions src/server/users/User.ts
Original file line number Diff line number Diff line change
@@ -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<IUserEmail>;
public type: UserType;
public isEnabled: boolean;
public name: string;
public roles: Array<string>;
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);
}
}