From c42777e741dcbfb64c981d2eb45ab767c3768464 Mon Sep 17 00:00:00 2001 From: June Date: Sun, 17 Aug 2025 18:38:04 +0200 Subject: [PATCH] introduce support for checking multiple rooms in one go --- config/config.go | 26 ++++-- main.go | 205 ++++++++++++++++++++++++++++------------------- 2 files changed, 142 insertions(+), 89 deletions(-) diff --git a/config/config.go b/config/config.go index 3380c4e..aef42c4 100644 --- a/config/config.go +++ b/config/config.go @@ -10,6 +10,8 @@ import ( "time" ) +type rooms []string + type configOption[T any] struct { flagName string flagDefaultValue T @@ -20,7 +22,7 @@ type configOption[T any] struct { type configOptions struct { userID configOption[string] token configOption[string] - room configOption[string] + rooms configOption[rooms] printHomeserverMemberCount configOption[bool] homeserverVersionInfoTimeout configOption[time.Duration] } @@ -28,7 +30,7 @@ type configOptions struct { type Config struct { UserID string Token string - Room string + Rooms rooms PrintHomeserverMemberCount bool HomeserverVersionInfoTimeout time.Duration } @@ -36,11 +38,20 @@ type Config struct { var configOpts = configOptions{ userID: configOption[string]{"user-id", "", "The Matrix user ID to use.", "MRVC_USER_ID"}, token: configOption[string]{"token", "", "The Matrix access token to use.", "MRVC_TOKEN"}, - room: configOption[string]{"room", "", "The Matrix room to check.", "MRVC_ROOM"}, + rooms: configOption[rooms]{"room", []string{}, "The Matrix room to check. The flag can be set multiple times to check multiple rooms.", "MRVC_ROOM"}, printHomeserverMemberCount: configOption[bool]{"print-homeserver-member-count", false, "Print the member count for each homeserver.", "MRVC_PRINT_HOMESERVER_MEMBER_COUNT"}, homeserverVersionInfoTimeout: configOption[time.Duration]{"homeserver-version-info-timeout", time.Second * 5, "Timeout for getting the homeservers version information per homeserver.", "MRVC_HOMESERVER_VERSION_INFO_TIMEOUT"}, } +func (r *rooms) String() string { + return fmt.Sprint(*r) +} + +func (r *rooms) Set(value string) error { + *r = append(*r, value) + return nil +} + func (configOpt configOption[T]) getFlagHelp() string { return fmt.Sprintf("%s (EnvVar: %s)", configOpt.flagHelpBase, configOpt.envVarName) } @@ -74,10 +85,15 @@ func (configOpt configOption[T]) getConfigValueWithError(configFlag *T, visitedF var userIdFlag = flag.String(configOpts.userID.getFlagArgs()) var tokenFlag = flag.String(configOpts.token.getFlagArgs()) -var roomFlag = flag.String(configOpts.room.getFlagArgs()) var printHomeserverMemberCountFlag = flag.Bool(configOpts.printHomeserverMemberCount.getFlagArgs()) var homeserverVersionInfoTimeoutFlag = flag.Duration(configOpts.homeserverVersionInfoTimeout.getFlagArgs()) +var roomsFlag rooms + +func init() { + flag.Var(&roomsFlag, configOpts.rooms.flagName, configOpts.rooms.getFlagHelp()) +} + func Get() Config { flag.Parse() @@ -98,7 +114,7 @@ func Get() Config { if err != nil { log.Fatal("A Matrix access token must be provided.") } - config.Room, err = configOpts.room.getConfigValueWithError(roomFlag, visitedFlags, func(envVar string) string { return envVar }) + config.Rooms, err = configOpts.rooms.getConfigValueWithError(&roomsFlag, visitedFlags, func(envVar string) rooms { return []string{envVar} }) if err != nil { log.Fatal("A Matrix room must be provided.") } diff --git a/main.go b/main.go index ff4a8bc..9dc9753 100644 --- a/main.go +++ b/main.go @@ -23,12 +23,19 @@ type HomeserverServerVersionInfo struct { 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 @@ -308,119 +315,149 @@ func main() { fclient.WithTimeout(config.HomeserverVersionInfoTimeout), ) - var roomId id.RoomID - // Check, if given room is an alias and try to resolve it into a room id. - if strings.HasPrefix(config.Room, "#") { - resolvedAlias, err := client.ResolveAlias(context.Background(), id.RoomAlias(config.Room)) + 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) } - roomId = resolvedAlias.RoomID - } else { - roomId = id.RoomID(config.Room) + joinedMembersByRoomID[roomID] = joinedMembers } - joinedMembers, err := client.JoinedMembers(context.Background(), roomId) - if err != nil { - log.Fatal(err) + 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 } - membersByHomeservers := make(map[string]uint) - for key := range joinedMembers.Joined { - membersByHomeservers[key.Homeserver()]++ + homeserverSet := make(map[string]bool) + for _, membersByHomeserver := range membersByHomeserverByRoomID { + for hs := range membersByHomeserver { + homeserverSet[hs] = true + } } - homeservers := make([]string, 0, len(membersByHomeservers)) - for hs := range membersByHomeservers { + homeservers := make([]string, 0, len(homeserverSet)) + for hs := range homeserverSet { homeservers = append(homeservers, hs) } serverVersionInfoByHomeserver := getServerVersionInfoByHomeserver(homeservers, federationClient) // Info map tree. - maxRoomVersionInfo := make(map[string](map[string](map[string](map[string]uint)))) + roomMaxVersionInfos := make(map[id.RoomID](map[string](map[string](map[string](map[string]uint))))) // Member counters. - membersByMaxRoomVersion := make(map[string]uint) + membersByRoomID := make(map[id.RoomID]uint) + membersByMaxRoomVersion := make(map[MaxRoomVersionPath]uint) membersByServerPath := make(map[ServerPath]uint) membersByVersionPath := make(map[VersionPath]uint) - for key, value := range membersByHomeservers { - serverVersionInfo := serverVersionInfoByHomeserver[key] + for roomID, membersByHomeserver := range membersByHomeserverByRoomID { + for hs, members := range membersByHomeserver { + serverVersionInfo := serverVersionInfoByHomeserver[hs] - maxRoomVersion := getMaxRoomVersion(serverVersionInfo) + 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 + // 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 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", config.Room, len(joinedMembers.Joined)) - - fmt.Println("Version Support:") - - maxRoomVersionKeys := make([]string, 0, len(maxRoomVersionInfo)) - for key := range maxRoomVersionInfo { - maxRoomVersionKeys = append(maxRoomVersionKeys, key) - } - slices.SortFunc(maxRoomVersionKeys, compareVersionStrings) - 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 into roomMaxVersionInfos map tree. + maxRoomVersionMap, ok := roomMaxVersionInfos[roomID] + if !ok { + maxRoomVersionMap = make(map[string](map[string](map[string](map[string]uint)))) + roomMaxVersionInfos[roomID] = maxRoomVersionMap } - slices.SortFunc(versionKeys, compareVersionStrings) - for _, versionKey := range versionKeys { - versionValue := serverValue[versionKey] + serverMap, ok := maxRoomVersionMap[maxRoomVersion] + if !ok { + serverMap = make(map[string](map[string](map[string]uint))) + maxRoomVersionMap[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[hs] = members + } + } - fmt.Printf(" %s -> %d\n", versionKey, membersByVersionPath[VersionPath{maxRoomVersionKey, serverKey, versionKey}]) + for roomID, roomIDValue := range roomMaxVersionInfos { + fmt.Println("Room:") + fmt.Printf(" %s -> %d\n", givenRoomsByRoomID[roomID], membersByRoomID[roomID]) - if config.PrintHomeserverMemberCount { - 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.Println("Version Support:") - fmt.Printf(" %s -> %d\n", homeserverKey, homeserverValue) + maxRoomVersionKeys := make([]string, 0, len(roomIDValue)) + for key := range roomIDValue { + maxRoomVersionKeys = append(maxRoomVersionKeys, key) + } + slices.SortFunc(maxRoomVersionKeys, compareVersionStrings) + for _, maxRoomVersionKey := range maxRoomVersionKeys { + maxRoomVersionValue := roomIDValue[maxRoomVersionKey] + + fmt.Printf(" %s -> %d\n", maxRoomVersionKey, membersByMaxRoomVersion[MaxRoomVersionPath{roomID, 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{roomID, maxRoomVersionKey, serverKey}]) + + versionKeys := make([]string, 0, len(serverValue)) + for key := range serverValue { + versionKeys = append(versionKeys, key) + } + slices.SortFunc(versionKeys, compareVersionStrings) + for _, versionKey := range versionKeys { + versionValue := serverValue[versionKey] + + fmt.Printf(" %s -> %d\n", versionKey, membersByVersionPath[VersionPath{roomID, maxRoomVersionKey, serverKey, versionKey}]) + + if config.PrintHomeserverMemberCount { + 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) + } } } } } + + fmt.Println() } }