diff --git a/Cargo.toml b/Cargo.toml index 8068eed19b16a102d18cab5c4c07d6250a95b568..61b4441708bea563bbf336b968d2f16a32d0a35b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ panic = 'abort' matrix-sdk = "0.4.1" tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "macros"] } rust-ini = "0.17" -url = "2.2.2" +url = "2.2" log = "0.4" log4rs = "1.0" +futures = "0.3" diff --git a/README.md b/README.md index 94c16eec6f85df8a28e092efb9c6bb12cdf0c5c1..f94396ae8001d763355d5b1378a0c10c8e803663 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,9 @@ toolchain, so a `rustup default nightly` might be needed. ## Help Here are the matrix sdk samples: https://github.com/matrix-org/matrix-rust-sdk/tree/main/crates/matrix-sdk/examples + +## TODO + +- Auto-join without restarting the bot +- When joining rooms at start-up, the bot no longer know the name of the rooms, + need another start-up to fix... diff --git a/src/matrix.rs b/src/matrix.rs index 78f91b08855d4a9e5cc1f03b3ffe3d41fba0e348..307c439ad04c11bd6fae0c09ccd1dd201743d397 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -2,17 +2,70 @@ use crate::config::Config; use log::{error, info, warn}; use matrix_sdk::{ deserialized_responses::EncryptionInfo, - room::Room, + room::{Invited, Room}, ruma::{ events::{ - room::message::{MessageEventContent, MessageType, TextMessageEventContent}, - AnyMessageEventContent, SyncMessageEvent, + room::{ + member::MemberEventContent, + message::{MessageEventContent, MessageType, TextMessageEventContent}, + }, + SyncMessageEvent, SyncStateEvent, }, UserId, }, - Client, Result, RoomInfo, SyncSettings, + Client, Result, SyncSettings, }; use std::convert::TryFrom; +use futures::join; +use tokio::time::{sleep, Duration}; + +async fn join_room(room: &Invited) { + info!("Autojoining room {}", room.room_id()); + let mut delay = 2; // seconds + + 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 + error!( + "Failed to join room {} ({:?}), retrying in {}s", + room.room_id(), + err, + delay + ); + + sleep(Duration::from_secs(delay)).await; + delay *= 2; + + if delay > 60 { + error!("Can't join room {} ({:?})", room.room_id(), err); + break; + } + } + info!("Successfully joined room {}", room.room_id()); +} + +async fn on_state_member( + room_member: SyncStateEvent<MemberEventContent>, + client: Client, + room: Room, +) { + let room_member_state_key = room_member.state_key; + let own_user_id = client.user_id().await.unwrap(); + if room_member_state_key != own_user_id { + error!( + "Room member state key {} don't match bot's id {}", + room_member_state_key, own_user_id + ); + return; + } + + match room { + Room::Joined(room) => warn!("Room {} already joined", room.room_id()), + Room::Invited(room) => join_room(&room).await, + Room::Left(room) => warn!("Leave room {}", room.room_id()), + }; +} async fn on_room_message( ev: SyncMessageEvent<MessageEventContent>, @@ -61,10 +114,17 @@ async fn on_room_message( } } -fn log_joined_rooms(client: &Client) { +async fn startup_and_log_rooms(client: &Client) { for room in client.joined_rooms() { info!("Got joined room: <{}>", room.name().unwrap()); } + for room in client.invited_rooms() { + warn!( + "Got pending invitation on room <{}>, try to join it", + room.name().unwrap() + ); + join_room(&room).await; + } } pub async fn connect_and_handle(config: Config) -> Result<()> { @@ -82,9 +142,11 @@ pub async fn connect_and_handle(config: Config) -> Result<()> { info!("Logged as: {}", alice); client.sync_once(SyncSettings::default()).await?; - log_joined_rooms(&client); - client.register_event_handler(on_room_message).await; + let startup_rooms = startup_and_log_rooms(&client); + let register_handle_state = client.register_event_handler(on_state_member); + let register_handle_message = client.register_event_handler(on_room_message); + join!(startup_rooms, register_handle_message, register_handle_state); info!("Entering sync loop"); let token = client.sync_token().await.unwrap();