syncv3: add support for filtering invites and room types

This commit is contained in:
morguldir 2024-08-24 06:44:32 +02:00
parent 001523ec81
commit 351062eb82
No known key found for this signature in database
GPG key ID: 5A6025D4F6E7A8A3

View file

@ -7,9 +7,7 @@ use std::{
use axum::extract::State;
use conduit::{
error,
utils::math::{ruma_from_u64, ruma_from_usize, usize_from_ruma, usize_from_u64_truncated},
Err, PduCount,
error, utils::math::{ruma_from_u64, ruma_from_usize, usize_from_ruma, usize_from_u64_truncated}, warn, Err, PduCount
};
use ruma::{
api::client::{
@ -29,9 +27,7 @@ use ruma::{
presence::PresenceEvent,
room::member::{MembershipState, RoomMemberEventContent},
StateEventType, TimelineEventType,
},
serde::Raw,
uint, DeviceId, EventId, OwnedUserId, RoomId, UInt, UserId,
}, room::RoomType, serde::Raw, uint, DeviceId, EventId, OwnedRoomId, OwnedUserId, RoomId, UInt, UserId
};
use tracing::{Instrument as _, Span};
@ -323,7 +319,7 @@ pub(crate) async fn sync_events_route(
#[tracing::instrument(skip_all, fields(user_id = %sender_user, room_id = %room_id), name = "left_room")]
async fn handle_left_room(
services: &Services, since: u64, room_id: &RoomId, sender_user: &UserId,
left_rooms: &mut BTreeMap<ruma::OwnedRoomId, LeftRoom>, next_batch_string: &str, full_state: bool,
left_rooms: &mut BTreeMap<OwnedRoomId, LeftRoom>, next_batch_string: &str, full_state: bool,
lazy_load_enabled: bool,
) -> Result<()> {
// Get and drop the lock to wait for remaining operations to finish
@ -1114,6 +1110,20 @@ pub(crate) async fn sync_events_v4_route(
.filter_map(Result::ok)
.collect::<Vec<_>>();
let all_invited_rooms = services
.rooms
.state_cache
.rooms_invited(&sender_user)
.filter_map(Result::ok)
.map(|r| r.0)
.collect::<Vec<_>>();
let all_rooms = all_joined_rooms
.iter()
.cloned()
.chain(all_invited_rooms.iter().cloned())
.collect();
if body.extensions.to_device.enabled.unwrap_or(false) {
services
.users
@ -1288,9 +1298,23 @@ pub(crate) async fn sync_events_v4_route(
let mut todo_rooms = BTreeMap::new(); // and required state
for (list_id, list) in body.lists {
if list.filters.and_then(|f| f.is_invite).unwrap_or(false) {
continue;
}
let active_rooms = match list.filters.clone().and_then(|f| f.is_invite) {
Some(true) => &all_invited_rooms,
Some(false) => &all_joined_rooms,
None => &all_rooms,
};
let active_rooms = match list.filters.clone().map(|f| f.not_room_types) {
Some(filter) if filter.is_empty() => active_rooms.clone(),
Some(value) => filter_rooms(active_rooms, State(services), &value, true),
None => active_rooms.clone(),
};
let active_rooms = match list.filters.clone().map(|f| f.room_types) {
Some(filter) if filter.is_empty() => active_rooms.clone(),
Some(value) => filter_rooms(&active_rooms, State(services), &value, false),
None => active_rooms,
};
let mut new_known_rooms = BTreeSet::new();
@ -1303,14 +1327,12 @@ pub(crate) async fn sync_events_v4_route(
.map(|mut r| {
r.0 = r.0.clamp(
uint!(0),
UInt::try_from(all_joined_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
UInt::try_from(active_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
);
r.1 = r.1.clamp(
r.0,
UInt::try_from(all_joined_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX),
);
let room_ids = if !all_joined_rooms.is_empty() {
all_joined_rooms[usize_from_ruma(r.0)..=usize_from_ruma(r.1)].to_vec()
r.1 =
r.1.clamp(r.0, UInt::try_from(active_rooms.len().saturating_sub(1)).unwrap_or(UInt::MAX));
let room_ids = if !active_rooms.is_empty() {
active_rooms[usize_from_ruma(r.0)..=usize_from_ruma(r.1)].to_vec()
} else {
Vec::new()
};
@ -1638,3 +1660,35 @@ pub(crate) async fn sync_events_v4_route(
delta_token: None,
})
}
fn filter_rooms(
rooms: &[OwnedRoomId], State(services): State<crate::State>, filter: &[Option<RoomType>], negate: bool,
) -> Vec<OwnedRoomId> {
return rooms
.iter()
.filter(|r| {
match services.rooms.state_accessor.get_room_type(r) {
Err(e) => {
warn!("Requested room type for {}, but could not retrieve with error {}", r, e);
false
},
Ok(None) => {
// For rooms which do not have a room type, use 'null' to include them
if negate {
!filter.contains(&None)
} else {
filter.contains(&None)
}
},
Ok(Some(room_type)) => {
if negate {
!filter.contains(&Some(room_type))
} else {
filter.is_empty() || filter.contains(&Some(room_type))
}
},
}
})
.cloned()
.collect();
}