fixup+update msc3266, add fed support, parallelise IO

Signed-off-by: June Clementine Strawberry <june@3.dog>
This commit is contained in:
June Clementine Strawberry 2025-04-02 22:51:17 -04:00
parent 1036f8dfa8
commit 0e0b8cc403
No known key found for this signature in database
8 changed files with 389 additions and 174 deletions
Cargo.lockCargo.toml
src
api/client
service/rooms
spaces
state_accessor

22
Cargo.lock generated
View file

@ -3531,7 +3531,7 @@ dependencies = [
[[package]]
name = "ruma"
version = "0.10.1"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"assign",
"js_int",
@ -3551,7 +3551,7 @@ dependencies = [
[[package]]
name = "ruma-appservice-api"
version = "0.10.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"js_int",
"ruma-common",
@ -3563,7 +3563,7 @@ dependencies = [
[[package]]
name = "ruma-client-api"
version = "0.18.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"as_variant",
"assign",
@ -3586,7 +3586,7 @@ dependencies = [
[[package]]
name = "ruma-common"
version = "0.13.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"as_variant",
"base64 0.22.1",
@ -3618,7 +3618,7 @@ dependencies = [
[[package]]
name = "ruma-events"
version = "0.28.1"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"as_variant",
"indexmap 2.8.0",
@ -3643,7 +3643,7 @@ dependencies = [
[[package]]
name = "ruma-federation-api"
version = "0.9.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"bytes",
"headers",
@ -3665,7 +3665,7 @@ dependencies = [
[[package]]
name = "ruma-identifiers-validation"
version = "0.9.5"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"js_int",
"thiserror 2.0.12",
@ -3674,7 +3674,7 @@ dependencies = [
[[package]]
name = "ruma-identity-service-api"
version = "0.9.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"js_int",
"ruma-common",
@ -3684,7 +3684,7 @@ dependencies = [
[[package]]
name = "ruma-macros"
version = "0.13.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"cfg-if",
"proc-macro-crate",
@ -3699,7 +3699,7 @@ dependencies = [
[[package]]
name = "ruma-push-gateway-api"
version = "0.9.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"js_int",
"ruma-common",
@ -3711,7 +3711,7 @@ dependencies = [
[[package]]
name = "ruma-signatures"
version = "0.15.0"
source = "git+https://github.com/girlbossceo/ruwuma?rev=d197318a2507d38ffe6ee524d0d52728ca72538a#d197318a2507d38ffe6ee524d0d52728ca72538a"
source = "git+https://github.com/girlbossceo/ruwuma?rev=ea1278657125e9414caada074e8c172bc252fb1c#ea1278657125e9414caada074e8c172bc252fb1c"
dependencies = [
"base64 0.22.1",
"ed25519-dalek",

View file

@ -346,7 +346,7 @@ version = "0.1.2"
[workspace.dependencies.ruma]
git = "https://github.com/girlbossceo/ruwuma"
#branch = "conduwuit-changes"
rev = "d197318a2507d38ffe6ee524d0d52728ca72538a"
rev = "ea1278657125e9414caada074e8c172bc252fb1c"
features = [
"compat",
"rand",

View file

@ -2,9 +2,14 @@ mod aliases;
mod create;
mod event;
mod initial_sync;
mod summary;
mod upgrade;
pub(crate) use self::{
aliases::get_room_aliases_route, create::create_room_route, event::get_room_event_route,
initial_sync::room_initial_sync_route, upgrade::upgrade_room_route,
aliases::get_room_aliases_route,
create::create_room_route,
event::get_room_event_route,
initial_sync::room_initial_sync_route,
summary::{get_room_summary, get_room_summary_legacy},
upgrade::upgrade_room_route,
};

View file

@ -0,0 +1,308 @@
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::{
Err, Result, debug_warn,
utils::{IterStream, future::TryExtExt},
};
use futures::{FutureExt, StreamExt, future::join3, stream::FuturesUnordered};
use ruma::{
OwnedRoomId, OwnedServerName, RoomId, UserId,
api::{
client::room::get_summary,
federation::space::{SpaceHierarchyParentSummary, get_hierarchy},
},
events::room::member::MembershipState,
space::SpaceRoomJoinRule::{self, *},
};
use service::Services;
use crate::{Ruma, RumaResponse};
/// # `GET /_matrix/client/unstable/im.nheko.summary/rooms/{roomIdOrAlias}/summary`
///
/// Returns a short description of the state of a room.
///
/// This is the "wrong" endpoint that some implementations/clients may use
/// according to the MSC. Request and response bodies are the same as
/// `get_room_summary`.
///
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
pub(crate) async fn get_room_summary_legacy(
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_summary::msc3266::Request>,
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
get_room_summary(State(services), InsecureClientIp(client), body)
.boxed()
.await
.map(RumaResponse)
}
/// # `GET /_matrix/client/unstable/im.nheko.summary/summary/{roomIdOrAlias}`
///
/// Returns a short description of the state of a room.
///
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
pub(crate) async fn get_room_summary(
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_summary::msc3266::Request>,
) -> Result<get_summary::msc3266::Response> {
let (room_id, servers) = services
.rooms
.alias
.resolve_with_servers(&body.room_id_or_alias, Some(body.via.clone()))
.await?;
if services.rooms.metadata.is_banned(&room_id).await {
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
}
room_summary_response(&services, &room_id, &servers, body.sender_user.as_deref())
.boxed()
.await
}
async fn room_summary_response(
services: &Services,
room_id: &RoomId,
servers: &[OwnedServerName],
sender_user: Option<&UserId>,
) -> Result<get_summary::msc3266::Response> {
if services.rooms.metadata.exists(room_id).await {
return local_room_summary_response(services, room_id, sender_user)
.boxed()
.await;
}
let room =
remote_room_summary_hierarchy_response(services, room_id, servers, sender_user).await?;
Ok(get_summary::msc3266::Response {
room_id: room_id.to_owned(),
canonical_alias: room.canonical_alias,
avatar_url: room.avatar_url,
guest_can_join: room.guest_can_join,
name: room.name,
num_joined_members: room.num_joined_members,
topic: room.topic,
world_readable: room.world_readable,
join_rule: room.join_rule,
room_type: room.room_type,
room_version: room.room_version,
membership: if sender_user.is_none() {
None
} else {
Some(MembershipState::Leave)
},
encryption: room.encryption,
allowed_room_ids: room.allowed_room_ids,
})
}
async fn local_room_summary_response(
services: &Services,
room_id: &RoomId,
sender_user: Option<&UserId>,
) -> Result<get_summary::msc3266::Response> {
let join_rule = services.rooms.state_accessor.get_space_join_rule(room_id);
let world_readable = services.rooms.state_accessor.is_world_readable(room_id);
let guest_can_join = services.rooms.state_accessor.guest_can_join(room_id);
let ((join_rule, allowed_room_ids), world_readable, guest_can_join) =
join3(join_rule, world_readable, guest_can_join).await;
user_can_see_summary(
services,
room_id,
&join_rule,
guest_can_join,
world_readable,
&allowed_room_ids,
sender_user,
)
.await?;
let canonical_alias = services
.rooms
.state_accessor
.get_canonical_alias(room_id)
.ok();
let name = services.rooms.state_accessor.get_name(room_id).ok();
let topic = services.rooms.state_accessor.get_room_topic(room_id).ok();
let room_type = services.rooms.state_accessor.get_room_type(room_id).ok();
let avatar_url = services
.rooms
.state_accessor
.get_avatar(room_id)
.map(|res| res.into_option().unwrap_or_default().url);
let room_version = services.rooms.state.get_room_version(room_id).ok();
let encryption = services
.rooms
.state_accessor
.get_room_encryption(room_id)
.ok();
let num_joined_members = services
.rooms
.state_cache
.room_joined_count(room_id)
.unwrap_or(0);
let (
canonical_alias,
name,
num_joined_members,
topic,
avatar_url,
room_type,
room_version,
encryption,
) = futures::join!(
canonical_alias,
name,
num_joined_members,
topic,
avatar_url,
room_type,
room_version,
encryption,
);
Ok(get_summary::msc3266::Response {
room_id: room_id.to_owned(),
canonical_alias,
avatar_url,
guest_can_join,
name,
num_joined_members: num_joined_members.try_into().unwrap_or_default(),
topic,
world_readable,
join_rule,
room_type,
room_version,
membership: if let Some(sender_user) = sender_user {
services
.rooms
.state_accessor
.get_member(room_id, sender_user)
.await
.map_or(Some(MembershipState::Leave), |content| Some(content.membership))
} else {
None
},
encryption,
allowed_room_ids,
})
}
/// used by MSC3266 to fetch a room's info if we do not know about it
async fn remote_room_summary_hierarchy_response(
services: &Services,
room_id: &RoomId,
servers: &[OwnedServerName],
sender_user: Option<&UserId>,
) -> Result<SpaceHierarchyParentSummary> {
if !services.config.allow_federation {
return Err!(Request(Forbidden("Federation is disabled.")));
}
if services.rooms.metadata.is_disabled(room_id).await {
return Err!(Request(Forbidden(
"Federaton of room {room_id} is currently disabled on this server."
)));
}
let request = get_hierarchy::v1::Request::new(room_id.to_owned());
let mut requests: FuturesUnordered<_> = servers
.iter()
.map(|server| {
services
.sending
.send_federation_request(server, request.clone())
})
.collect();
while let Some(Ok(response)) = requests.next().await {
let room = response.room.clone();
if room.room_id != room_id {
debug_warn!(
"Room ID {} returned does not belong to the requested room ID {}",
room.room_id,
room_id
);
continue;
}
return user_can_see_summary(
services,
room_id,
&room.join_rule,
room.guest_can_join,
room.world_readable,
&room.allowed_room_ids,
sender_user,
)
.await
.map(|()| room);
}
Err!(Request(NotFound(
"Room is unknown to this server and was unable to fetch over federation with the \
provided servers available"
)))
}
async fn user_can_see_summary(
services: &Services,
room_id: &RoomId,
join_rule: &SpaceRoomJoinRule,
guest_can_join: bool,
world_readable: bool,
allowed_room_ids: &[OwnedRoomId],
sender_user: Option<&UserId>,
) -> Result {
match sender_user {
| Some(sender_user) => {
let user_can_see_state_events = services
.rooms
.state_accessor
.user_can_see_state_events(sender_user, room_id);
let is_guest = services.users.is_deactivated(sender_user).unwrap_or(false);
let user_in_allowed_restricted_room = allowed_room_ids
.iter()
.stream()
.any(|room| services.rooms.state_cache.is_joined(sender_user, room));
let (user_can_see_state_events, is_guest, user_in_allowed_restricted_room) =
join3(user_can_see_state_events, is_guest, user_in_allowed_restricted_room)
.boxed()
.await;
if user_can_see_state_events
|| (is_guest && guest_can_join)
|| matches!(&join_rule, &Public | &Knock | &KnockRestricted)
|| user_in_allowed_restricted_room
{
return Ok(());
}
Err!(Request(Forbidden(
"Room is not world readable, not publicly accessible/joinable, restricted room \
conditions not met, and guest access is forbidden. Not allowed to see details \
of this room."
)))
},
| None => {
if matches!(join_rule, Public | Knock | KnockRestricted) || world_readable {
return Ok(());
}
Err!(Request(Forbidden(
"Room is not world readable or publicly accessible/joinable, authentication is \
required"
)))
},
}
}

View file

@ -103,7 +103,7 @@ pub(crate) async fn upgrade_room_route(
// Use the m.room.tombstone event as the predecessor
let predecessor = Some(ruma::events::room::create::PreviousRoom::new(
body.room_id.clone(),
(*tombstone_event_id).to_owned(),
Some(tombstone_event_id),
));
// Send a m.room.create event containing a predecessor field and the applicable

View file

@ -2,7 +2,7 @@ use std::collections::BTreeMap;
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::Err;
use conduwuit::{Err, Error, Result};
use futures::StreamExt;
use ruma::{
OwnedRoomId,
@ -14,16 +14,14 @@ use ruma::{
delete_profile_key, delete_timezone_key, get_profile_key, get_timezone_key,
set_profile_key, set_timezone_key,
},
room::get_summary,
},
federation,
},
events::room::member::MembershipState,
presence::PresenceState,
};
use super::{update_avatar_url, update_displayname};
use crate::{Error, Result, Ruma, RumaResponse};
use crate::Ruma;
/// # `GET /_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`
///
@ -38,13 +36,10 @@ pub(crate) async fn get_mutual_rooms_route(
InsecureClientIp(client): InsecureClientIp,
body: Ruma<mutual_rooms::unstable::Request>,
) -> Result<mutual_rooms::unstable::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_user = body.sender_user();
if sender_user == &body.user_id {
return Err(Error::BadRequest(
ErrorKind::Unknown,
"You cannot request rooms in common with yourself.",
));
if sender_user == body.user_id {
return Err!(Request(Unknown("You cannot request rooms in common with yourself.")));
}
if !services.users.exists(&body.user_id).await {
@ -65,129 +60,6 @@ pub(crate) async fn get_mutual_rooms_route(
})
}
/// # `GET /_matrix/client/unstable/im.nheko.summary/rooms/{roomIdOrAlias}/summary`
///
/// Returns a short description of the state of a room.
///
/// This is the "wrong" endpoint that some implementations/clients may use
/// according to the MSC. Request and response bodies are the same as
/// `get_room_summary`.
///
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
pub(crate) async fn get_room_summary_legacy(
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_summary::msc3266::Request>,
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
get_room_summary(State(services), InsecureClientIp(client), body)
.await
.map(RumaResponse)
}
/// # `GET /_matrix/client/unstable/im.nheko.summary/summary/{roomIdOrAlias}`
///
/// Returns a short description of the state of a room.
///
/// TODO: support fetching remote room info if we don't know the room
///
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
pub(crate) async fn get_room_summary(
State(services): State<crate::State>,
InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_summary::msc3266::Request>,
) -> Result<get_summary::msc3266::Response> {
let sender_user = body.sender_user.as_ref();
let room_id = services.rooms.alias.resolve(&body.room_id_or_alias).await?;
if !services.rooms.metadata.exists(&room_id).await {
return Err(Error::BadRequest(ErrorKind::NotFound, "Room is unknown to this server"));
}
if sender_user.is_none()
&& !services
.rooms
.state_accessor
.is_world_readable(&room_id)
.await
{
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Room is not world readable, authentication is required",
));
}
Ok(get_summary::msc3266::Response {
room_id: room_id.clone(),
canonical_alias: services
.rooms
.state_accessor
.get_canonical_alias(&room_id)
.await
.ok(),
avatar_url: services
.rooms
.state_accessor
.get_avatar(&room_id)
.await
.into_option()
.unwrap_or_default()
.url,
guest_can_join: services.rooms.state_accessor.guest_can_join(&room_id).await,
name: services.rooms.state_accessor.get_name(&room_id).await.ok(),
num_joined_members: services
.rooms
.state_cache
.room_joined_count(&room_id)
.await
.unwrap_or(0)
.try_into()?,
topic: services
.rooms
.state_accessor
.get_room_topic(&room_id)
.await
.ok(),
world_readable: services
.rooms
.state_accessor
.is_world_readable(&room_id)
.await,
join_rule: services
.rooms
.state_accessor
.get_join_rule(&room_id)
.await
.unwrap_or_default()
.0,
room_type: services
.rooms
.state_accessor
.get_room_type(&room_id)
.await
.ok(),
room_version: services.rooms.state.get_room_version(&room_id).await.ok(),
membership: if let Some(sender_user) = sender_user {
services
.rooms
.state_accessor
.get_member(&room_id, sender_user)
.await
.map_or_else(|_| MembershipState::Leave, |content| content.membership)
.into()
} else {
None
},
encryption: services
.rooms
.state_accessor
.get_room_encryption(&room_id)
.await
.ok(),
})
}
/// # `DELETE /_matrix/client/unstable/uk.tcpip.msc4133/profile/:user_id/us.cloke.msc4175.tz`
///
/// Deletes the `tz` (timezone) of a user, as per MSC4133 and MSC4175.

View file

@ -27,7 +27,6 @@ use ruma::{
},
events::{
StateEventType,
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
space::child::{HierarchySpaceChildEvent, SpaceChildEventContent},
},
serde::Raw,
@ -306,25 +305,18 @@ async fn get_room_summary(
children_state: Vec<Raw<HierarchySpaceChildEvent>>,
identifier: &Identifier<'_>,
) -> Result<SpaceHierarchyParentSummary, Error> {
let join_rule = self
let (join_rule, allowed_room_ids) = self
.services
.state_accessor
.room_state_get_content(room_id, &StateEventType::RoomJoinRules, "")
.await
.map_or(JoinRule::Invite, |c: RoomJoinRulesEventContent| c.join_rule);
.get_space_join_rule(room_id)
.await;
let allowed_room_ids = self
.services
.state_accessor
.allowed_room_ids(join_rule.clone());
let join_rule = join_rule.clone().into();
let is_accessible_child = self
.is_accessible_child(room_id, &join_rule, identifier, &allowed_room_ids)
.await;
if !is_accessible_child {
return Err!(Request(Forbidden("User is not allowed to see the room",)));
return Err!(Request(Forbidden("User is not allowed to see the room")));
}
let name = self.services.state_accessor.get_name(room_id).ok();
@ -355,6 +347,14 @@ async fn get_room_summary(
.get_avatar(room_id)
.map(|res| res.into_option().unwrap_or_default().url);
let room_version = self.services.state.get_room_version(room_id).ok();
let encryption = self
.services
.state_accessor
.get_room_encryption(room_id)
.ok();
let (
canonical_alias,
name,
@ -364,6 +364,8 @@ async fn get_room_summary(
guest_can_join,
avatar_url,
room_type,
room_version,
encryption,
) = futures::join!(
canonical_alias,
name,
@ -372,7 +374,9 @@ async fn get_room_summary(
world_readable,
guest_can_join,
avatar_url,
room_type
room_type,
room_version,
encryption,
);
Ok(SpaceHierarchyParentSummary {
@ -387,9 +391,9 @@ async fn get_room_summary(
allowed_room_ids,
join_rule,
room_id: room_id.to_owned(),
num_joined_members: num_joined_members
.try_into()
.expect("user count should not be that big"),
num_joined_members: num_joined_members.try_into().unwrap_or_default(),
encryption,
room_version,
})
}
@ -487,6 +491,8 @@ async fn cache_insert(
join_rule,
room_type,
allowed_room_ids,
encryption,
room_version,
} = child;
let summary = SpaceHierarchyParentSummary {
@ -506,6 +512,8 @@ async fn cache_insert(
.map(PduEvent::into_stripped_spacechild_state_event)
.collect()
.await,
encryption,
room_version,
};
cache.insert(current_room.to_owned(), Some(CachedSpaceHierarchySummary { summary }));
@ -527,7 +535,9 @@ impl From<CachedSpaceHierarchySummary> for SpaceHierarchyRoomsChunk {
join_rule,
room_type,
children_state,
..
allowed_room_ids,
encryption,
room_version,
} = value.summary;
Self {
@ -542,6 +552,9 @@ impl From<CachedSpaceHierarchySummary> for SpaceHierarchyRoomsChunk {
join_rule,
room_type,
children_state,
encryption,
room_version,
allowed_room_ids,
}
}
}
@ -562,7 +575,9 @@ pub fn summary_to_chunk(summary: SpaceHierarchyParentSummary) -> SpaceHierarchyR
join_rule,
room_type,
children_state,
..
allowed_room_ids,
encryption,
room_version,
} = summary;
SpaceHierarchyRoomsChunk {
@ -577,5 +592,8 @@ pub fn summary_to_chunk(summary: SpaceHierarchyParentSummary) -> SpaceHierarchyR
join_rule,
room_type,
children_state,
encryption,
room_version,
allowed_room_ids,
}
}

View file

@ -129,22 +129,34 @@ impl Service {
.map(|c: RoomTopicEventContent| c.topic)
}
/// Returns the join rule (`SpaceRoomJoinRule`) for a given room
pub async fn get_join_rule(
/// Returns the space join rule (`SpaceRoomJoinRule`) for a given room and
/// any allowed room IDs if available. Will default to Invite and empty vec
/// if doesnt exist or invalid,
pub async fn get_space_join_rule(
&self,
room_id: &RoomId,
) -> Result<(SpaceRoomJoinRule, Vec<OwnedRoomId>)> {
) -> (SpaceRoomJoinRule, Vec<OwnedRoomId>) {
self.room_state_get_content(room_id, &StateEventType::RoomJoinRules, "")
.await
.map(|c: RoomJoinRulesEventContent| {
(c.join_rule.clone().into(), self.allowed_room_ids(c.join_rule))
})
.or_else(|_| Ok((SpaceRoomJoinRule::Invite, vec![])))
.map_or_else(
|_| (SpaceRoomJoinRule::Invite, vec![]),
|c: RoomJoinRulesEventContent| {
(c.join_rule.clone().into(), self.allowed_room_ids(c.join_rule))
},
)
}
/// Returns the join rules for a given room (`JoinRule` type). Will default
/// to Invite if doesnt exist or invalid
pub async fn get_join_rules(&self, room_id: &RoomId) -> JoinRule {
self.room_state_get_content(room_id, &StateEventType::RoomJoinRules, "")
.await
.map_or_else(|_| JoinRule::Invite, |c: RoomJoinRulesEventContent| (c.join_rule))
}
/// Returns an empty vec if not a restricted room
pub fn allowed_room_ids(&self, join_rule: JoinRule) -> Vec<OwnedRoomId> {
let mut room_ids = Vec::with_capacity(1);
let mut room_ids = Vec::with_capacity(1); // restricted rooms generally only have 1 allowed room ID
if let JoinRule::Restricted(r) | JoinRule::KnockRestricted(r) = join_rule {
for rule in r.allow {
if let AllowRule::RoomMembership(RoomMembership { room_id: membership }) = rule {