Another great content created by the amazing members of our Community. This tutorial is brought to you by Rohan Lekhwani - so a special thanks to him! The original article was posted on his Medium page and you can check it by clicking here.
How to create a Rocket.Chat App
Rocket.Chat can be easily extended with powerful Apps.
Here is a step by step of how I created MemeBuddy for Rocket.Chat.
Setting up shop
First you will need a server to test the app, there exists a multitude of setup options available.
On your development machine, you will need to install the Rocket.Chat Apps-Engine CLI. Follow these docs.
Creating MemeBuddy App
Generate a starter for the app using the CLI:
When chatting, you can trigger MemeBuddy at any time to generate a Meme. This is done through a slash command.
Crafting Slash Commands
To code the slash command, create a commands directory and a meme.ts file within it. This file would have an executor function that tells the app what to do when someone sends a /meme within a channel.
public async executor( context: SlashCommandContext, read: IRead, modify: IModify, http: IHttp, persistence: IPersistence): Promise {
console.log(“A small step for man, a giant leap for mankind”)
}
Register the MemeCommand in the generated MemeBuddyApp.ts file. Do it within the extendConfiguration method with the following line of code.
await configuration.slashCommands.provideSlashCommand(new MemeCommand(this));
Congratulations! Your first Rocket.Chat App is completed.
Loading app on server
Get it onto your server, run the following command:
rc-apps deploy --url http://localhost:3000 --username <username> --password <password>
See it in action. Run the /meme command in any channel. You should see a log message from Neil Armstrong.
A small step for man, a giant leap for mankind.
O.K. buddy! That was simple, but the app doesn’t do much. Let’s add the memes.
Note — You can sideload apps directly onto a production server without going through the Marketplace. Sideloading apps requires enabling development mode on the server. This can be done by navigating to Administration -> General and scrolling down to the Apps section to switch on the “Enable Development Mode” radio button.
Calling APIs of external systems — Gimme the Memes
Apps in Rocket.Chat often reach out to large external systems to get data or have work done. Apps call out on RESTful APIs. MemeBuddy calls D3vd’s Meme API for fetching memes.
In MemeBuddy, we use http.get() and supply REST arguments subreddit and number of memes to a hosted server at:
https://meme-api.herokuapp.com/gimme/wholesomememes/1
The result is:
Adding interactive UI Components Into Chat Flow
Time to get fancy, let’s send that message into the channel together with interactive clickable buttons for the users to select their memes.
After all everyone has a unique meme-o-scope. Right? (bad joke, noted)
Rocket.Chat Apps use UIKit to build rich message blocks. If you’re already familiar with Slack’s BlockKit , the IModify interface is where everything happens.
Create a file named initiatorMessage.ts in a lib directory. Add an initiatorMessage function:
export async function initiatorMessage({ data, read, persistence, modify, http}: { data; read: IRead; persistence: IPersistence; modify: IModify; http: IHttp;})
{
// Function Body
}
To send a simple text message into a channel is 2 lines of code:
const greetBuilder = await modify
.getCreator()
.startMessage()
.setRoom(data.room)
.setText(`Hey _${data.sender.username}_ !`);
await modify.getCreator().finish(greetBuilder);
IModify helps to send rich messages that can contain interactive user interface elements. Use its getBlockBuilder() to get the BlockBuilder class and insert block elements within messages. Here, I add three buttons for the categories.
block.addActionsBlock({
blockId: “subreddits”,
elements: [
block.newButtonElement({
actionId: “memeSelect”,
text: block.newPlainTextObject(“Programmer”),
value: “programmerhumor”,
style: ButtonStyle.PRIMARY,}),
block.newButtonElement({
// … Other keys similar to above
value: “dankmemes”,}),
block.newButtonElement({
// … Other keys similar to above
value: “wholesomememes”,}),
],
})
Making interactive messages visible only to sender
We are in a public channel but only the user interacting with MemeBuddy should see the buttoned message. Use the getNotifier and notifyUser methods for this.
await modify
.getNotifier()
.notifyUser(data.sender, builder.getMessage());
The message stays around only for the user session. A /meme would now give us the following:
The buttons don’t do anything yet. Let’s fix it.
Wiring button actions to make REST API calls
Clicking the button should trigger its category of meme to be fetched from Reddit via the REST APIs.
Wire it up with executeBlockActionHandler() in the MemeBuddyApp class.
Calling context.getInteractionData() we get the action details. If the actionId is memeSelect it implies that one of the three buttons was clicked.
data.value is returned by the button upon clicking (the subreddit to fetch memes from in our case). Use it to get the memes via REST API.
public async executeBlockActionHandler(context: UIKitBlockInteractionContext, http: IHttp, //… Other parameters)
{
const data = context.getInteractionData();
const { actionId } = data;
switch (actionId) {
case “memeSelect”: {
try {
const memeResponse = await http.get(`https://meme-api.herokuapp.com/gimme/${data.value}/1`);
// Send memeResponse through a message here
return {
success: true,
};
} catch (err){// Handle Error }
} // end of case block
} // end of switch block
} // end of function block
Sending graphics via message attachment
Memes are sent in-channel via message attachments.
Create a class MemeAsImageAttachment within the lib directory that implements IMessageAttachment.
import { IMessageAttachment } from ‘@rocket.chat/apps-engine/definition/messages’;
export class MemeAsImageAttachment implements IMessageAttachment
{
imageUrl?: string;
constructor(imgUrl: string){
this.imageUrl = imgUrl;
}
We can now build an attachment into the message using IMesssageExtender::addAttachment().
const memeSender = await modify
.getCreator()
.startLivechatMessage()
.setVisitor(data.visitor)
.setText(`*${memeResponse.data.memes[0].title}*`)
.addAttachment(
new MemeAsImageAttachment(`${memeResponse.data.memes[0].url}`)
);
This completes MemeBuddy, ready for action in any team-chat channel.
Adapting App for LiveChat — Omnichannel Operation
Rocket.Chat is also a great platform for building real-time omnichannel customer service websites and community engagement portals. Its LiveChat widget is used by millions of users on a daily basis.
Adapting MemeBuddy to work with the LiveChat widget is straightforward.
There is no slash command in LiveChat, but recognition of “trigger words” in messages can take its place.
Parsing trigger words in LiveChat messages
To access every message sent, make the MemeBuddyApp class implement IPostMessageSent interface and hook executePostMessageSent(). Here, I check for the trigger word :meme:
if (message.room.type !== “l”) {
return;
}
if (message.text === “:meme:”) {
const data = {
room: message.room,
sender: message.sender,
};
await initiatorMessage({ data, read, persistence, modify, http });
} // end of if block
} // end of function block
Interactive button clicks within LiveChat messages are handled through the executeLiveChatBlockActionHandler() method. The differences when sending messages to LiveChat are:
- 1. Use of startLiveChatMessage instead of startMessage.
- 2. Setting the visitor for sending the message within LiveChat.
- 3. Notifiers are not applicable for LiveChat messages.
Now MemeBuddy works on both the team chat and the omnichannel LiveChat widget!
Publish your amazing app for the world
You now know everything there is to know about building Rocket.Chat apps.
If you need to review anything, find the MemeBuddy repo here.
Rocket.Chat operates a Rocket.Chat Apps Marketplace that can make your (free or paid) app available to every single Rocket.Chat installation in the universe.
I can’t wait to see what innovative new app you will build!
Frequently asked questions about <anything>
- Digital sovereignty
- Federation capabilities
- Scalable and white-labeled
- Highly scalable and secure
- Full patient conversation history
- HIPAA-ready
- Secure data governance and digital sovereignty
- Trusted by State, Local, and Federal agencies across the world
- Matrix federation capabilities for cross-agency communication
- Open source code
- Highly secure and scalable
- Unmatched flexibility
- End-to-end encryption
- Cloud or on-prem deployment
- Supports compliance with HIPAA, GDPR, FINRA, and more
- Supports compliance with HIPAA, GDPR, FINRA, and more
- Highly secure and flexible
- On-prem or cloud deployment