Outdated documentation
This documentation hasn't been updated in a while. Some information might no longer be valid.
You can find the latest version of the documentation at https://turt2live.github.io/matrix-bot-sdk/index.html
Usage of matrix-bot-sdk
This article concerns matrix-bot-sdk, a TypeScript client SDK for Matrix. We'll build a simple "echo bot", meaning a bot which replies to messages with the text it has just read.
Note that although the SDK is written in TypeScript, we'll use JavaScript in our examples. If you'd prefer to use TypeScript, then do!
Setup
Let's make a new folder, and import our only npm dependency. The following examples are all meant to be run in a bash terminal.
mkdir matrix-js-echo-bot
cd matrix-js-echo-bot
npm install matrix-bot-sdk
Create a new file named "index.js", and let's get started.
Instantiation
In our js file, start by importing the minimum we'll need for this example:
const sdk = require("matrix-bot-sdk");
const MatrixClient = sdk.MatrixClient;
const SimpleFsStorageProvider = sdk.SimpleFsStorageProvider;
const AutojoinRoomsMixin = sdk.AutojoinRoomsMixin;
Create a new account for your bot on a homeserver, then get the access_token
.
The simplest way to do this is using Element, take a look at these
instructions. Set some variables to
store the homeserver and access_token
. This is all the authentication you
need!
const homeserverUrl = "https://matrix.org"; // make sure to update this with your url
const accessToken = "YourSecretAccessToken";
Now we'll configure a storage provider - matrix-bot-sdk provides the
SimpleFsStorageProvider
, which is ideal for most cases:
const storage = new SimpleFsStorageProvider("bot.json");
When the bot starts, the SDK will create a a new file called "bot.json" to store the data it needs.
Finally we're ready to start the client! As you'd expect, we'll use the variables we've already specified.
const client = new MatrixClient(homeserverUrl, accessToken, storage);
There is one more thing we need to do. We'll include a mixin which instructs the bot to auto-accept any room invite it receives. This makes testing much more convenient.
AutojoinRoomsMixin.setupOnClient(client);
Finally, let's start the Client:
client.start().then(() => console.log("Client started!"));
If you're keeping up, your code should look something like:
import {
MatrixClient,
SimpleFsStorageProvider,
AutojoinRoomsMixin
} from "matrix-bot-sdk";
const homeserverUrl = "https://matrix.org"; // make sure to update this with your url
const accessToken = "YourSecretAccessToken";
const storage = new SimpleFsStorageProvider("bot.json");
const client = new MatrixClient(homeserverUrl, accessToken, storage);
AutojoinRoomsMixin.setupOnClient(client);
client.start().then(() => console.log("Client started!"));
Let's run it:
node index.js
This should now join and sit idle, but join any room you invite the bot to.
/sync loop
Right now, while it's just listening to invites and nothing else, what is the
bot actually doing? It's calling the /sync
endpoint in a loop. Calling this
endpoint returns all new events since some previous point.
Leave the script running and open bot.json
, which is the file we specified for
storage. This file contains a field syncToken
, which is being occasionally
updated - the SDK uses this field to give a token to the homeserver, which uses
it to know which events to send back.
Receiving and Sending events
In order to echo messages, our bot must first be able to read them. The
client.on()
method of our MatrixClient takes two arguments: one for the event
type, one for a callback to handle the event:
client.on("room.message", (roomId, event) => {
if (! event["content"]) return;
const sender = event["sender"];
const body = event["content"]["body"];
console.log(`${roomId}: ${sender} says '${body}`);
});
This way we can inspect the contents of an event and render them. We
choose to exit early in the case that event["content"]
is empty because this
will usually mean the message was redacted.
To send a message, we use the client.sendMessage()
method. This takes two
arguments: the roomId, and a JSON object containing the contents of the message
to send, for example:
client.sendMessage(roomId, {
"msgtype": "m.text",
"body": "This is message text.",
});
Note, it's also possible to use client.sendText()
to achieve the same result,
as in
client.sendText(roomId, "This is message text.")
The reason for showing client.sendMessage()
is to make it clear that the
message format is just the same as you'd find in the spec
.
Implementing echobot functionality
To work, an echobot needs only to listen for incoming messages, read the message text, and use it to reply. Let's demonstrate that now.
- Read the message as in the example above
- Inspect the body text, if it starts with "!echo", send back the remaining text
- Strip out the "!echo" tag
- Send a message containing the result
client.on("room.message", (roomId, event) => {
if (! event["content"]) return;
const sender = event["sender"];
const body = event["content"]["body"];
console.log(`${roomId}: ${sender} says '${body}`);
if (body.startsWith("!echo")) {
const replyText = body.substring("!echo".length).trim();
client.sendMessage(roomId, {
"msgtype": "m.notice",
"body": replyText,
});
}
});
Conclusion
It's extremely simple to listen to messages with matrix-bot-sdk create an echobot! There are many more features, you can see the MatrixClient class is very well documented . Next in this series, we'll explore Rich Replies, and take a look at the kick and ban functions for room administration.
PS, use TypeScript
This SDK uses TypeScript, which provides a lot of benefits. In this example, we used JavaScript, but it's just as easy to use TypeScript and maybe preferable, since it is the language matrix-bot-sdk is written in.
First let's install tsc
, which compiles from TypeScript to JavaScript:
npm install typescript
Now, start tsc in watch-mode (-w
), and leave it to compile our code:
npx tsc --watch *.ts
Now, whenever we create a new TypeScript (.ts
) file, it will be automatically
watched and compiled to JavaScript.
When you have your .js file(s), you can run them with node <filename>
as
normal.