UNPKG

@rocket.chat/apps-engine

Version:

The engine code for the Rocket.Chat Apps which manages, runs, translates, coordinates and all of that.

391 lines (326 loc) 16 kB
import type { IModifyCreator } from '@rocket.chat/apps-engine/definition/accessors/IModifyCreator.ts'; import type { IUploadCreator } from '@rocket.chat/apps-engine/definition/accessors/IUploadCreator.ts'; import type { IEmailCreator } from '@rocket.chat/apps-engine/definition/accessors/IEmailCreator.ts'; import type { IContactCreator } from '@rocket.chat/apps-engine/definition/accessors/IContactCreator.ts'; import type { ILivechatCreator } from '@rocket.chat/apps-engine/definition/accessors/ILivechatCreator.ts'; import type { IMessage } from '@rocket.chat/apps-engine/definition/messages/IMessage.ts'; import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms/IRoom.ts'; import type { IBotUser } from '@rocket.chat/apps-engine/definition/users/IBotUser.ts'; import type { UserType as _UserType } from '@rocket.chat/apps-engine/definition/users/UserType.ts'; import type { RocketChatAssociationModel as _RocketChatAssociationModel } from '@rocket.chat/apps-engine/definition/metadata/RocketChatAssociations.ts'; import type { IMessageBuilder } from '@rocket.chat/apps-engine/definition/accessors/IMessageBuilder.ts'; import type { IRoomBuilder } from '@rocket.chat/apps-engine/definition/accessors/IRoomBuilder.ts'; import type { IUserBuilder } from '@rocket.chat/apps-engine/definition/accessors/IUserBuilder.ts'; import type { IVideoConferenceBuilder } from '@rocket.chat/apps-engine/definition/accessors/IVideoConferenceBuilder.ts'; import type { RoomType as _RoomType } from '@rocket.chat/apps-engine/definition/rooms/RoomType.ts'; import type { ILivechatMessageBuilder } from '@rocket.chat/apps-engine/definition/accessors/ILivechatMessageBuilder.ts'; import type { UIHelper as _UIHelper } from '@rocket.chat/apps-engine/server/misc/UIHelper.ts'; import * as Messenger from '../../messenger.ts'; import { randomBytes } from 'node:crypto'; import { BlockBuilder } from '../builders/BlockBuilder.ts'; import { MessageBuilder } from '../builders/MessageBuilder.ts'; import { DiscussionBuilder, IDiscussionBuilder } from '../builders/DiscussionBuilder.ts'; import { ILivechatMessage, LivechatMessageBuilder } from '../builders/LivechatMessageBuilder.ts'; import { RoomBuilder } from '../builders/RoomBuilder.ts'; import { UserBuilder } from '../builders/UserBuilder.ts'; import { AppVideoConference, VideoConferenceBuilder } from '../builders/VideoConferenceBuilder.ts'; import { AppObjectRegistry } from '../../../AppObjectRegistry.ts'; import { require } from '../../../lib/require.ts'; const { UIHelper } = require('@rocket.chat/apps-engine/server/misc/UIHelper.js') as { UIHelper: typeof _UIHelper }; const { RoomType } = require('@rocket.chat/apps-engine/definition/rooms/RoomType.js') as { RoomType: typeof _RoomType }; const { UserType } = require('@rocket.chat/apps-engine/definition/users/UserType.js') as { UserType: typeof _UserType }; const { RocketChatAssociationModel } = require('@rocket.chat/apps-engine/definition/metadata/RocketChatAssociations.js') as { RocketChatAssociationModel: typeof _RocketChatAssociationModel; }; export class ModifyCreator implements IModifyCreator { constructor(private readonly senderFn: typeof Messenger.sendRequest) { } getLivechatCreator(): ILivechatCreator { return new Proxy( { __kind: 'getLivechatCreator' }, { get: (_target: unknown, prop: string) => { // It's not worthwhile to make an asynchronous request for such a simple method if (prop === 'createToken') { return () => randomBytes(16).toString('hex'); } if (prop === 'toJSON') { return () => ({}); } return (...params: unknown[]) => this.senderFn({ method: `accessor:getModifier:getCreator:getLivechatCreator:${prop}`, params, }) .then((response) => response.result) .catch((err) => { if (err instanceof Error) { throw err; } if (err?.error?.message) { throw new Error(err.error.message); } throw new Error(err.error); }); }, }, ) as ILivechatCreator; } getUploadCreator(): IUploadCreator { return new Proxy( { __kind: 'getUploadCreator' }, { get: (_target: unknown, prop: string) => (...params: unknown[]) => prop === 'toJSON' ? {} : this.senderFn({ method: `accessor:getModifier:getCreator:getUploadCreator:${prop}`, params, }) .then((response) => response.result) .catch((err) => { if (err instanceof Error) { throw err; } if (err?.error?.message) { throw new Error(err.error.message); } throw new Error(err.error); }), }, ) as IUploadCreator; } getEmailCreator(): IEmailCreator { return new Proxy( { __kind: 'getEmailCreator' }, { get: (_target: unknown, prop: string) => (...params: unknown[]) => prop === 'toJSON' ? {} : this.senderFn({ method: `accessor:getModifier:getCreator:getEmailCreator:${prop}`, params }) .then((response) => response.result) .catch((err) => { if (err instanceof Error) { throw err; } if (err?.error?.message) { throw new Error(err.error.message); } throw new Error(err.error); }), } ) } getContactCreator(): IContactCreator { return new Proxy( { __kind: 'getContactCreator' }, { get: (_target: unknown, prop: string) => (...params: unknown[]) => prop === 'toJSON' ? {} : this.senderFn({ method: `accessor:getModifier:getCreator:getContactCreator:${prop}`, params }) .then((response) => response.result) .catch((err) => { if (err instanceof Error) { throw err; } if (err?.error?.message) { throw new Error(err.error.message); } throw new Error(err.error); }), } ) } getBlockBuilder() { return new BlockBuilder(); } startMessage(data?: IMessage) { if (data) { delete data.id; } return new MessageBuilder(data); } startLivechatMessage(data?: ILivechatMessage) { if (data) { delete data.id; } return new LivechatMessageBuilder(data); } startRoom(data?: IRoom) { if (data) { // @ts-ignore - this has been imported from the Apps-Engine delete data.id; } return new RoomBuilder(data); } startDiscussion(data?: Partial<IRoom>) { if (data) { delete data.id; } return new DiscussionBuilder(data); } startVideoConference(data?: Partial<AppVideoConference>) { return new VideoConferenceBuilder(data); } startBotUser(data?: Partial<IBotUser>) { if (data) { delete data.id; const { roles } = data; if (roles?.length) { const hasRole = roles .map((role: string) => role.toLocaleLowerCase()) .some((role: string) => role === 'admin' || role === 'owner' || role === 'moderator'); if (hasRole) { throw new Error('Invalid role assigned to the user. Should not be admin, owner or moderator.'); } } if (!data.type) { data.type = UserType.BOT; } } return new UserBuilder(data); } public finish( builder: IMessageBuilder | ILivechatMessageBuilder | IRoomBuilder | IDiscussionBuilder | IVideoConferenceBuilder | IUserBuilder, ): Promise<string> { switch (builder.kind) { case RocketChatAssociationModel.MESSAGE: return this._finishMessage(builder as IMessageBuilder); case RocketChatAssociationModel.LIVECHAT_MESSAGE: return this._finishLivechatMessage(builder as ILivechatMessageBuilder); case RocketChatAssociationModel.ROOM: return this._finishRoom(builder as IRoomBuilder); case RocketChatAssociationModel.DISCUSSION: return this._finishDiscussion(builder as IDiscussionBuilder); case RocketChatAssociationModel.VIDEO_CONFERENCE: return this._finishVideoConference(builder as IVideoConferenceBuilder); case RocketChatAssociationModel.USER: return this._finishUser(builder as IUserBuilder); default: throw new Error('Invalid builder passed to the ModifyCreator.finish function.'); } } private async _finishMessage(builder: IMessageBuilder): Promise<string> { const result = builder.getMessage(); delete result.id; if (!result.sender || !result.sender.id) { const response = await this.senderFn({ method: 'bridges:getUserBridge:doGetAppUser', params: ['APP_ID'], }); const appUser = response.result; if (!appUser) { throw new Error('Invalid sender assigned to the message.'); } result.sender = appUser; } if (result.blocks?.length) { // Can we move this elsewhere? This AppObjectRegistry usage doesn't really belong here, but where? result.blocks = UIHelper.assignIds(result.blocks, AppObjectRegistry.get('id') || ''); } const response = await this.senderFn({ method: 'bridges:getMessageBridge:doCreate', params: [result, AppObjectRegistry.get('id')], }); return String(response.result); } private async _finishLivechatMessage(builder: ILivechatMessageBuilder): Promise<string> { if (builder.getSender() && !builder.getVisitor()) { return this._finishMessage(builder.getMessageBuilder()); } const result = builder.getMessage(); delete result.id; if (!result.token && (!result.visitor || !result.visitor.token)) { throw new Error('Invalid visitor sending the message'); } result.token = result.visitor ? result.visitor.token : result.token; const response = await this.senderFn({ method: 'bridges:getLivechatBridge:doCreateMessage', params: [result, AppObjectRegistry.get('id')], }); return String(response.result); } private async _finishRoom(builder: IRoomBuilder): Promise<string> { const result = builder.getRoom(); delete result.id; if (!result.type) { throw new Error('Invalid type assigned to the room.'); } if (result.type !== RoomType.LIVE_CHAT) { if (!result.creator || !result.creator.id) { throw new Error('Invalid creator assigned to the room.'); } } if (result.type !== RoomType.DIRECT_MESSAGE) { if (result.type !== RoomType.LIVE_CHAT) { if (!result.slugifiedName || !result.slugifiedName.trim()) { throw new Error('Invalid slugifiedName assigned to the room.'); } } if (!result.displayName || !result.displayName.trim()) { throw new Error('Invalid displayName assigned to the room.'); } } const response = await this.senderFn({ method: 'bridges:getRoomBridge:doCreate', params: [result, builder.getMembersToBeAddedUsernames(), AppObjectRegistry.get('id')], }); return String(response.result); } private async _finishDiscussion(builder: IDiscussionBuilder): Promise<string> { const room = builder.getRoom(); delete room.id; if (!room.creator || !room.creator.id) { throw new Error('Invalid creator assigned to the discussion.'); } if (!room.slugifiedName || !room.slugifiedName.trim()) { throw new Error('Invalid slugifiedName assigned to the discussion.'); } if (!room.displayName || !room.displayName.trim()) { throw new Error('Invalid displayName assigned to the discussion.'); } if (!room.parentRoom || !room.parentRoom.id) { throw new Error('Invalid parentRoom assigned to the discussion.'); } const response = await this.senderFn({ method: 'bridges:getRoomBridge:doCreateDiscussion', params: [room, builder.getParentMessage(), builder.getReply(), builder.getMembersToBeAddedUsernames(), AppObjectRegistry.get('id')], }); return String(response.result); } private async _finishVideoConference(builder: IVideoConferenceBuilder): Promise<string> { const videoConference = builder.getVideoConference(); if (!videoConference.createdBy) { throw new Error('Invalid creator assigned to the video conference.'); } if (!videoConference.providerName?.trim()) { throw new Error('Invalid provider name assigned to the video conference.'); } if (!videoConference.rid) { throw new Error('Invalid roomId assigned to the video conference.'); } const response = await this.senderFn({ method: 'bridges:getVideoConferenceBridge:doCreate', params: [videoConference, AppObjectRegistry.get('id')], }); return String(response.result); } private async _finishUser(builder: IUserBuilder): Promise<string> { const user = builder.getUser(); const response = await this.senderFn({ method: 'bridges:getUserBridge:doCreate', params: [user, AppObjectRegistry.get('id')], }); return String(response.result); } }