mirror of
https://gitlab.com/famedly/conduit.git
synced 2025-06-27 16:35:59 +00:00
feat(sync): v4 -> v5
This commit is contained in:
parent
215198d1c0
commit
ec2c61e277
6 changed files with 66 additions and 128 deletions
|
@ -171,7 +171,7 @@ features = [
|
||||||
"ring-compat",
|
"ring-compat",
|
||||||
"state-res",
|
"state-res",
|
||||||
"unstable-msc2448",
|
"unstable-msc2448",
|
||||||
"unstable-msc3575",
|
"unstable-msc4186",
|
||||||
]
|
]
|
||||||
git = "https://github.com/ruma/ruma.git"
|
git = "https://github.com/ruma/ruma.git"
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ use ruma::{
|
||||||
KnockState, KnockedRoom, LeftRoom, Presence, RoomAccountData, RoomSummary, Rooms,
|
KnockState, KnockedRoom, LeftRoom, Presence, RoomAccountData, RoomSummary, Rooms,
|
||||||
State, Timeline, ToDevice,
|
State, Timeline, ToDevice,
|
||||||
},
|
},
|
||||||
v4::{SlidingOp, SlidingSyncRoomHero},
|
|
||||||
DeviceLists, UnreadNotificationsCount,
|
DeviceLists, UnreadNotificationsCount,
|
||||||
},
|
},
|
||||||
uiaa::UiaaResponse,
|
uiaa::UiaaResponse,
|
||||||
|
@ -1270,9 +1269,9 @@ fn share_encrypted_room(
|
||||||
.any(|encrypted| encrypted))
|
.any(|encrypted| encrypted))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn sync_events_v4_route(
|
pub async fn sync_events_v5_route(
|
||||||
body: Ruma<sync_events::v4::Request>,
|
body: Ruma<sync_events::v5::Request>,
|
||||||
) -> Result<sync_events::v4::Response, RumaResponse<UiaaResponse>> {
|
) -> Result<sync_events::v5::Response, RumaResponse<UiaaResponse>> {
|
||||||
let sender_user = body.sender_user.expect("user is authenticated");
|
let sender_user = body.sender_user.expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.expect("user is authenticated");
|
let sender_device = body.sender_device.expect("user is authenticated");
|
||||||
let mut body = body.body;
|
let mut body = body.body;
|
||||||
|
@ -1331,10 +1330,9 @@ pub async fn sync_events_v4_route(
|
||||||
);
|
);
|
||||||
|
|
||||||
for room_id in &all_joined_rooms {
|
for room_id in &all_joined_rooms {
|
||||||
let current_shortstatehash =
|
let Some(current_shortstatehash) =
|
||||||
if let Some(s) = services().rooms.state.get_room_shortstatehash(room_id)? {
|
services().rooms.state.get_room_shortstatehash(room_id)?
|
||||||
s
|
else {
|
||||||
} else {
|
|
||||||
error!("Room {} has no state", room_id);
|
error!("Room {} has no state", room_id);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -1510,32 +1508,18 @@ pub async fn sync_events_v4_route(
|
||||||
|
|
||||||
let mut new_known_rooms = BTreeSet::new();
|
let mut new_known_rooms = BTreeSet::new();
|
||||||
|
|
||||||
lists.insert(
|
for (mut start, mut end) in list.ranges {
|
||||||
list_id.clone(),
|
start = start.clamp(uint!(0), UInt::from(all_joined_rooms.len() as u32 - 1));
|
||||||
sync_events::v4::SyncList {
|
end = end.clamp(start, UInt::from(all_joined_rooms.len() as u32 - 1));
|
||||||
ops: list
|
let room_ids =
|
||||||
.ranges
|
all_joined_rooms[(u64::from(start) as usize)..=(u64::from(end) as usize)].to_vec();
|
||||||
.into_iter()
|
|
||||||
.map(|mut r| {
|
|
||||||
r.0 =
|
|
||||||
r.0.clamp(uint!(0), UInt::from(all_joined_rooms.len() as u32 - 1));
|
|
||||||
r.1 =
|
|
||||||
r.1.clamp(r.0, UInt::from(all_joined_rooms.len() as u32 - 1));
|
|
||||||
let room_ids = all_joined_rooms
|
|
||||||
[(u64::from(r.0) as usize)..=(u64::from(r.1) as usize)]
|
|
||||||
.to_vec();
|
|
||||||
new_known_rooms.extend(room_ids.iter().cloned());
|
new_known_rooms.extend(room_ids.iter().cloned());
|
||||||
for room_id in &room_ids {
|
for room_id in &room_ids {
|
||||||
let todo_room = todo_rooms.entry(room_id.clone()).or_insert((
|
let todo_room =
|
||||||
BTreeSet::new(),
|
todo_rooms
|
||||||
0,
|
.entry(room_id.clone())
|
||||||
u64::MAX,
|
.or_insert((BTreeSet::new(), 0, u64::MAX));
|
||||||
));
|
let limit = u64::from(list.room_details.timeline_limit).min(100);
|
||||||
let limit = list
|
|
||||||
.room_details
|
|
||||||
.timeline_limit
|
|
||||||
.map_or(10, u64::from)
|
|
||||||
.min(100);
|
|
||||||
todo_room
|
todo_room
|
||||||
.0
|
.0
|
||||||
.extend(list.room_details.required_state.iter().cloned());
|
.extend(list.room_details.required_state.iter().cloned());
|
||||||
|
@ -1549,15 +1533,11 @@ pub async fn sync_events_v4_route(
|
||||||
.unwrap_or(0),
|
.unwrap_or(0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
sync_events::v4::SyncOp {
|
|
||||||
op: SlidingOp::Sync,
|
|
||||||
range: Some(r),
|
|
||||||
index: None,
|
|
||||||
room_ids,
|
|
||||||
room_id: None,
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.collect(),
|
lists.insert(
|
||||||
|
list_id.clone(),
|
||||||
|
sync_events::v5::response::List {
|
||||||
count: UInt::from(all_joined_rooms.len() as u32),
|
count: UInt::from(all_joined_rooms.len() as u32),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1582,7 +1562,7 @@ pub async fn sync_events_v4_route(
|
||||||
let todo_room = todo_rooms
|
let todo_room = todo_rooms
|
||||||
.entry(room_id.clone())
|
.entry(room_id.clone())
|
||||||
.or_insert((BTreeSet::new(), 0, u64::MAX));
|
.or_insert((BTreeSet::new(), 0, u64::MAX));
|
||||||
let limit = room.timeline_limit.map_or(10, u64::from).min(100);
|
let limit = u64::from(room.timeline_limit).min(100);
|
||||||
todo_room.0.extend(room.required_state.iter().cloned());
|
todo_room.0.extend(room.required_state.iter().cloned());
|
||||||
todo_room.1 = todo_room.1.max(limit);
|
todo_room.1 = todo_room.1.max(limit);
|
||||||
// 0 means unknown because it got out of date
|
// 0 means unknown because it got out of date
|
||||||
|
@ -1596,11 +1576,6 @@ pub async fn sync_events_v4_route(
|
||||||
known_subscription_rooms.insert(room_id.clone());
|
known_subscription_rooms.insert(room_id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
for r in body.unsubscribe_rooms {
|
|
||||||
known_subscription_rooms.remove(&r);
|
|
||||||
body.room_subscriptions.remove(&r);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(conn_id) = &body.conn_id {
|
if let Some(conn_id) = &body.conn_id {
|
||||||
services().users.update_sync_known_rooms(
|
services().users.update_sync_known_rooms(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
|
@ -1656,6 +1631,11 @@ pub async fn sync_events_v4_route(
|
||||||
.map(|(_, pdu)| pdu.to_sync_room_event())
|
.map(|(_, pdu)| pdu.to_sync_room_event())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let bump_stamp = timeline_pdus
|
||||||
|
.iter()
|
||||||
|
.map(|(_, pdu)| pdu.origin_server_ts)
|
||||||
|
.max();
|
||||||
|
|
||||||
let required_state = required_state_request
|
let required_state = required_state_request
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|state| {
|
.flat_map(|state| {
|
||||||
|
@ -1683,7 +1663,7 @@ pub async fn sync_events_v4_route(
|
||||||
.get_member(room_id, &member)
|
.get_member(room_id, &member)
|
||||||
.ok()
|
.ok()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|memberevent| SlidingSyncRoomHero {
|
.map(|memberevent| sync_events::v5::response::Hero {
|
||||||
user_id: member,
|
user_id: member,
|
||||||
name: memberevent.displayname,
|
name: memberevent.displayname,
|
||||||
avatar: memberevent.avatar_url,
|
avatar: memberevent.avatar_url,
|
||||||
|
@ -1720,7 +1700,7 @@ pub async fn sync_events_v4_route(
|
||||||
|
|
||||||
rooms.insert(
|
rooms.insert(
|
||||||
room_id.clone(),
|
room_id.clone(),
|
||||||
sync_events::v4::SlidingSyncRoom {
|
sync_events::v5::response::Room {
|
||||||
name: services().rooms.state_accessor.get_name(room_id)?.or(name),
|
name: services().rooms.state_accessor.get_name(room_id)?.or(name),
|
||||||
avatar: if let Some(avatar) = avatar {
|
avatar: if let Some(avatar) = avatar {
|
||||||
JsOption::Some(avatar)
|
JsOption::Some(avatar)
|
||||||
|
@ -1773,7 +1753,7 @@ pub async fn sync_events_v4_route(
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
num_live: None, // Count events in timeline greater than global sync counter
|
num_live: None, // Count events in timeline greater than global sync counter
|
||||||
timestamp: None,
|
bump_stamp,
|
||||||
heroes: if body
|
heroes: if body
|
||||||
.room_subscriptions
|
.room_subscriptions
|
||||||
.get(room_id)
|
.get(room_id)
|
||||||
|
@ -1801,15 +1781,14 @@ pub async fn sync_events_v4_route(
|
||||||
let _ = tokio::time::timeout(duration, watcher).await;
|
let _ = tokio::time::timeout(duration, watcher).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(sync_events::v4::Response {
|
Ok(sync_events::v5::Response {
|
||||||
initial: globalsince == 0,
|
|
||||||
txn_id: body.txn_id.clone(),
|
txn_id: body.txn_id.clone(),
|
||||||
pos: next_batch.to_string(),
|
pos: next_batch.to_string(),
|
||||||
lists,
|
lists,
|
||||||
rooms,
|
rooms,
|
||||||
extensions: sync_events::v4::Extensions {
|
extensions: sync_events::v5::response::Extensions {
|
||||||
to_device: if body.extensions.to_device.enabled.unwrap_or(false) {
|
to_device: if body.extensions.to_device.enabled.unwrap_or(false) {
|
||||||
Some(sync_events::v4::ToDevice {
|
Some(sync_events::v5::response::ToDevice {
|
||||||
events: services()
|
events: services()
|
||||||
.users
|
.users
|
||||||
.get_to_device_events(&sender_user, &sender_device)?,
|
.get_to_device_events(&sender_user, &sender_device)?,
|
||||||
|
@ -1818,7 +1797,7 @@ pub async fn sync_events_v4_route(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
e2ee: sync_events::v4::E2EE {
|
e2ee: sync_events::v5::response::E2EE {
|
||||||
device_lists: DeviceLists {
|
device_lists: DeviceLists {
|
||||||
changed: device_list_changes.into_iter().collect(),
|
changed: device_list_changes.into_iter().collect(),
|
||||||
left: device_list_left.into_iter().collect(),
|
left: device_list_left.into_iter().collect(),
|
||||||
|
@ -1829,7 +1808,7 @@ pub async fn sync_events_v4_route(
|
||||||
// Fallback keys are not yet supported
|
// Fallback keys are not yet supported
|
||||||
device_unused_fallback_key_types: None,
|
device_unused_fallback_key_types: None,
|
||||||
},
|
},
|
||||||
account_data: sync_events::v4::AccountData {
|
account_data: sync_events::v5::response::AccountData {
|
||||||
global: if body.extensions.account_data.enabled.unwrap_or(false) {
|
global: if body.extensions.account_data.enabled.unwrap_or(false) {
|
||||||
services()
|
services()
|
||||||
.account_data
|
.account_data
|
||||||
|
@ -1848,13 +1827,12 @@ pub async fn sync_events_v4_route(
|
||||||
},
|
},
|
||||||
rooms: BTreeMap::new(),
|
rooms: BTreeMap::new(),
|
||||||
},
|
},
|
||||||
receipts: sync_events::v4::Receipts {
|
receipts: sync_events::v5::response::Receipts {
|
||||||
rooms: BTreeMap::new(),
|
rooms: BTreeMap::new(),
|
||||||
},
|
},
|
||||||
typing: sync_events::v4::Typing {
|
typing: sync_events::v5::response::Typing {
|
||||||
rooms: BTreeMap::new(),
|
rooms: BTreeMap::new(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delta_token: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ pub async fn get_supported_versions_route(
|
||||||
unstable_features: BTreeMap::from_iter([
|
unstable_features: BTreeMap::from_iter([
|
||||||
("org.matrix.e2e_cross_signing".to_owned(), true),
|
("org.matrix.e2e_cross_signing".to_owned(), true),
|
||||||
("org.matrix.msc3916.stable".to_owned(), true),
|
("org.matrix.msc3916.stable".to_owned(), true),
|
||||||
|
("org.matrix.simplified_msc3575".to_owned(), true),
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use ruma::api::client::discovery::discover_homeserver::{
|
use ruma::api::client::discovery::discover_homeserver::{self, HomeserverInfo};
|
||||||
self, HomeserverInfo, SlidingSyncProxyInfo,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{services, Result, Ruma};
|
use crate::{services, Result, Ruma};
|
||||||
|
|
||||||
|
@ -17,6 +15,5 @@ pub async fn well_known_client(
|
||||||
base_url: client_url.clone(),
|
base_url: client_url.clone(),
|
||||||
},
|
},
|
||||||
identity_server: None,
|
identity_server: None,
|
||||||
sliding_sync_proxy: Some(SlidingSyncProxyInfo { url: client_url }),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -410,7 +410,7 @@ fn routes(config: &Config) -> Router {
|
||||||
.put(client_server::send_state_event_for_empty_key_route),
|
.put(client_server::send_state_event_for_empty_key_route),
|
||||||
)
|
)
|
||||||
.ruma_route(client_server::sync_events_route)
|
.ruma_route(client_server::sync_events_route)
|
||||||
.ruma_route(client_server::sync_events_v4_route)
|
.ruma_route(client_server::sync_events_v5_route)
|
||||||
.ruma_route(client_server::get_context_route)
|
.ruma_route(client_server::get_context_route)
|
||||||
.ruma_route(client_server::get_message_events_route)
|
.ruma_route(client_server::get_message_events_route)
|
||||||
.ruma_route(client_server::search_events_route)
|
.ruma_route(client_server::search_events_route)
|
||||||
|
|
|
@ -10,10 +10,7 @@ use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
device::Device,
|
device::Device,
|
||||||
filter::FilterDefinition,
|
filter::FilterDefinition,
|
||||||
sync::sync_events::{
|
sync::sync_events::{self},
|
||||||
self,
|
|
||||||
v4::{ExtensionsConfig, SyncRequestList},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
encryption::{CrossSigningKey, DeviceKeys, OneTimeKey},
|
encryption::{CrossSigningKey, DeviceKeys, OneTimeKey},
|
||||||
events::AnyToDeviceEvent,
|
events::AnyToDeviceEvent,
|
||||||
|
@ -25,10 +22,10 @@ use ruma::{
|
||||||
use crate::{services, Error, Result};
|
use crate::{services, Error, Result};
|
||||||
|
|
||||||
pub struct SlidingSyncCache {
|
pub struct SlidingSyncCache {
|
||||||
lists: BTreeMap<String, SyncRequestList>,
|
lists: BTreeMap<String, sync_events::v5::request::List>,
|
||||||
subscriptions: BTreeMap<OwnedRoomId, sync_events::v4::RoomSubscription>,
|
subscriptions: BTreeMap<OwnedRoomId, sync_events::v5::request::RoomSubscription>,
|
||||||
known_rooms: BTreeMap<String, BTreeMap<OwnedRoomId, u64>>, // For every room, the roomsince number
|
known_rooms: BTreeMap<String, BTreeMap<OwnedRoomId, u64>>, // For every room, the roomsince number
|
||||||
extensions: ExtensionsConfig,
|
extensions: sync_events::v5::request::Extensions,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
|
@ -60,7 +57,7 @@ impl Service {
|
||||||
&self,
|
&self,
|
||||||
user_id: OwnedUserId,
|
user_id: OwnedUserId,
|
||||||
device_id: OwnedDeviceId,
|
device_id: OwnedDeviceId,
|
||||||
request: &mut sync_events::v4::Request,
|
request: &mut sync_events::v5::Request,
|
||||||
) -> BTreeMap<String, BTreeMap<OwnedRoomId, u64>> {
|
) -> BTreeMap<String, BTreeMap<OwnedRoomId, u64>> {
|
||||||
let Some(conn_id) = request.conn_id.clone() else {
|
let Some(conn_id) = request.conn_id.clone() else {
|
||||||
return BTreeMap::new();
|
return BTreeMap::new();
|
||||||
|
@ -75,7 +72,7 @@ impl Service {
|
||||||
lists: BTreeMap::new(),
|
lists: BTreeMap::new(),
|
||||||
subscriptions: BTreeMap::new(),
|
subscriptions: BTreeMap::new(),
|
||||||
known_rooms: BTreeMap::new(),
|
known_rooms: BTreeMap::new(),
|
||||||
extensions: ExtensionsConfig::default(),
|
extensions: sync_events::v5::request::Extensions::default(),
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -84,57 +81,22 @@ impl Service {
|
||||||
|
|
||||||
for (list_id, list) in &mut request.lists {
|
for (list_id, list) in &mut request.lists {
|
||||||
if let Some(cached_list) = cached.lists.get(list_id) {
|
if let Some(cached_list) = cached.lists.get(list_id) {
|
||||||
if list.sort.is_empty() {
|
|
||||||
list.sort.clone_from(&cached_list.sort);
|
|
||||||
};
|
|
||||||
if list.room_details.required_state.is_empty() {
|
if list.room_details.required_state.is_empty() {
|
||||||
list.room_details
|
list.room_details
|
||||||
.required_state
|
.required_state
|
||||||
.clone_from(&cached_list.room_details.required_state);
|
.clone_from(&cached_list.room_details.required_state);
|
||||||
};
|
};
|
||||||
list.room_details.timeline_limit = list
|
|
||||||
.room_details
|
|
||||||
.timeline_limit
|
|
||||||
.or(cached_list.room_details.timeline_limit);
|
|
||||||
list.include_old_rooms = list
|
|
||||||
.include_old_rooms
|
|
||||||
.clone()
|
|
||||||
.or(cached_list.include_old_rooms.clone());
|
|
||||||
match (&mut list.filters, cached_list.filters.clone()) {
|
match (&mut list.filters, cached_list.filters.clone()) {
|
||||||
(Some(list_filters), Some(cached_filters)) => {
|
(Some(list_filters), Some(cached_filters)) => {
|
||||||
list_filters.is_dm = list_filters.is_dm.or(cached_filters.is_dm);
|
|
||||||
if list_filters.spaces.is_empty() {
|
|
||||||
list_filters.spaces = cached_filters.spaces;
|
|
||||||
}
|
|
||||||
list_filters.is_encrypted =
|
|
||||||
list_filters.is_encrypted.or(cached_filters.is_encrypted);
|
|
||||||
list_filters.is_invite =
|
list_filters.is_invite =
|
||||||
list_filters.is_invite.or(cached_filters.is_invite);
|
list_filters.is_invite.or(cached_filters.is_invite);
|
||||||
if list_filters.room_types.is_empty() {
|
|
||||||
list_filters.room_types = cached_filters.room_types;
|
|
||||||
}
|
|
||||||
if list_filters.not_room_types.is_empty() {
|
if list_filters.not_room_types.is_empty() {
|
||||||
list_filters.not_room_types = cached_filters.not_room_types;
|
list_filters.not_room_types = cached_filters.not_room_types;
|
||||||
}
|
}
|
||||||
list_filters.room_name_like = list_filters
|
|
||||||
.room_name_like
|
|
||||||
.clone()
|
|
||||||
.or(cached_filters.room_name_like);
|
|
||||||
if list_filters.tags.is_empty() {
|
|
||||||
list_filters.tags = cached_filters.tags;
|
|
||||||
}
|
|
||||||
if list_filters.not_tags.is_empty() {
|
|
||||||
list_filters.not_tags = cached_filters.not_tags;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(_, Some(cached_filters)) => list.filters = Some(cached_filters),
|
(_, Some(cached_filters)) => list.filters = Some(cached_filters),
|
||||||
(Some(list_filters), _) => list.filters = Some(list_filters.clone()),
|
|
||||||
(_, _) => {}
|
(_, _) => {}
|
||||||
}
|
}
|
||||||
if list.bump_event_types.is_empty() {
|
|
||||||
list.bump_event_types
|
|
||||||
.clone_from(&cached_list.bump_event_types);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
cached.lists.insert(list_id.clone(), list.clone());
|
cached.lists.insert(list_id.clone(), list.clone());
|
||||||
}
|
}
|
||||||
|
@ -192,7 +154,7 @@ impl Service {
|
||||||
user_id: OwnedUserId,
|
user_id: OwnedUserId,
|
||||||
device_id: OwnedDeviceId,
|
device_id: OwnedDeviceId,
|
||||||
conn_id: String,
|
conn_id: String,
|
||||||
subscriptions: BTreeMap<OwnedRoomId, sync_events::v4::RoomSubscription>,
|
subscriptions: BTreeMap<OwnedRoomId, sync_events::v5::request::RoomSubscription>,
|
||||||
) {
|
) {
|
||||||
let mut cache = self.connections.lock().unwrap();
|
let mut cache = self.connections.lock().unwrap();
|
||||||
let cached = Arc::clone(
|
let cached = Arc::clone(
|
||||||
|
@ -203,7 +165,7 @@ impl Service {
|
||||||
lists: BTreeMap::new(),
|
lists: BTreeMap::new(),
|
||||||
subscriptions: BTreeMap::new(),
|
subscriptions: BTreeMap::new(),
|
||||||
known_rooms: BTreeMap::new(),
|
known_rooms: BTreeMap::new(),
|
||||||
extensions: ExtensionsConfig::default(),
|
extensions: sync_events::v5::request::Extensions::default(),
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -231,7 +193,7 @@ impl Service {
|
||||||
lists: BTreeMap::new(),
|
lists: BTreeMap::new(),
|
||||||
subscriptions: BTreeMap::new(),
|
subscriptions: BTreeMap::new(),
|
||||||
known_rooms: BTreeMap::new(),
|
known_rooms: BTreeMap::new(),
|
||||||
extensions: ExtensionsConfig::default(),
|
extensions: sync_events::v5::request::Extensions::default(),
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue