🤖 Creating a Matrix Message Relay Bot with Deno and WASM #
In this post, we see how you can build a Matrix message relay bot API with Deno. Matrix is a secure messaging protocol, which Element and other message apps use. Element has similar features to apps like Telegram. It offers some enhancements over Telegram, such as end-to-end encrypted group chat within rooms. Another difference is that you can use Element to message peers who use other Matrix messaging apps. This is analogous to you being able to use your favourite email app to email contacts who prefer another app.
Although messaging bots today have some quite sophisticated applications, we will keep things simple. Our bot will relay messages via a Deno API to an existing Matrix Element chat room. As an example, you might use this API to let you know each time someone sends a contact form message on one of the sites you are managing. Using the Element smartphone app, you or your team can get the alert even if away from the desk.
What are we Building? #
We will create a Deno API to listen for new messages. The messages (sent from our contact form server code, for example) will arrive at the API as REST requests. Under the hood, to interface with the Matrix API, we will use the Rust Matrix SDK. WASM will provide the link between the Rust Matrix code and the Deno serverless app. If that sounds interesting to you, then let’s get going!
⚙️ Creating a Deno Server App #
We will start by writing the HTTP server code for the Deno app. The server will listen for POST
HTTP requests on the /matrix-message
route. To get going, create
a new directory for the project and in there add a main.ts
file with
this content:
Deno has an HTTP server included in its standard library. We import it in line 2
and start the server running in the last line of the code (line 49
). You see, that Deno.serve
call takes a handler
function as an argument. We define handler
from line 18
down. Deno uses standard JavaScript APIs, so the handler
function
probably does not contain anything too surprising to you. That will especially be the case if you have
already read through the getting started with Deno Fresh posts which runs through Deno APIs.
We have a logRequest
utility function defined in the code above. It
uses the Temporal API to add a timestamp to the message it creates. We add a deno.json
file next, with an import map for the Temporal module and the others we used above.
🛠️ Deno Config: deno.json
#
Create a deno.json
file in the project root directory and add the following
content:
This includes paths for imports as well as the tasks
we will run to
build WASM and start up the app. The tasks
here provide a similar function
to the scripts
you would find in a package.json
file.
Setting up the App to run WASM #
We can use the wasmbuild
task straight away to set up our project for
Deno WASM. Run this command in the Terminal:
If this is your first time running WASM in Deno take a look at the Deno Fresh WASM post where we walk through a few details. The last command should have created an rs_lib
directory in your project for our Rust code. We will update the Cargo.toml
file, in there, next.
🦀 Rusting Up #
Disclaimer time! I’m still learning Rust. I tested the code, and it works, though you might have a better way of doing things here. Keen to hear about improvements. You can drop a comment below or reach out on Twitter or via other channels.
Cargo.toml
#
Update the content in rs_lib/Cargo.toml
:
lib.rs
#
Next, update the content in rs_lib/src/lib.rs
:
You will create the matrix_client
module referenced here next.
matrix_client.rs
#
Finally, create rs_lib/src/matrix_client.rs
with the following content:
rs_lib/src/matrix_client.rs
— click to expand code.
Rust Summary #
In Cargo.toml
, we add the matrix-sdk
crate as well as wasm-bindgen-futures
. You need that last one
because we will call an async Rust function from the WASM in our Deno app. The function, we call
from the WASM is matrix_message
, defined in lib.rs
. It just sets up a Matrix client instance for us, then uses the instance to send the message.
The parameters we give in our Deno code will feed through.
Finally, turning to matrix_client.rs
. The 'a
’s sprinkled throughout the file are Rust lifetime annotations. I added them here because
when we create a new instance of MatrixClient
we will not reserve new
memory for username
, password
and other
parameters then copy them across. Instead, we “borrow” them. Effectively, we just store
their existing memory address and read their value directly when we need to.
The lifetime annotations help make our code memory safe, avoiding us accidentally trying to read the values after that memory has been freed, for example. For more explanation on the lifetime annotations, see the Rust Book .
🧱 Building WASM #
To build the WASM module, just run the command:
This will create all the WASM code we need to contact the Matrix room from our Deno app. You can
find the module in the lib
folder of your project.
Updating Deno App with WASM #
Let’s add the missing logic (for sending messages) to the main server file main.ts
now:
main.ts
— click to expand code.
The WASM module import statement is in lines 2
– 5
. Notice, we have an instantiate
function (generated for us) as well as the matrix_message
function
we wrote in Rust. Before we can call matrix_message
we need to make
a one-off call to this instantiate
function. That initializes the WASM
module.
The only thing we are missing now is the environment variables. We can add those next, then run a test.
🤐 Environment Variables #
Create a .env
file in the project root directory. Remember to add it
to your .gitignore
file, so your secrets will not get committed to
your remote repo.
To get the ELEMENT_ROOM_ID
in Element Matrix desktop:
- Select the room the bot will post to.
- Click the info button in the top right corner.
- Select Room settings then Advanced.
- Use the Internal room ID displayed here.
- how to create an HTTP server in Deno;
- how to use the Matrix SDK to send messages into a Matrix chat room; and
- an example of how you can generate WASM code from Rust with Deno tooling.
- listening for messages in the Element room and using AI to respond
- moderating messages received on the REST API (filtering out inappropriate content, for example) before relaying them
Which HTTP server should you use with Deno? #
- Deno has a built-in, fast HTTP server. You can access it via the Deno standard library. Unless you have a special reason, this would be your first choice. There is no need to add a third party HTTP sever module like you would in Node or Rust.
Does the Matrix Rust SDK compile to WASM? #
- We have seen you can compile an app using the Matrix Rust SDK into WASM. To get it to work, disable the default features, then enable the `js` and `native-tls` features. The generated WASM module can be quite large. This might be an issue if you want to serve your WASM from a Rust Cloudflare Worker. That said, it should fit comfortably within the 20 MB limit that Deno deploy offer.
How is Element Matrix different to Telegram? #
- Element is a secure messaging app implementing the Matrix protocol. Like Telegram, it has mobile and desktop apps. A key difference is Element offers end-to-end encryption in rooms as well as one-to-one conversations. The interface is intuitive and not too different to other message apps. You can also use Element to message peers who use other message apps but still run using the Matrix protocol.
You can create a new account for your bot at https://app.element.io/#/welcome
. Use your bot account username and password in your .env
file.
🗳 Poll #
💯 Matrix Message Relay Bot: Testing #
To spin up your app, run this command from the Terminal:
The server should now be listening on http://localhost:8080
. We
will use curl from another Terminal tab to test it with this command (sending a POST
request with plaintext message in the request body):
Check back in the Deno app Terminal tab. You should see a log message showing a 204
response code.
Also, if you open up Element Matrix, you should see the message:
🙌🏽 Matrix Message Relay Bot: Wrapping Up #
We had an introduction to how you can create a Matrix message relay bot. In particular, you saw:
The complete code for this project is in the Rodney Lab GitHub repo . I do hope the post has either helped you with an existing project or provided some inspiration for a new project. You might want to explore the Matrix Rust SDK further, considering extensions like:
Be sure to add authentication before pushing the code to your live server.
Get in touch if you have some tips on how I can improve my Rust. Also, let me know if you have suggestions for other content.
🏁 Matrix Message Relay Bot: Summary #
🙏🏽 Matrix Message Relay Bot: Feedback #
Have you found the post useful? Would you prefer to see posts on another topic instead? Get in touch with ideas for new posts. Also, if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, then please consider supporting me through Buy me a Coffee.
Just dropped a post on how you can create an Element Matrix bot REST API with 🦖 Deno to relay messages to your chat room.
— Rodney (@askRodney) February 17, 2023
We use a spot of 🦀 Rust WASM to work with the Matrix SDK.
Hope you find it useful!
#learndeno #takeTheRustPill #askRodneyhttps://t.co/wUfQzdnNTj
Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter, @rodney@toot.community on Mastodon and also the #rodney Element Matrix room. Also, see further ways to get in touch with Rodney Lab. I post regularly on Astro as well as Deno. Also, subscribe to the newsletter to keep up-to-date with our latest projects.