use axum::extract::State; use conduwuit::{ Err, Event, Result, at, matrix::pdu::PduEvent, utils::{ future::TryExtExt, stream::{BroadbandExt, ReadyExt}, }, }; use futures::{StreamExt, future::join}; use ruma::{ api::client::membership::{ get_member_events::{self, v3::MembershipEventFilter}, joined_members::{self, v3::RoomMember}, }, events::{ StateEventType, room::member::{MembershipState, RoomMemberEventContent}, }, }; use crate::Ruma; /// # `POST /_matrix/client/r0/rooms/{roomId}/members` /// /// Lists all joined users in a room (TODO: at a specific point in time, with a /// specific membership). /// /// - Only works if the user is currently joined pub(crate) async fn get_member_events_route( State(services): State, body: Ruma, ) -> Result { let sender_user = body.sender_user(); let membership = body.membership.as_ref(); let not_membership = body.not_membership.as_ref(); if !services .rooms .state_accessor .user_can_see_state_events(sender_user, &body.room_id) .await { return Err!(Request(Forbidden("You don't have permission to view this room."))); } Ok(get_member_events::v3::Response { chunk: services .rooms .state_accessor .room_state_full(&body.room_id) .ready_filter_map(Result::ok) .ready_filter(|((ty, _), _)| *ty == StateEventType::RoomMember) .map(at!(1)) .ready_filter_map(|pdu| membership_filter(pdu, membership, not_membership)) .map(Event::into_format) .collect() .await, }) } /// # `POST /_matrix/client/r0/rooms/{roomId}/joined_members` /// /// Lists all members of a room. /// /// - The sender user must be in the room /// - TODO: An appservice just needs a puppet joined pub(crate) async fn joined_members_route( State(services): State, body: Ruma, ) -> Result { if !services .rooms .state_accessor .user_can_see_state_events(body.sender_user(), &body.room_id) .await { return Err!(Request(Forbidden("You don't have permission to view this room."))); } Ok(joined_members::v3::Response { joined: services .rooms .state_cache .room_members(&body.room_id) .map(ToOwned::to_owned) .broad_then(|user_id| async move { let (display_name, avatar_url) = join( services.users.displayname(&user_id).ok(), services.users.avatar_url(&user_id).ok(), ) .await; (user_id, RoomMember { display_name, avatar_url }) }) .collect() .await, }) } fn membership_filter( pdu: PduEvent, for_membership: Option<&MembershipEventFilter>, not_membership: Option<&MembershipEventFilter>, ) -> Option { let membership_state_filter = match for_membership { | Some(MembershipEventFilter::Ban) => MembershipState::Ban, | Some(MembershipEventFilter::Invite) => MembershipState::Invite, | Some(MembershipEventFilter::Knock) => MembershipState::Knock, | Some(MembershipEventFilter::Leave) => MembershipState::Leave, | Some(_) | None => MembershipState::Join, }; let not_membership_state_filter = match not_membership { | Some(MembershipEventFilter::Ban) => MembershipState::Ban, | Some(MembershipEventFilter::Invite) => MembershipState::Invite, | Some(MembershipEventFilter::Join) => MembershipState::Join, | Some(MembershipEventFilter::Knock) => MembershipState::Knock, | Some(_) | None => MembershipState::Leave, }; let evt_membership = pdu.get_content::().ok()?.membership; if for_membership.is_some() && not_membership.is_some() { if membership_state_filter != evt_membership || not_membership_state_filter == evt_membership { None } else { Some(pdu) } } else if for_membership.is_some() && not_membership.is_none() { if membership_state_filter != evt_membership { None } else { Some(pdu) } } else if not_membership.is_some() && for_membership.is_none() { if not_membership_state_filter == evt_membership { None } else { Some(pdu) } } else { Some(pdu) } }