diff options
author | stuebinm | 2024-03-27 23:10:02 +0100 |
---|---|---|
committer | stuebinm | 2024-03-27 23:10:02 +0100 |
commit | bade89a506c380a7d4cab4fdd765e28686c14776 (patch) | |
tree | 451bd9fdee32b03c159bcbff71699afaaef48341 /src/matrix_bot.rs |
simple bot to play around with
Diffstat (limited to 'src/matrix_bot.rs')
-rw-r--r-- | src/matrix_bot.rs | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/src/matrix_bot.rs b/src/matrix_bot.rs new file mode 100644 index 0000000..7618b0d --- /dev/null +++ b/src/matrix_bot.rs @@ -0,0 +1,134 @@ +use std::{sync::Arc, ops::Deref}; +use tokio::sync::Mutex; + +use anyhow::Context; +use matrix_sdk::{ + ruma::{events::room::member::StrippedRoomMemberEvent, api::client::uiaa, OwnedRoomId, OwnedRoomOrAliasId}, Client, Room, +}; +use secrecy::{Secret, ExposeSecret}; +use tokio::time::{sleep, Duration}; + +#[tracing::instrument(skip(client, configured_rooms, room, state))] +pub async fn join_rooms_on_invite( + event: StrippedRoomMemberEvent, + client: Client, + room: Room, + configured_rooms: Vec<OwnedRoomId>, + state: Arc<Mutex<Vec<Room>>> +) { + if event.state_key == client.user_id().unwrap() { + let room_id = room.room_id().to_owned(); + if configured_rooms.contains(&room.room_id().into()) { + tokio::spawn(async move { + join_room_with_cooldown(&room).await; + state.lock().await.push(room); + tracing::info!("Successfully joined room {room_id}"); + }); + } + else { + tracing::warn!("was invited to room {room_id} which is not configured"); + if let Err(e) = room.leave().await { + tracing::error!("error leaving room {room_id}: {e}"); + } + } + } +} + +async fn join_room_with_cooldown(room: &Room) { + tracing::info!("Autojoining room {}", room.room_id()); + let mut delay = 2; + let room_id = room.room_id().to_owned(); + + while let Err(err) = room.join().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 + tracing::error!("Failed to join room {room_id} ({err:?}), retrying in {delay}s"); + + sleep(Duration::from_secs(delay)).await; + delay *= 2; + + if delay > 3600 { + eprintln!("Can't join room {} ({err:?})", room.room_id()); + break; + } + } +} + +pub async fn resolve_room_aliases(client: &Client, aliases: &Vec<OwnedRoomOrAliasId>) -> Vec<OwnedRoomId> { + futures_util::future::join_all (aliases + .iter() + .map(|name| async {match name.deref().try_into() { + Ok(alias) => Some(client.resolve_room_alias(alias).await.ok()?.room_id), + Err(id) => Some(id.into()) + }})) + .await + .into_iter() + .filter_map(|o: Option<_>| o) + .collect() +} + +#[tracing::instrument(skip(client))] +pub async fn join_configured_rooms(client: &Client, configured_rooms: &Vec<OwnedRoomId>) -> anyhow::Result<()> { + let joined_rooms = client.joined_rooms(); + + let missing_rooms = configured_rooms + .iter() + .filter(|room_id| !joined_rooms.iter().any(|room| room.room_id() == *room_id)) + .collect::<Vec<_>>(); + + let extra_rooms = joined_rooms + .iter() + .filter(|room| !configured_rooms.contains(&room.room_id().into())) + .collect::<Vec<_>>(); + + for room_id in missing_rooms { + tracing::info!("attempting to join room {room_id:?}"); + if let Err(e) = client.join_room_by_id(room_id).await { + tracing::error!("could not join room {}", e) + } + } + + for room in extra_rooms { + tracing::info!("leaving room {}", room.room_id()); + if let Err(e) = room.leave().await { + tracing::error!("could not leave room {}", e) + } + } + + Ok(()) +} + +#[tracing::instrument(skip(password))] +pub async fn delete_other_devices(client: &Client, username: &str, password: &Secret<String>) -> anyhow::Result<()> { + let own_id = client + .device_id() + .with_context(|| "no own device id?")?; + + let devices = client.devices().await? + .devices + .into_iter() + .map(|device| device.device_id) + .filter(|id| id != own_id) + .collect::<Vec<_>>(); + + tracing::info!("deleting devices {:?}", devices); + + // deleting devices is a funny double-request thing since it requires the + // user's password; see the documentation on delete_devices() + if let Err(e) = client.delete_devices(&devices, None).await { + if let Some(info) = e.as_uiaa_response() { + let mut password = uiaa::Password::new( + uiaa::UserIdentifier::UserIdOrLocalpart(username.to_owned()), + password.expose_secret().to_owned(), + ); + password.session = info.session.clone(); + + client + .delete_devices(&devices, Some(uiaa::AuthData::Password(password))) + .await?; + } + } + + Ok(()) +} |