summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Utils.cpp106
1 files changed, 81 insertions, 25 deletions
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 63dcdd00..2a0c4673 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -21,6 +21,7 @@
 
 #include <array>
 #include <cmath>
+#include <unordered_set>
 #include <variant>
 
 #include <cmark.h>
@@ -1263,30 +1264,87 @@ utils::roomVias(const std::string &roomid)
 {
     std::vector<std::string> vias;
 
+    // for joined rooms
     {
-        auto members = cache::getMembers(roomid, 0, 100);
+        // see https://spec.matrix.org/v1.6/appendices/#routing for the algorithm
+
+        auto members = cache::roomMembers(roomid);
         if (!members.empty()) {
-            vias.push_back(http::client()->user_id().hostname());
+            auto powerlevels =
+              cache::client()->getStateEvent<mtx::events::state::PowerLevels>(roomid).value_or(
+                mtx::events::StateEvent<mtx::events::state::PowerLevels>{});
+
+            std::unordered_set<std::string> users_with_high_pl;
+            std::set<std::string> users_with_high_pl_in_room;
+            // we should pick PL > 50, but imo that is broken, so we just pick users who have admins
+            // perm
+            for (const auto &user : powerlevels.content.users) {
+                if (user.second >= powerlevels.content.events_default &&
+                    user.second >= powerlevels.content.state_default) {
+                    users_with_high_pl.insert(user.first);
+                }
+            }
+
+            std::unordered_map<std::string, std::size_t> usercount_by_server;
             for (const auto &m : members) {
-                if (vias.size() >= 4)
-                    break;
+                auto user_id = mtx::identifiers::parse<mtx::identifiers::User>(m);
+                usercount_by_server[user_id.hostname()] += 1;
 
-                auto user_id =
-                  mtx::identifiers::parse<mtx::identifiers::User>(m.user_id.toStdString());
+                if (users_with_high_pl.count(m))
+                    users_with_high_pl_in_room.insert(m);
+            }
 
-                auto server = user_id.hostname();
-                if (std::find(begin(vias), end(vias), server) == vias.end())
-                    vias.push_back(server);
+            // TODO(Nico): remove acled servers
+
+            // add the highest powerlevel user
+            auto max_pl_user = std::max_element(
+              users_with_high_pl_in_room.begin(),
+              users_with_high_pl_in_room.end(),
+              [&pl_content = powerlevels.content](const std::string &a, const std::string &b) {
+                  return pl_content.user_level(a) < pl_content.user_level(b);
+              });
+            if (max_pl_user != users_with_high_pl_in_room.end()) {
+                auto host =
+                  mtx::identifiers::parse<mtx::identifiers::User>(*max_pl_user).hostname();
+                vias.push_back(host);
+                usercount_by_server.erase(host);
+            }
+
+            // add up to 3 users, by usercount size from that server
+            std::vector<std::pair<std::size_t, std::string>> servers_sorted_by_usercount;
+            servers_sorted_by_usercount.reserve(usercount_by_server.size());
+            for (const auto &[server, count] : usercount_by_server)
+                servers_sorted_by_usercount.emplace_back(count, server);
+
+            std::sort(servers_sorted_by_usercount.begin(),
+                      servers_sorted_by_usercount.end(),
+                      [](const auto &a, const auto &b) {
+                          if (a.first == b.first)
+                              // same pl, sort lex smaller server first
+                              return a.second < b.second;
+
+                          // sort high user count first
+                          return a.first > b.first;
+                      });
+
+            for (const auto &server : servers_sorted_by_usercount) {
+                if (vias.size() >= 3)
+                    break;
+
+                vias.push_back(server.second);
             }
+
+            return vias;
         }
     }
 
-    if (vias.empty()) {
+    // for invites
+    {
         auto members = cache::getMembersFromInvite(roomid, 0, 100);
         if (!members.empty()) {
             vias.push_back(http::client()->user_id().hostname());
             for (const auto &m : members) {
-                if (vias.size() >= 4)
+                if (vias.size() >= 3)
                     break;
 
                 auto user_id =
@@ -1296,24 +1354,22 @@ utils::roomVias(const std::string &roomid)
                 if (std::find(begin(vias), end(vias), server) == vias.end())
                     vias.push_back(server);
             }
+
+            return vias;
         }
     }
 
-    if (vias.empty()) {
-        auto parents = cache::client()->getParentRoomIds(roomid);
-        for (const auto &p : parents) {
-            auto child =
-              cache::client()->getStateEvent<mtx::events::state::space::Child>(p, roomid);
-            if (child && child->content.via)
-                vias.insert(vias.end(), child->content.via->begin(), child->content.via->end());
-        }
+    // for space previews
+    auto parents = cache::client()->getParentRoomIds(roomid);
+    for (const auto &p : parents) {
+        auto child = cache::client()->getStateEvent<mtx::events::state::space::Child>(p, roomid);
+        if (child && child->content.via)
+            vias.insert(vias.end(), child->content.via->begin(), child->content.via->end());
+    }
 
-        std::sort(begin(vias), end(vias));
-        auto last = std::unique(begin(vias), end(vias));
-        vias.erase(last, end(vias));
+    std::sort(begin(vias), end(vias));
+    auto last = std::unique(begin(vias), end(vias));
+    vias.erase(last, end(vias));
 
-        // if (vias.size()> 3)
-        //     vias.erase(begin(vias)+3, end(vias));
-    }
     return vias;
 }