tuwunel_service/rooms/state_cache/
via.rs1use std::cmp::Reverse;
2
3use futures::{Stream, StreamExt, stream::iter};
4use ruma::{
5 OwnedServerName, RoomId, ServerName,
6 events::{StateEventType, room::power_levels::RoomPowerLevelsEventContent},
7 int,
8};
9use tuwunel_core::{
10 Result, implement,
11 itertools::Itertools,
12 utils::{StreamTools, stream::TryIgnore},
13 warn,
14};
15use tuwunel_database::{Ignore, Txn};
16
17#[implement(super::Service)]
18#[tracing::instrument(level = "debug", skip(self, txn, servers))]
19pub(crate) async fn add_servers_invite_via(
20 &self,
21 txn: &mut Txn,
22 room_id: &RoomId,
23 servers: Vec<OwnedServerName>,
24) {
25 let mut servers: Vec<_> = self
26 .servers_invite_via(room_id)
27 .map(ToOwned::to_owned)
28 .chain(iter(servers.into_iter()))
29 .collect()
30 .await;
31
32 servers.sort_unstable();
33 servers.dedup();
34
35 let servers = servers
36 .iter()
37 .map(|server| server.as_bytes())
38 .collect_vec()
39 .join(&[0xFF][..]);
40
41 txn.insert_raw(&self.db.roomid_inviteviaservers, room_id.as_bytes(), &servers);
42}
43
44#[implement(super::Service)]
49#[tracing::instrument(skip(self), level = "trace")]
50pub async fn servers_route_via(&self, room_id: &RoomId) -> Result<Vec<OwnedServerName>> {
51 let most_powerful = self.most_powerful_user_server(room_id).await;
52
53 Ok(most_powerful
54 .into_iter()
55 .chain(self.popular_servers(room_id).await)
56 .take(5)
57 .collect())
58}
59
60#[implement(super::Service)]
63#[tracing::instrument(skip(self), level = "trace")]
64pub async fn most_powerful_user_server(&self, room_id: &RoomId) -> Option<OwnedServerName> {
65 self.services
66 .state_accessor
67 .room_state_get_content(room_id, &StateEventType::RoomPowerLevels, "")
68 .await
69 .ok()
70 .and_then(|content: RoomPowerLevelsEventContent| {
71 content
72 .users
73 .into_iter()
74 .max_by_key(|(_, power)| *power)
75 .filter(|(_, power)| *power >= int!(50))
76 .map(|(user, _)| user.server_name().to_owned())
77 })
78}
79
80#[implement(super::Service)]
84#[tracing::instrument(skip(self), level = "trace")]
85pub async fn popular_servers(&self, room_id: &RoomId) -> Vec<OwnedServerName> {
86 self.room_members(room_id)
87 .counts_by(|user| user.server_name().to_owned())
88 .await
89 .into_iter()
90 .sorted_by_key(|(_, users)| Reverse(*users))
91 .map(|(server, _)| server)
92 .collect()
93}
94
95#[implement(super::Service)]
96#[tracing::instrument(skip(self), level = "debug")]
97pub fn servers_invite_via<'a>(
98 &'a self,
99 room_id: &'a RoomId,
100) -> impl Stream<Item = &ServerName> + Send + 'a {
101 type KeyVal<'a> = (Ignore, Vec<&'a ServerName>);
102
103 self.db
104 .roomid_inviteviaservers
105 .stream_raw_prefix(room_id)
106 .ignore_err()
107 .map(|(_, servers): KeyVal<'_>| *servers.last().expect("at least one server"))
108}