Now that we have learned about the event interfaces, let's go through some examples to implement them. On this page, we will follow three examples:
Messaging governance
Notify messages
Content inspection
Example 1: Messaging governance
In this example, we will use the IPreMessageSentPrevent interface to control whether a particular message should be sent to a channel. We are using our Hello World app to test this.
Open your app folder in Visual Studio and select the main app file, in this case,
HelloWorldApp.ts.To the
HelloWorldAppclass, add theIPreMessageSentPreventinterface as follows:export class HelloWorldApp extends App implements IPreMessageSentPrevent {Import the following interface in the main app file:
import { IPreMessageSentPrevent } from '@rocket.chat/apps-engine/definition/messages/IPreMessageSentPrevent';Now Visual Studio will display an error that a part of the interface is missing its implementation, with the option to fix it quickly. Accept the suggested fix. Visual Studio adds two methods to the interface that are not implemented. The methods will throw an error when executed. But now, we have a skeleton code to work with:
checkPreMessageSentPrevent?(message: IMessage, read: IRead, http: IHttp): Promise<boolean> { throw new Error('Method not implemented.'); } executePreMessageSentPrevent(message: IMessage, read: IRead, http: IHttp, persistence: IPersistence): Promise<boolean> { throw new Error('Method not implemented.'); }With the
checkPreMessageSentPreventmethod, we will only check the messages from channels other than general. Visual Studio helps you navigate the APIs of object classes by suggesting available methods. For this example, you can see that the method variablemessagehas a room attribute, which has aslugifiedNamestring attribute. Update the method as follows:checkPreMessageSentPrevent?(message: IMessage, read: IRead, http: IHttp): Promise<boolean> { return message.room.slugifiedName != 'general'; }Now Visual Studio displays an error that the method signature is not compatible with returning a
Promise<boolean>. To fix this issue, addasyncto the method signature.Next, implement the
executePreMessageSentPreventmethod. If the message equals "test", we prevent it from being published. Update the method as follows:async executePreMessageSentPrevent(message: IMessage, read: IRead, http: IHttp, persistence: IPersistence): Promise<boolean> { return message.text == 'test'; }Your main app file should look something like this:
import { IAppAccessors, IHttp, ILogger, IPersistence, IRead, } from '@rocket.chat/apps-engine/definition/accessors'; import { App } from '@rocket.chat/apps-engine/definition/App'; import { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import { IPreMessageSentPrevent } from '@rocket.chat/apps-engine/definition/messages/IPreMessageSentPrevent'; import { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata'; export class HelloWorldApp extends App implements IPreMessageSentPrevent { constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) { super(info, logger, accessors); logger.debug('Hello, World!'); } async checkPreMessageSentPrevent?(message: IMessage, read: IRead, http: IHttp): Promise<boolean> { return message.room.slugifiedName != 'general'; } async executePreMessageSentPrevent(message: IMessage, read: IRead, http: IHttp, persistence: IPersistence): Promise<boolean> { return message.text == 'test'; } }Save the file and deploy your app.
If we send the
testmessage in a channel other than general, it is not published (it will appear grayed out). If the message is something different, it will get sent as shown in the following screenshot:.png?sv=2022-11-02&spr=https&st=2026-02-22T06%3A18%3A07Z&se=2026-02-22T06%3A35%3A07Z&sr=c&sp=r&sig=CkY%2FTTRTLwVkSniXBo%2BLXj3oUM17Jb5u7hKo%2FblMAiA%3D)
As for the room general, all messages will be sent, including
testas shown in the following screenshot:.png?sv=2022-11-02&spr=https&st=2026-02-22T06%3A18%3A07Z&se=2026-02-22T06%3A35%3A07Z&sr=c&sp=r&sig=CkY%2FTTRTLwVkSniXBo%2BLXj3oUM17Jb5u7hKo%2FblMAiA%3D)
Example 2: Notify messages
In this example, we will notify the general channel whenever a message is sent anywhere else. Here we will implement the IPostMessageSent interface.
Like the previous example, in the main app file (in this case,
HelloWorldApp.ts), we will add thecheckandexecutemethods from theIPostMessageSentinterface as shown in the code below.The
checkPostMessageSentmethod ensures that the handler only runs for messages outside of the#generalchannel.In the
executePostMessageSentmethod, we fetch the#generalchannel and create a new message there. This message includes information about the original message, the room where it was published, and the sender. We usesendMessagetogether with themessageBuilderto construct and post a richly formatted message in#general.import { IAppAccessors, IHttp, ILogger, IModify, IPersistence, IRead, } from '@rocket.chat/apps-engine/definition/accessors'; import { App } from '@rocket.chat/apps-engine/definition/App'; import { IMessage, IPostMessageSent } from '@rocket.chat/apps-engine/definition/messages'; import { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata'; import { IRoom } from '@rocket.chat/apps-engine/definition/rooms/IRoom'; import { IUser } from '@rocket.chat/apps-engine/definition/users/IUser'; export class HelloWorldApp extends App implements IPostMessageSent { constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) { super(info, logger, accessors); logger.debug('Hello, World!'); } async checkPostMessageSent?(message: IMessage, read: IRead, http: IHttp): Promise<boolean> { return message.room.slugifiedName != 'general'; } async executePostMessageSent(message: IMessage, read: IRead, http: IHttp, persistence: IPersistence, modify: IModify): Promise<void> { const general = await read.getRoomReader().getByName('general'); if (!general) { return; } const msg = `@${message.sender.username} said "${message.text}" in #${message.room.displayName}`; const author = await read.getUserReader().getAppUser(); if (general.id != message.room.id) { // prevent this for running on #general channel await this.sendMessage(general, msg, author?author:message.sender, modify); } } private async sendMessage(room: IRoom, textMessage: string, author: IUser, modify: IModify) { const messageBuilder = modify.getCreator().startMessage({ text: textMessage, } as IMessage); messageBuilder.setRoom(room); messageBuilder.setSender(author); return modify.getCreator().finish(messageBuilder); } }Now save, deploy, and test the app. You should be able to see the messages posted to the general channel when writing to any other channel.
.png?sv=2022-11-02&spr=https&st=2026-02-22T06%3A18%3A07Z&se=2026-02-22T06%3A35%3A07Z&sr=c&sp=r&sig=CkY%2FTTRTLwVkSniXBo%2BLXj3oUM17Jb5u7hKo%2FblMAiA%3D)
Example 3: Content inspection
A typical use case of the previous two examples is to control the content of the information being exchanged. For instance, we could use regular expressions matching inappropriate words to flag them.
Some apps that implement content inspection are:
For our Hello World app, let's look into inspecting file attachments.
In our main app file, we will implement
IPreFileUploadand print a message notifying the user that the attached file has been inspected.If the file type is
text/plain, we will also display the file's content. The main app file looks something like this:import { IAppAccessors, IHttp, ILogger, IModify, IPersistence, IRead, } from '@rocket.chat/apps-engine/definition/accessors'; import { App } from '@rocket.chat/apps-engine/definition/App'; import { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import { AppMethod, IAppInfo } from '@rocket.chat/apps-engine/definition/metadata'; import { IRoom } from '@rocket.chat/apps-engine/definition/rooms/IRoom'; import { IFileUploadContext, IPreFileUpload } from '@rocket.chat/apps-engine/definition/uploads'; import { IUser } from '@rocket.chat/apps-engine/definition/users/IUser'; export class HelloWorldApp extends App implements IPreFileUpload { public async executePreFileUpload( context: IFileUploadContext, read: IRead, http: IHttp, persis: IPersistence, modify: IModify ): Promise<void> { const user = await read.getUserReader().getById(context.file.userId); const room = await read.getRoomReader().getById(context.file.rid); if (!room || !user) { return; } // Build file details message let fileDetails = `File Uploaded - Name: ${context.file.name}\n`; fileDetails += `File Uploaded - Type: ${context.file.type}\n`; fileDetails += `File Uploaded - Size: ${context.file.size}`; // If text file, include content if (context.file.type === 'text/plain') { const content = String.fromCharCode.apply(null, context.content); fileDetails += `\nFile Uploaded - Content: ${content}`; } // Get app user to send the message const appUser = await read.getUserReader().getAppUser(); const sender = appUser ? appUser : user; // Send message to the room where file was uploaded await this.sendMessage(room, fileDetails, sender, modify); } private async sendMessage( room: IRoom, textMessage: string, author: IUser, modify: IModify ): Promise<void> { const messageBuilder = modify.getCreator().startMessage({ text: textMessage, } as IMessage); messageBuilder.setRoom(room); messageBuilder.setSender(author); await modify.getCreator().finish(messageBuilder); } }Save the file and deploy the app. Send an attachment to a room to test the app. The notification is like a temporary private message visible only to the user who sent the attachment (if you refresh the page, the notification is gone).
The following screenshot shows an example:.png?sv=2022-11-02&spr=https&st=2026-02-22T06%3A18%3A07Z&se=2026-02-22T06%3A35%3A07Z&sr=c&sp=r&sig=CkY%2FTTRTLwVkSniXBo%2BLXj3oUM17Jb5u7hKo%2FblMAiA%3D)
Great! With these examples, you have learned how to implement event interfaces and react to certain events. You have made significant progress in expanding your app!
In the upcoming sections, we will look at another way to extend your app's capabilities by creating interactive user experiences with the Apps-Engine UIKit.