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