#### 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}]"}
    1844 views