Newer
Older
use super::State;
use crate::{ConduitResult, Database, Error, Result, Ruma};
use ruma::{
api::client::{
error::ErrorKind,
r0::{
directory::{
self, get_public_rooms, get_public_rooms_filtered, get_room_visibility,
set_room_visibility,
},
room,
},
},
events::{
room::{avatar, canonical_alias, guest_access, history_visibility, name, topic},
EventType,
},
Raw,
};
#[cfg(feature = "conduit_bin")]
use rocket::{get, post, put};
#[cfg_attr(
feature = "conduit_bin",
post("/_matrix/client/r0/publicRooms", data = "<body>")
pub async fn get_public_rooms_filtered_route(
) -> ConduitResult<get_public_rooms_filtered::Response> {
let limit = body.limit.map_or(10, u64::from);
let mut since = 0_u64;
if let Some(s) = &body.since {
let mut characters = s.chars();
let backwards = match characters.next() {
Some('n') => false,
Some('p') => true,
_ => {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Invalid `since` token",
))
}
};
since = characters
.collect::<String>()
.parse()
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `since` token."))?;
if backwards {
since = since.saturating_sub(limit);
}
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
db.rooms
.public_rooms()
.map(|room_id| {
let room_id = room_id?;
// TODO: Do not load full state?
let state = db.rooms.room_state_full(&room_id)?;
let chunk = directory::PublicRoomsChunk {
aliases: Vec::new(),
canonical_alias: state
.get(&(EventType::RoomCanonicalAlias, "".to_owned()))
.map_or(Ok::<_, Error>(None), |s| {
Ok(serde_json::from_value::<
Raw<canonical_alias::CanonicalAliasEventContent>,
>(s.content.clone())
.expect("from_value::<Raw<..>> can never fail")
.deserialize()
.map_err(|_| {
Error::bad_database("Invalid canonical alias event in database.")
})?
.alias)
})?,
name: state.get(&(EventType::RoomName, "".to_owned())).map_or(
Ok::<_, Error>(None),
|s| {
Ok(serde_json::from_value::<Raw<name::NameEventContent>>(
s.content.clone(),
)
.expect("from_value::<Raw<..>> can never fail")
.deserialize()
.map_err(|_| {
Error::bad_database("Invalid room name event in database.")
})?
.name()
.map(|n| n.to_owned()))
},
)?,
num_joined_members: (db.rooms.room_members(&room_id).count() as u32).into(),
room_id,
topic: state.get(&(EventType::RoomTopic, "".to_owned())).map_or(
Ok::<_, Error>(None),
|s| {
Ok(Some(
serde_json::from_value::<Raw<topic::TopicEventContent>>(
s.content.clone(),
)
.expect("from_value::<Raw<..>> can never fail")
.deserialize()
.map_err(|_| {
Error::bad_database("Invalid room topic event in database.")
})?
.topic,
))
},
)?,
world_readable: state
.get(&(EventType::RoomHistoryVisibility, "".to_owned()))
.map_or(Ok::<_, Error>(false), |s| {
Ok(serde_json::from_value::<
Raw<history_visibility::HistoryVisibilityEventContent>,
>(s.content.clone())
.expect("from_value::<Raw<..>> can never fail")
.deserialize()
.map_err(|_| {
Error::bad_database(
"Invalid room history visibility event in database.",
)
})?
.history_visibility
== history_visibility::HistoryVisibility::WorldReadable)
})?,
guest_can_join: state
.get(&(EventType::RoomGuestAccess, "".to_owned()))
.map_or(Ok::<_, Error>(false), |s| {
Ok(
serde_json::from_value::<Raw<guest_access::GuestAccessEventContent>>(
s.content.clone(),
)
.expect("from_value::<Raw<..>> can never fail")
.deserialize()
.map_err(|_| {
Error::bad_database("Invalid room guest access event in database.")
})?
.guest_access
== guest_access::GuestAccess::CanJoin,
)
})?,
avatar_url: state
.get(&(EventType::RoomAvatar, "".to_owned()))
.map(|s| {
Ok::<_, Error>(
serde_json::from_value::<Raw<avatar::AvatarEventContent>>(
s.content.clone(),
)
.expect("from_value::<Raw<..>> can never fail")
.deserialize()
.map_err(|_| {
Error::bad_database("Invalid room avatar event in database.")
})?
.url,
)
})
.transpose()?,
};
Ok(chunk)
})
.filter_map(|r: Result<_>| r.ok()) // Filter out buggy rooms
// We need to collect all, so we can sort by member count
.collect::<Vec<_>>();
all_rooms.sort_by(|l, r| r.num_joined_members.cmp(&l.num_joined_members));
&server_server::send_request(
&db,
"privacytools.io".to_owned(),
ruma::api::federation::v1::get_public_rooms::Request {
limit: Some(20_u32.into()),
since: None,
room_network: ruma::api::federation::v1::get_public_rooms::RoomNetwork::Matrix,
},
)
.await
?
.chunk
.into_iter()
.map(|c| serde_json::from_str(&serde_json::to_string(&c)?)?)
.collect::<Vec<_>>(),
);
*/
let total_room_count_estimate = (all_rooms.len() as u32).into();
let chunk = all_rooms
.into_iter()
.skip(since as usize)
.take(limit as usize)
.collect::<Vec<_>>();
let prev_batch = if since == 0 {
None
} else {
Some(format!("p{}", since))
};
let next_batch = if chunk.len() < limit as usize {
None
} else {
Some(format!("n{}", since + limit))
};
Ok(get_public_rooms_filtered::Response {
chunk,
total_room_count_estimate: Some(total_room_count_estimate),
}
.into())
}
#[cfg_attr(
feature = "conduit_bin",
get("/_matrix/client/r0/publicRooms", data = "<body>")
)]
pub async fn get_public_rooms_route(
db: State<'_, Database>,
) -> ConduitResult<get_public_rooms::Response> {
let Ruma {
body:
limit,
server,
since,
},
sender_id,
device_id,
json_body,
} = body;
let get_public_rooms_filtered::Response {
chunk,
prev_batch,
next_batch,
total_room_count_estimate,
} = get_public_rooms_filtered_route(
db,
Ruma {
filter: None,
limit,
room_network: get_public_rooms_filtered::RoomNetwork::Matrix,
server,
since,
},
sender_id,
device_id,
json_body,
},
)
.await?
.0;
Ok(get_public_rooms::Response {
chunk,
prev_batch,
next_batch,
total_room_count_estimate,
}
.into())
}
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
#[cfg_attr(
feature = "conduit_bin",
put("/_matrix/client/r0/directory/list/room/<_>", data = "<body>")
)]
pub async fn set_room_visibility_route(
db: State<'_, Database>,
body: Ruma<set_room_visibility::Request>,
) -> ConduitResult<set_room_visibility::Response> {
match body.visibility {
room::Visibility::Public => db.rooms.set_public(&body.room_id, true)?,
room::Visibility::Private => db.rooms.set_public(&body.room_id, false)?,
}
Ok(set_room_visibility::Response.into())
}
#[cfg_attr(
feature = "conduit_bin",
get("/_matrix/client/r0/directory/list/room/<_>", data = "<body>")
)]
pub async fn get_room_visibility_route(
db: State<'_, Database>,
body: Ruma<get_room_visibility::Request>,
) -> ConduitResult<get_room_visibility::Response> {
Ok(get_room_visibility::Response {
visibility: if db.rooms.is_public_room(&body.room_id)? {
room::Visibility::Public
} else {
room::Visibility::Private
},
}
.into())
}