#### 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
- Install Rust from:
https://rustup.rs
- Have Git & your favorite Editor ready
_or_ simply install VSCode: https://code.visualstudio.com/
- clone: https://github.com/matrix-org/matrix-rust-sdk.git
---
## 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](https://app.element.io))
---
## 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
```rust=35
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(())
}
```
<small>`examples/getting_started/src/main.rs`</small>
---
The actual SDK setup:
```rust=59
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}");
```
<small>`examples/getting_started/src/main.rs`</small>
---
Attaching the message handlers
```rust=88
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(())
}
```
<small>`examples/getting_started/src/main.rs`</small>
---
The autojoin handler
```rust=116
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());
}
}
```
<small>`examples/getting_started/src/main.rs`</small>
---
The message posting handler
```rust=154
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");
}
}
}
```
<small>`examples/getting_started/src/main.rs`</small>
---
### 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! :sheep:
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
{"metaMigratedAt":"2023-06-17T03:22:34.173Z","metaMigratedFrom":"YAML","title":"Rust Matrix SDK 101 -- baby\\'s first matrix rust bot","breaks":true,"description":"View the slide with \"Slide Mode\".","contributors":"[{\"id\":\"7c6aab33-6425-4708-b066-3fd2f68e020f\",\"add\":10210,\"del\":6908}]"}