1
0
Fork 0
mirror of https://codeberg.org/june64/mrvc.git synced 2026-01-09 23:52:54 +01:00
mrvc/main.go
June f727df2cf4
add first version of mrvc (max room version checker)
Add first version of mrvc (max room version checker) - a tool for
determining how many members support which maximum room version for a
given Matrix room.
2025-08-15 18:05:14 +02:00

327 lines
14 KiB
Go

package main
import (
"context"
"fmt"
"log"
"os"
"sort"
"strings"
"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"
)
type ServerPath struct {
MaxRoomVersion string
Server string
}
type VersionPath struct {
MaxRoomVersion string
Server string
Version string
}
var unknownServerVersionInfo = fclient.Version{
Server: struct {
Name string `json:"name"`
Version string `json:"version"`
}{
Name: "unknown",
Version: "unknown",
},
}
// 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 main() {
userLocalpart := os.Getenv("MATRIX_USER_LOCALPART")
homeserver := os.Getenv("MATRIX_HOMESERVER")
token := os.Getenv("MATRIX_TOKEN")
room := os.Getenv("MATRIX_ROOM")
verbose := false
client, err := mautrix.NewClient(
homeserver,
id.NewUserID(userLocalpart, homeserver),
token,
)
if err != nil {
log.Fatal(err)
}
federationClient := fclient.NewClient(
fclient.WithWellKnownSRVLookups(true),
)
joinedMembers, err := client.JoinedMembers(context.Background(), id.RoomID(room))
if err != nil {
log.Fatal(err)
}
membersByHomeservers := make(map[string]uint)
for key := range joinedMembers.Joined {
membersByHomeservers[key.Homeserver()]++
}
// Info map tree.
maxRoomVersionInfo := make(map[string](map[string](map[string](map[string]uint))))
// Member counters.
membersByMaxRoomVersion := make(map[string]uint)
membersByServerPath := make(map[ServerPath]uint)
membersByVersionPath := make(map[VersionPath]uint)
for key, value := range membersByHomeservers {
serverVersionInfo, err := federationClient.GetVersion(context.Background(), spec.ServerName(key))
if err != nil {
serverVersionInfo = unknownServerVersionInfo
}
maxRoomVersion := getMaxRoomVersion(serverVersionInfo)
// Add to counters.
membersByMaxRoomVersion[maxRoomVersion] += value
membersByServerPath[ServerPath{maxRoomVersion, serverVersionInfo.Server.Name}] += value
membersByVersionPath[VersionPath{maxRoomVersion, serverVersionInfo.Server.Name, serverVersionInfo.Server.Version}] += value
// Sort into maxRoomVersionInfo map tree.
serverMap, ok := maxRoomVersionInfo[maxRoomVersion]
if !ok {
serverMap = make(map[string](map[string](map[string]uint)))
maxRoomVersionInfo[maxRoomVersion] = serverMap
}
versionMap, ok := serverMap[serverVersionInfo.Server.Name]
if !ok {
versionMap = make(map[string](map[string]uint))
serverMap[serverVersionInfo.Server.Name] = versionMap
}
homeserverMap, ok := versionMap[serverVersionInfo.Server.Version]
if !ok {
homeserverMap = make(map[string]uint)
versionMap[serverVersionInfo.Server.Version] = homeserverMap
}
homeserverMap[key] = value
}
fmt.Println("Room:")
fmt.Printf("%s -> %d\n\n", room, len(joinedMembers.Joined))
fmt.Println("Version Support:")
maxRoomVersionKeys := make([]string, 0, len(maxRoomVersionInfo))
for key := range maxRoomVersionInfo {
maxRoomVersionKeys = append(maxRoomVersionKeys, key)
}
sort.Strings(maxRoomVersionKeys)
for _, maxRoomVersionKey := range maxRoomVersionKeys {
maxRoomVersionValue := maxRoomVersionInfo[maxRoomVersionKey]
fmt.Printf("%s -> %d\n", maxRoomVersionKey, membersByMaxRoomVersion[maxRoomVersionKey])
serverKeys := make([]string, 0, len(maxRoomVersionValue))
for key := range maxRoomVersionValue {
serverKeys = append(serverKeys, key)
}
sort.Strings(serverKeys)
for _, serverKey := range serverKeys {
serverValue := maxRoomVersionValue[serverKey]
fmt.Printf(" %s -> %d\n", serverKey, membersByServerPath[ServerPath{maxRoomVersionKey, serverKey}])
versionKeys := make([]string, 0, len(serverValue))
for key := range serverValue {
versionKeys = append(versionKeys, key)
}
sort.Strings(versionKeys)
for _, versionKey := range versionKeys {
versionValue := serverValue[versionKey]
fmt.Printf(" %s -> %d\n", versionKey, membersByVersionPath[VersionPath{maxRoomVersionKey, serverKey, versionKey}])
if verbose {
homeserverKeys := make([]string, 0, len(versionValue))
for key := range versionValue {
homeserverKeys = append(homeserverKeys, key)
}
sort.Strings(homeserverKeys)
for _, homeserverKey := range homeserverKeys {
homeserverValue := versionValue[homeserverKey]
fmt.Printf(" %s -> %d\n", homeserverKey, homeserverValue)
}
}
}
}
}
}