changed 3 years ago
Published Linked with GitHub

Rust SDK 101

Baby's first Rust Matrix Bot

slides: https://hackmd.io/@gnunicorn/matrix-rust-sdk-101#/


We have a collaborative session

please prepare you laptop


Installing the Dev Environment


Non-code Setup:

  • A matrix client with a signed in account
  • a second matrix account for your bot (we recommend to use a dedicated test account, you can just register a new account via element web)

Start your first bot*

  • open a terminal in matrix-rust-sdk
  • run cargo run -p example-getting-started -- <homeserver_url> <username> <password>

Use your favorite Matrix client, invite the bot into a channel (or create a DM) and post !party. See what happens.


So how did this happen?

Let's look at the code.


The CLI setup

async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); let (homeserver_url, username, password) = match (env::args().nth(1), env::args().nth(2), env::args().nth(3)) { (Some(a), Some(b), Some(c)) => (a, b, c), _ => { eprintln!( "Usage: {} <homeserver_url> <username> <password>", env::args().next().unwrap() ); exit(1) } }; login_and_sync(homeserver_url, &username, &password).await?; Ok(()) }

examples/getting_started/src/main.rs


The actual SDK setup:

async fn login_and_sync( homeserver_url: String, username: &str, password: &str, ) -> anyhow::Result<()> { // first, we set up the client. We use the convenient client builder to set our // custom homeserver URL on it #[allow(unused_mut)] let mut client_builder = Client::builder().homeserver_url(homeserver_url); // Matrix-SDK has support for pluggable, configurable state and crypto-store // support we use the default sled-store (enabled by default on native // architectures), to configure a local cache and store for our crypto keys let home = dirs::data_dir().expect("no home directory found").join("getting_started"); client_builder = client_builder.sled_store(home, None)?; // alright, let's make that into a client let client = client_builder.build().await?; // then let's log that client in client .login_username(username, password) .initial_device_display_name("getting started bot") .send() .await?; // it worked! println!("logged in as {username}");

examples/getting_started/src/main.rs


Attaching the message handlers

client.add_event_handler(on_stripped_state_member).await; client.sync_once(SyncSettings::default()).await.unwrap(); client.add_event_handler(on_room_message).await; let settings = SyncSettings::default().token(client.sync_token().await.unwrap()); client.sync(settings).await; // this essentially loops until we kill the bot Ok(()) }

examples/getting_started/src/main.rs


The autojoin handler

async fn on_stripped_state_member( room_member: StrippedRoomMemberEvent, client: Client, room: Room, ) { if room_member.state_key != client.user_id().unwrap() { return; } if let Room::Invited(room) = room { println!("Autojoining room {}", room.room_id()); let mut delay = 2; while let Err(err) = room.accept_invitation().await { // retry autojoin due to synapse sending invites, before the // invited user can join for more information see // https://github.com/matrix-org/synapse/issues/4345 eprintln!("Failed to join room {} ({err:?}), retrying in {delay}s", room.room_id()); sleep(Duration::from_secs(delay)).await; delay *= 2; if delay > 3600 { eprintln!("Can't join room {} ({err:?})", room.room_id()); break; } } println!("Successfully joined room {}", room.room_id()); } }

examples/getting_started/src/main.rs


The message posting handler

async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) { if let Room::Joined(room) = room { let msg_body = match event.content.msgtype { MessageType::Text(TextMessageEventContent { body, .. }) => body, _ => return, }; if msg_body.contains("!party") { let content = RoomMessageEventContent::text_plain("🎉🎊🥳 let's PARTY!! 🥳🎊🎉"); println!("sending"); room.send(content, None).await.unwrap(); println!("message sent"); } } }

examples/getting_started/src/main.rs


Let's change things

how about

  • Adding another command
    • one that does react on a different keyword - e.g. ping
    • one that reacts on say: and posts the remainder in a new message
  • What about encryption?

Thank you!
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  1. slides at
    https://hackmd.io/@gnunicorn/matrix-rust-sdk-101#/6
  2. Rust SDK chat at
    https://matrix.to/#/#matrix-rust-sdk:matrix.org
  3. Ben can be found at:
    gnunicorn.org | @gnunicorn:matrix.org
Select a repo