mirror of
https://codeberg.org/june64/mrvc.git
synced 2026-01-10 16:06:33 +01:00
Instead of creating a nested map on demand for the nested room infos, move to an extendable tree structure described by explicit types. This makes the information structure clearer and allows for easily adding information at various levels in the future.
490 lines
19 KiB
Go
490 lines
19 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/go-version"
|
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
|
"maunium.net/go/mautrix"
|
|
"maunium.net/go/mautrix/id"
|
|
|
|
"codeberg.org/june64/mrvc/config"
|
|
)
|
|
|
|
type HomeserverServerVersionInfo struct {
|
|
Homeserver string
|
|
ServerVersionInfo fclient.Version
|
|
}
|
|
|
|
type MaxRoomVersionPath struct {
|
|
RoomID id.RoomID
|
|
MaxRoomVersion string
|
|
}
|
|
|
|
type ServerPath struct {
|
|
RoomID id.RoomID
|
|
MaxRoomVersion string
|
|
Server string
|
|
}
|
|
|
|
type VersionPath struct {
|
|
RoomID id.RoomID
|
|
MaxRoomVersion string
|
|
Server string
|
|
Version string
|
|
}
|
|
|
|
type RoomInfoTree map[id.RoomID](*RoomInfo)
|
|
|
|
type RoomInfo struct {
|
|
MaxRoomVersions map[string](*MaxRoomVersionInfo)
|
|
}
|
|
|
|
type MaxRoomVersionInfo struct {
|
|
Servers map[string](*ServerInfo)
|
|
}
|
|
|
|
type ServerInfo struct {
|
|
Versions map[string](*VersionInfo)
|
|
}
|
|
|
|
type VersionInfo struct {
|
|
Homeservers map[string](*HomeserverInfo)
|
|
}
|
|
|
|
type HomeserverInfo struct {
|
|
MemberCount uint
|
|
}
|
|
|
|
var unknownServerVersionInfo = fclient.Version{
|
|
Server: struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
}{
|
|
Name: "unknown",
|
|
Version: "unknown",
|
|
},
|
|
}
|
|
|
|
func getServerVersionInfoByHomeserver(homeservers []string, federationClient *fclient.Client) map[string](fclient.Version) {
|
|
homeserverChannel := make(chan string)
|
|
go func() {
|
|
for _, hs := range homeservers {
|
|
homeserverChannel <- hs
|
|
}
|
|
close(homeserverChannel)
|
|
}()
|
|
|
|
homeserverSVInfoChannel := make(chan HomeserverServerVersionInfo)
|
|
var wg sync.WaitGroup
|
|
const numHomeserverSVInfoReceivers = 100
|
|
wg.Add(numHomeserverSVInfoReceivers)
|
|
for i := 0; i < numHomeserverSVInfoReceivers; i++ {
|
|
go func() {
|
|
for hs := range homeserverChannel {
|
|
serverVersionInfo, err := federationClient.GetVersion(context.Background(), spec.ServerName(hs))
|
|
if err != nil {
|
|
serverVersionInfo = unknownServerVersionInfo
|
|
}
|
|
homeserverSVInfoChannel <- HomeserverServerVersionInfo{hs, serverVersionInfo}
|
|
}
|
|
wg.Done()
|
|
}()
|
|
}
|
|
|
|
go func() {
|
|
wg.Wait()
|
|
close(homeserverSVInfoChannel)
|
|
}()
|
|
|
|
serverVersionInfoByHomeserver := make(map[string](fclient.Version))
|
|
for homeserverSVInfo := range homeserverSVInfoChannel {
|
|
serverVersionInfoByHomeserver[homeserverSVInfo.Homeserver] = homeserverSVInfo.ServerVersionInfo
|
|
}
|
|
return serverVersionInfoByHomeserver
|
|
}
|
|
|
|
// Gets the maximum supported room version for the given server-version-pair.
|
|
func getMaxRoomVersion(serverVersionInfo fclient.Version) string {
|
|
// Use only the first part of the version string as sometimes there are suffixes after a space, like " (<commit>)", which then can't be parsed.
|
|
serverVersion, err := version.NewVersion(strings.Split(serverVersionInfo.Server.Version, " ")[0])
|
|
if err != nil {
|
|
return "unkown"
|
|
}
|
|
|
|
switch serverVersionInfo.Server.Name {
|
|
case "Conduit":
|
|
// https://conduit.rs/
|
|
// https://conduit.rs/changelog/
|
|
// https://gitlab.com/famedly/conduit
|
|
switch {
|
|
// https://conduit.rs/changelog/#v0-10-8-2025-08-11
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.10.8"))):
|
|
return "v12"
|
|
// https://conduit.rs/changelog/#v0-7-0-2024-04-25
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.7.0"))):
|
|
return "v11"
|
|
// https://conduit.rs/changelog/#v0-5-0-2022-12-21
|
|
// Includes MR 400:
|
|
// https://gitlab.com/famedly/conduit/-/merge_requests/400/commits
|
|
// MR 400 includes a commit, which specifies v10 as stable.
|
|
// https://gitlab.com/famedly/conduit/-/commit/1e1a144dfa98429ef9f02d16045796b73013830d
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.5.0"))):
|
|
return "v10"
|
|
// https://conduit.rs/changelog/#v0-4-0-2022-06-23
|
|
// Includes MR 257:
|
|
// https://gitlab.com/famedly/conduit/-/merge_requests/257/commits
|
|
// MR 257 includes a commit, which specifies added support v7 to v9.
|
|
// https://gitlab.com/famedly/conduit/-/commit/0ae39807a478370a769217d01fa33514299a2b35
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.4.0"))):
|
|
return "v9"
|
|
// https://conduit.rs/changelog/#v0-2-0-2021-09-01
|
|
default:
|
|
return "v6"
|
|
}
|
|
case "conduwuit", "Conduwuit":
|
|
// Using continuwuitys source (history) as a reference:
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity
|
|
|
|
// History from first commit on "continuwuity":
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/commits/commit/e054a56b3286a6fb3091bedd5261089435ed26d1
|
|
// Last commit on "conduwuit"/"Conduwuit":
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/d8311a5ff672fdc4729d956af5e3af8646b0670d
|
|
// Last version was "0.5.0":
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/d8311a5ff672fdc4729d956af5e3af8646b0670d/Cargo.toml
|
|
|
|
// Matching both "conduwuit" and "Conduwuit" as the capitalization got changed.
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/commit/5b5ccba64e3d36a9235f4e0d449f40d859046dad
|
|
|
|
switch {
|
|
// Since the version went from "0.7.0-<something>" to "0.3.0" and the last version was "0.5.0", give all versions greater than "0.7.0" a lower max room version.
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/commit/af0b81f5fb737d3550cf22fbcf8d8b8eb1947408
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.7.0"))):
|
|
return "v10"
|
|
// v11 support (shows there as included in tag "v0.3.0"):
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/commit/223f05c922ea6f3085d386f0758df550459a25c3
|
|
// Tag "v0.3.0":
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/src/tag/v0.3.0/src/service/globals/mod.rs#L130
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.3.0"))):
|
|
return "v11"
|
|
// First version named "Conduwuit"/"conduwuit":
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/commit/e71855cd0b59d9274e8528a4c1ee480f99006c3c
|
|
// v10 support at first version named "Conduwuit"/"conduwuit":
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/e71855cd0b59d9274e8528a4c1ee480f99006c3c/src/service/globals/mod.rs#L187
|
|
// And shows first version as "0.7.0-alpha+conduwuit-0.1.3":
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/e71855cd0b59d9274e8528a4c1ee480f99006c3c/Cargo.toml
|
|
// Because of this versioning and the match on "0.7.0" before, this default should never match, but it is included for clarity.
|
|
default:
|
|
return "v10"
|
|
}
|
|
case "continuwuity":
|
|
// https://continuwuity.org/
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity
|
|
|
|
// First version named "continuwuity":
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/commit/dc599db19c48ad3cbae15fc419c4a531d217ed05
|
|
// v11 support at first version named "continuwuity":
|
|
// https://forgejo.ellis.link/continuwuation/continuwuity/src/commit/dc599db19c48ad3cbae15fc419c4a531d217ed05/src/core/info/room_version.rs
|
|
return "v11"
|
|
case "Dendrite":
|
|
// https://github.com/element-hq/dendrite
|
|
// https://element-hq.github.io/dendrite/
|
|
// https://github.com/element-hq/dendrite/blob/main/CHANGES.md
|
|
switch {
|
|
// https://github.com/element-hq/dendrite/blob/v0.15.0/CHANGES.md#dendrite-0150-2025-08-12
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.15.0"))):
|
|
return "v12"
|
|
// https://github.com/matrix-org/dendrite/blob/v0.13.3/CHANGES.md#dendrite-0133-2023-09-28
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.13.3"))):
|
|
return "v11"
|
|
// https://github.com/matrix-org/dendrite/blob/v0.8.7/CHANGES.md#dendrite-087-2022-06-01
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.8.7"))):
|
|
return "v10"
|
|
// https://github.com/matrix-org/dendrite/blob/v0.8.6/CHANGES.md#dendrite-086-2022-05-26
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.8.6"))):
|
|
return "v9"
|
|
// https://github.com/matrix-org/dendrite/blob/v0.8.6/CHANGES.md#dendrite-086-2022-05-26
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.8.6"))):
|
|
return "v8"
|
|
// https://github.com/matrix-org/dendrite/blob/v0.4.1/CHANGES.md#dendrite-041-2021-07-26
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.4.1"))):
|
|
return "v7"
|
|
// https://github.com/matrix-org/dendrite/blob/v0.1.0/CHANGES.md#dendrite-010-2020-10-08
|
|
default:
|
|
return "v6"
|
|
}
|
|
case "Synapse":
|
|
// https://github.com/element-hq/synapse/
|
|
// https://element-hq.github.io/synapse/latest/
|
|
// https://github.com/element-hq/synapse/blob/develop/CHANGES.md
|
|
// https://github.com/element-hq/synapse/blob/develop/docs/changelogs/
|
|
switch {
|
|
// https://github.com/element-hq/synapse/blob/v1.135.1/CHANGES.md#synapse-11351-2025-08-11
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("1.135.1"))):
|
|
return "v12"
|
|
// https://github.com/element-hq/synapse/blob/v1.89.0rc1/CHANGES.md#synapse-1890rc1-2023-07-25
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("1.89.0rc1"))):
|
|
return "v11"
|
|
// https://github.com/element-hq/synapse/blob/v1.64.0rc1/CHANGES.md#synapse-1640rc1-2022-07-26
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("1.64.0rc1"))):
|
|
return "v10"
|
|
// https://github.com/element-hq/synapse/blob/v1.42.0rc2/CHANGES.md#synapse-1420rc2-2021-09-06
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("1.42.0rc2"))):
|
|
return "v9"
|
|
// https://github.com/element-hq/synapse/blob/v1.40.0rc3/CHANGES.md#synapse-1400rc3-2021-08-09
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("1.40.0rc3"))):
|
|
return "v8"
|
|
// https://github.com/element-hq/synapse/blob/v1.37.0rc1/CHANGES.md#synapse-1370rc1-2021-06-24
|
|
// https://github.com/matrix-org/synapse/pull/10167
|
|
// https://github.com/matrix-org/synapse/pull/10167/files#diff-8e36e2fc4dec5d84357ee3d140ad223b66fb36e630c25bbb45382ca59cdb0f40
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("1.37.0rc1"))):
|
|
return "v7"
|
|
// https://github.com/element-hq/synapse/blob/v1.14.0rc1/CHANGES.md#synapse-1140rc1-2020-05-26
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("1.14.0rc1"))):
|
|
return "v6"
|
|
// https://github.com/element-hq/synapse/blob/v1.0.0rc1/CHANGES.md#synapse-100rc1-2019-06-07
|
|
// https://github.com/matrix-org/synapse/pull/5354
|
|
// https://github.com/matrix-org/synapse/pull/5354/files#diff-8e36e2fc4dec5d84357ee3d140ad223b66fb36e630c25bbb45382ca59cdb0f40
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("1.0.0rc1"))):
|
|
return "v5"
|
|
// https://github.com/element-hq/synapse/blob/v0.99.5rc1/CHANGES.md#synapse-0995rc1-2019-05-21
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.99.5rc1"))):
|
|
return "v4"
|
|
// https://github.com/element-hq/synapse/blob/v0.99.0rc1/CHANGES.md#synapse-0990rc1-2019-01-30
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.99.0rc1"))):
|
|
return "v3"
|
|
// https://github.com/element-hq/synapse/blob/v0.34.1rc1/CHANGES.md#synapse-0341rc1-2019-01-08
|
|
// https://github.com/matrix-org/synapse/pull/4307
|
|
// https://github.com/matrix-org/synapse/pull/4307/files#diff-32e0f096397b0e3545054c51c42b5be4f0db953083b60d76a047cb1618f6b877
|
|
case serverVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.34.1rc1"))):
|
|
return "v2"
|
|
default:
|
|
return "v1"
|
|
}
|
|
case "Tuwunel":
|
|
// https://github.com/matrix-construct/tuwunel
|
|
|
|
// First version named "Tuwunel":
|
|
// https://github.com/matrix-construct/tuwunel/commit/9b658d86b2f49d01ba8d2f5ae7f263a104e8f282#diff-d58f31e506570d0bcb7629251ec1b350c5dde4a205a338349f154f3fd3187e93
|
|
// v11 support at first version named "Tuwunel":
|
|
// https://github.com/matrix-construct/tuwunel/blob/9b658d86b2f49d01ba8d2f5ae7f263a104e8f282/src/core/info/room_version.rs
|
|
return "v11"
|
|
default:
|
|
return "unkown"
|
|
}
|
|
}
|
|
|
|
func compareVersionStrings(a, b string) int {
|
|
// Try to parse a and b as versions.
|
|
// Use only the first part of the version string as sometimes there are suffixes after a space, like " (<commit>)", which then can't be parsed.
|
|
aStart := strings.Split(a, " ")[0]
|
|
bStart := strings.Split(b, " ")[0]
|
|
aVersion, aErr := version.NewVersion(aStart)
|
|
bVersion, bErr := version.NewVersion(bStart)
|
|
// An input, which can't be parsed as a version, should be interpreted as being smaller than an input, which can be parsed as a version.
|
|
switch {
|
|
case aErr != nil && bErr == nil:
|
|
return -1
|
|
case aErr == nil && bErr != nil:
|
|
return 1
|
|
case aErr != nil && bErr != nil:
|
|
return strings.Compare(a, b)
|
|
}
|
|
|
|
if cmpResult := aVersion.Compare(bVersion); cmpResult != 0 {
|
|
return cmpResult
|
|
} else {
|
|
// When the versions are equal, look at the potential suffixes in the version string.
|
|
aEnd := strings.TrimPrefix(a, aStart)
|
|
bEnd := strings.TrimPrefix(b, bStart)
|
|
|
|
return strings.Compare(aEnd, bEnd)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
config := config.Get()
|
|
|
|
userId := id.UserID(config.UserID)
|
|
_, homeserver, err := userId.ParseAndValidate()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
clientWellKnown, err := mautrix.DiscoverClientAPI(context.Background(), homeserver)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
homeserverURL := clientWellKnown.Homeserver.BaseURL
|
|
|
|
client, err := mautrix.NewClient(
|
|
homeserverURL,
|
|
userId,
|
|
config.Token,
|
|
)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
federationClient := fclient.NewClient(
|
|
fclient.WithWellKnownSRVLookups(true),
|
|
fclient.WithTimeout(config.HomeserverVersionInfoTimeout),
|
|
)
|
|
|
|
givenRoomsByRoomID := make(map[id.RoomID]string)
|
|
for _, room := range config.Rooms {
|
|
// Check, if given room is an alias and try to resolve it into a room id.
|
|
if strings.HasPrefix(room, "#") {
|
|
resolvedAlias, err := client.ResolveAlias(context.Background(), id.RoomAlias(room))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
givenRoomsByRoomID[resolvedAlias.RoomID] = room
|
|
} else {
|
|
givenRoomsByRoomID[id.RoomID(room)] = room
|
|
}
|
|
}
|
|
|
|
joinedMembersByRoomID := make(map[id.RoomID]*mautrix.RespJoinedMembers)
|
|
for roomID := range givenRoomsByRoomID {
|
|
joinedMembers, err := client.JoinedMembers(context.Background(), roomID)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
joinedMembersByRoomID[roomID] = joinedMembers
|
|
}
|
|
|
|
membersByHomeserverByRoomID := make(map[id.RoomID](map[string]uint))
|
|
for roomID, joinedMembers := range joinedMembersByRoomID {
|
|
membersByHomeserver := make(map[string]uint)
|
|
for key := range joinedMembers.Joined {
|
|
membersByHomeserver[key.Homeserver()]++
|
|
}
|
|
membersByHomeserverByRoomID[roomID] = membersByHomeserver
|
|
}
|
|
|
|
homeserverSet := make(map[string]bool)
|
|
for _, membersByHomeserver := range membersByHomeserverByRoomID {
|
|
for hs := range membersByHomeserver {
|
|
homeserverSet[hs] = true
|
|
}
|
|
}
|
|
|
|
homeservers := make([]string, 0, len(homeserverSet))
|
|
for hs := range homeserverSet {
|
|
homeservers = append(homeservers, hs)
|
|
}
|
|
|
|
serverVersionInfoByHomeserver := getServerVersionInfoByHomeserver(homeservers, federationClient)
|
|
|
|
// Info map tree.
|
|
roomInfoTree := make(RoomInfoTree)
|
|
// Member counters.
|
|
membersByRoomID := make(map[id.RoomID]uint)
|
|
membersByMaxRoomVersion := make(map[MaxRoomVersionPath]uint)
|
|
membersByServerPath := make(map[ServerPath]uint)
|
|
membersByVersionPath := make(map[VersionPath]uint)
|
|
|
|
for roomID, membersByHomeserver := range membersByHomeserverByRoomID {
|
|
for hs, members := range membersByHomeserver {
|
|
serverVersionInfo := serverVersionInfoByHomeserver[hs]
|
|
|
|
maxRoomVersion := getMaxRoomVersion(serverVersionInfo)
|
|
|
|
// Add to counters.
|
|
membersByRoomID[roomID] += members
|
|
membersByMaxRoomVersion[MaxRoomVersionPath{roomID, maxRoomVersion}] += members
|
|
membersByServerPath[ServerPath{roomID, maxRoomVersion, serverVersionInfo.Server.Name}] += members
|
|
membersByVersionPath[VersionPath{roomID, maxRoomVersion, serverVersionInfo.Server.Name, serverVersionInfo.Server.Version}] += members
|
|
|
|
// Sort into roomInfoTree.
|
|
roomInfo, ok := roomInfoTree[roomID]
|
|
if !ok {
|
|
roomInfo = &RoomInfo{MaxRoomVersions: make(map[string]*MaxRoomVersionInfo)}
|
|
roomInfoTree[roomID] = roomInfo
|
|
}
|
|
maxRoomVersionInfo, ok := roomInfo.MaxRoomVersions[maxRoomVersion]
|
|
if !ok {
|
|
maxRoomVersionInfo = &MaxRoomVersionInfo{Servers: make(map[string]*ServerInfo)}
|
|
roomInfo.MaxRoomVersions[maxRoomVersion] = maxRoomVersionInfo
|
|
}
|
|
serverInfo, ok := maxRoomVersionInfo.Servers[serverVersionInfo.Server.Name]
|
|
if !ok {
|
|
serverInfo = &ServerInfo{Versions: make(map[string]*VersionInfo)}
|
|
maxRoomVersionInfo.Servers[serverVersionInfo.Server.Name] = serverInfo
|
|
}
|
|
versionInfo, ok := serverInfo.Versions[serverVersionInfo.Server.Version]
|
|
if !ok {
|
|
versionInfo = &VersionInfo{Homeservers: make(map[string]*HomeserverInfo)}
|
|
serverInfo.Versions[serverVersionInfo.Server.Version] = versionInfo
|
|
}
|
|
homeserverInfo, ok := versionInfo.Homeservers[hs]
|
|
if !ok {
|
|
homeserverInfo = &HomeserverInfo{}
|
|
versionInfo.Homeservers[hs] = homeserverInfo
|
|
}
|
|
homeserverInfo.MemberCount = members
|
|
}
|
|
}
|
|
|
|
for roomID, roomInfo := range roomInfoTree {
|
|
fmt.Println("Room:")
|
|
fmt.Printf(" %s -> %d\n", givenRoomsByRoomID[roomID], membersByRoomID[roomID])
|
|
|
|
fmt.Println("Version Support:")
|
|
|
|
maxRoomVersionKeys := make([]string, 0, len(roomInfo.MaxRoomVersions))
|
|
for key := range roomInfo.MaxRoomVersions {
|
|
maxRoomVersionKeys = append(maxRoomVersionKeys, key)
|
|
}
|
|
slices.SortFunc(maxRoomVersionKeys, compareVersionStrings)
|
|
for _, maxRoomVersionKey := range maxRoomVersionKeys {
|
|
maxRoomVersionInfo := roomInfo.MaxRoomVersions[maxRoomVersionKey]
|
|
|
|
fmt.Printf(" %s -> %d\n", maxRoomVersionKey, membersByMaxRoomVersion[MaxRoomVersionPath{roomID, maxRoomVersionKey}])
|
|
|
|
serverKeys := make([]string, 0, len(maxRoomVersionInfo.Servers))
|
|
for key := range maxRoomVersionInfo.Servers {
|
|
serverKeys = append(serverKeys, key)
|
|
}
|
|
sort.Strings(serverKeys)
|
|
for _, serverKey := range serverKeys {
|
|
serverInfo := maxRoomVersionInfo.Servers[serverKey]
|
|
|
|
fmt.Printf(" %s -> %d\n", serverKey, membersByServerPath[ServerPath{roomID, maxRoomVersionKey, serverKey}])
|
|
|
|
versionKeys := make([]string, 0, len(serverInfo.Versions))
|
|
for key := range serverInfo.Versions {
|
|
versionKeys = append(versionKeys, key)
|
|
}
|
|
slices.SortFunc(versionKeys, compareVersionStrings)
|
|
for _, versionKey := range versionKeys {
|
|
versionInfo := serverInfo.Versions[versionKey]
|
|
|
|
fmt.Printf(" %s -> %d\n", versionKey, membersByVersionPath[VersionPath{roomID, maxRoomVersionKey, serverKey, versionKey}])
|
|
|
|
if config.PrintHomeserverMemberCount {
|
|
homeserverKeys := make([]string, 0, len(versionInfo.Homeservers))
|
|
for key := range versionInfo.Homeservers {
|
|
homeserverKeys = append(homeserverKeys, key)
|
|
}
|
|
sort.Strings(homeserverKeys)
|
|
for _, homeserverKey := range homeserverKeys {
|
|
homeserverInfo := versionInfo.Homeservers[homeserverKey]
|
|
|
|
fmt.Printf(" %s -> %d\n", homeserverKey, homeserverInfo.MemberCount)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt.Println()
|
|
}
|
|
}
|