use crate::controllers::HttpResult; use crate::controllers::matrix::matrix_event_controller::{APIEvent, get_events}; use crate::controllers::matrix::matrix_media_controller; use crate::extractors::matrix_client_extractor::MatrixClientExtractor; use actix_web::{HttpRequest, HttpResponse, web}; use futures_util::{StreamExt, stream}; use matrix_sdk::notification_settings::{ IsEncrypted, IsOneToOne, NotificationSettings, RoomNotificationMode, }; use matrix_sdk::room::ParentSpace; use matrix_sdk::ruma::events::receipt::{ReceiptThread, ReceiptType}; use matrix_sdk::ruma::{ MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedMxcUri, OwnedRoomId, OwnedUserId, }; use matrix_sdk::{Room, RoomMemberships}; #[derive(serde::Serialize)] pub struct APIRoomInfo { id: OwnedRoomId, name: Option, members: Vec, avatar: Option, is_space: bool, parents: Vec, number_unread_messages: u64, notifications: RoomNotificationMode, latest_event: Option, } impl APIRoomInfo { async fn from_room(r: &Room, notif: &NotificationSettings) -> anyhow::Result { // Get parent spaces let parent_spaces = r .parent_spaces() .await? .collect::>() .await .into_iter() .collect::, _>>()? .into_iter() .filter_map(|d| match d { ParentSpace::Reciprocal(r) | ParentSpace::WithPowerlevel(r) => { Some(r.room_id().to_owned()) } _ => None, }) .collect::>(); let members = r .members(RoomMemberships::ACTIVE) .await? .into_iter() .map(|r| r.user_id().to_owned()) .collect::>(); let notifications = notif .get_user_defined_room_notification_mode(r.room_id()) .await .unwrap_or( notif .get_default_room_notification_mode( IsEncrypted::from(r.encryption_state().is_encrypted()), IsOneToOne::from(members.len() == 2), ) .await, ); Ok(Self { id: r.room_id().to_owned(), name: r.name(), members, avatar: r.avatar_url(), is_space: r.is_space(), parents: parent_spaces, number_unread_messages: r.unread_notification_counts().notification_count, notifications, latest_event: get_events(r, 1, None, None) .await? .events .into_iter() .next(), }) } } /// Get the list of joined rooms of the user pub async fn joined_rooms(client: MatrixClientExtractor) -> HttpResult { let notifs = client.client.client.notification_settings().await; let list = stream::iter(client.client.client.joined_rooms()) .then(async |room| APIRoomInfo::from_room(&room, ¬ifs).await) .collect::>() .await .into_iter() .collect::, _>>()?; Ok(HttpResponse::Ok().json(list)) } /// Get joined spaces rooms of user pub async fn get_joined_spaces(client: MatrixClientExtractor) -> HttpResult { let notifs = client.client.client.notification_settings().await; let list = stream::iter(client.client.client.joined_space_rooms()) .then(async |room| APIRoomInfo::from_room(&room, ¬ifs).await) .collect::>() .await .into_iter() .collect::, _>>()?; Ok(HttpResponse::Ok().json(list)) } #[derive(serde::Deserialize)] pub struct RoomIdInPath { pub(crate) room_id: OwnedRoomId, } /// Get the list of joined rooms of the user pub async fn single_room_info( client: MatrixClientExtractor, path: web::Path, ) -> HttpResult { let notifs = client.client.client.notification_settings().await; Ok(match client.client.client.get_room(&path.room_id) { None => HttpResponse::NotFound().json("Room not found"), Some(r) => HttpResponse::Ok().json(APIRoomInfo::from_room(&r, ¬ifs).await?), }) } /// Get room avatar pub async fn room_avatar( req: HttpRequest, client: MatrixClientExtractor, path: web::Path, ) -> HttpResult { let Some(room) = client.client.client.get_room(&path.room_id) else { return Ok(HttpResponse::NotFound().json("Room not found")); }; let Some(uri) = room.avatar_url() else { return Ok(HttpResponse::NotFound().json("Room has no avatar")); }; matrix_media_controller::serve_mxc_file(req, uri).await } #[derive(serde::Serialize)] pub struct UserReceipt { user: OwnedUserId, event_id: OwnedEventId, ts: Option, } /// Get room receipts pub async fn receipts(client: MatrixClientExtractor, path: web::Path) -> HttpResult { let Some(room) = client.client.client.get_room(&path.room_id) else { return Ok(HttpResponse::NotFound().json("Room not found")); }; let members = room.members(RoomMemberships::ACTIVE).await?; let mut receipts = Vec::new(); for m in members { let Some((event_id, receipt)) = room .load_user_receipt(ReceiptType::Read, ReceiptThread::Main, m.user_id()) .await? else { continue; }; receipts.push(UserReceipt { user: m.user_id().to_owned(), event_id, ts: receipt.ts, }) } Ok(HttpResponse::Ok().json(receipts)) }