From b67adc8a4ecd62bc6a80a0a6f12cc11f881e5b08 Mon Sep 17 00:00:00 2001 From: rxdn <29165304+rxdn@users.noreply.github.com> Date: Fri, 21 Jun 2024 21:00:50 +0100 Subject: [PATCH] Show servers with no permission --- app/http/endpoints/api/guilds.go | 150 +++++++++++---------- frontend/src/components/Guild.svelte | 74 ++++++---- frontend/src/components/InviteBadge.svelte | 2 +- 3 files changed, 126 insertions(+), 100 deletions(-) diff --git a/app/http/endpoints/api/guilds.go b/app/http/endpoints/api/guilds.go index cfac950..cc9d336 100644 --- a/app/http/endpoints/api/guilds.go +++ b/app/http/endpoints/api/guilds.go @@ -1,19 +1,21 @@ package api import ( + "cmp" "context" - "errors" dbclient "github.com/TicketsBot/GoPanel/database" "github.com/TicketsBot/GoPanel/rpc/cache" "github.com/TicketsBot/GoPanel/utils" + "github.com/TicketsBot/common/collections" "github.com/TicketsBot/common/permission" syncutils "github.com/TicketsBot/common/utils" "github.com/TicketsBot/database" "github.com/gin-gonic/gin" "github.com/jackc/pgtype" - "github.com/rxdn/gdl/rest/request" "golang.org/x/sync/errgroup" - "sort" + "slices" + "sync" + "time" ) type wrappedGuild struct { @@ -23,101 +25,101 @@ type wrappedGuild struct { PermissionLevel permission.PermissionLevel `json:"permission_level"` } -func GetGuilds(ctx *gin.Context) { - userId := ctx.Keys["userid"].(uint64) +func GetGuilds(c *gin.Context) { + userId := c.Keys["userid"].(uint64) - // Get all guilds the user is in - guilds, err := dbclient.Client.UserGuilds.Get(userId) + // Get the guilds that the user is in, that the bot is also in + userGuilds, err := getGuildIntersection(userId) if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) - return - } - - // Get the subset of guilds that the user is in that the bot is also in - guildIds := make([]uint64, len(guilds)) - guildMap := make(map[uint64]database.UserGuild) // Make a map of all guilds for O(1) access - for i, guild := range guilds { - guildIds[i] = guild.GuildId - guildMap[guild.GuildId] = guild - } - - botGuilds, err := getExistingGuilds(guildIds) - if err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + c.JSON(500, utils.ErrorJson(err)) return } wg := syncutils.NewChannelWaitGroup() - wg.Add(len(botGuilds)) + wg.Add(len(userGuilds)) - group, _ := errgroup.WithContext(context.Background()) - ch := make(chan wrappedGuild) - for _, guildId := range botGuilds { - guildId := guildId - g := guildMap[guildId] + ctx, cancel := context.WithTimeout(c, time.Second*10) + defer cancel() + + group, ctx := errgroup.WithContext(ctx) + + var mu sync.Mutex + guilds := make([]wrappedGuild, 0, len(userGuilds)) + for _, guild := range userGuilds { + guild := guild group.Go(func() error { defer wg.Done() - // Determine the user's permission level in this guild - var permLevel permission.PermissionLevel - if g.Owner { - permLevel = permission.Admin - } else { - tmp, err := utils.GetPermissionLevel(context.Background(), g.GuildId, userId) - if err != nil { - // If a Discord error occurs, just skip the server - var restError request.RestError - if errors.As(err, &restError) { - return nil - } else { - return err - } - } - - permLevel = tmp + permLevel, err := utils.GetPermissionLevel(ctx, guild.GuildId, userId) + if err != nil { + return err } - if permLevel >= permission.Support { - wrapped := wrappedGuild{ - Id: g.GuildId, - Name: g.Name, - Icon: g.Icon, - PermissionLevel: permLevel, - } - - ch <- wrapped - } + mu.Lock() + guilds = append(guilds, wrappedGuild{ + Id: guild.GuildId, + Name: guild.Name, + Icon: guild.Icon, + PermissionLevel: permLevel, + }) + mu.Unlock() return nil }) } - adminGuilds := make([]wrappedGuild, 0) - group.Go(func() error { - loop: - for { - select { - case <-wg.Wait(): - break loop - case guild := <-ch: - adminGuilds = append(adminGuilds, guild) - } - } - return nil - }) - if err := group.Wait(); err != nil { - ctx.JSON(500, utils.ErrorJson(err)) + c.JSON(500, utils.ErrorJson(err)) return } - // sort - sort.Slice(adminGuilds, func(i, j int) bool { - return adminGuilds[i].Name < adminGuilds[j].Name + // Sort the guilds by name, but put the guilds with permission_level=0 last + slices.SortFunc(guilds, func(a, b wrappedGuild) int { + if a.PermissionLevel == 0 && b.PermissionLevel > 0 { + return 1 + } else if a.PermissionLevel > 0 && b.PermissionLevel == 0 { + return -1 + } + + return cmp.Compare(a.Name, b.Name) }) - ctx.JSON(200, adminGuilds) + c.JSON(200, guilds) +} + +func getGuildIntersection(userId uint64) ([]database.UserGuild, error) { + // Get all the guilds that the user is in + userGuilds, err := dbclient.Client.UserGuilds.Get(userId) + if err != nil { + return nil, err + } + + guildIds := make([]uint64, len(userGuilds)) + for i, guild := range userGuilds { + guildIds[i] = guild.GuildId + } + + // Restrict the set of guilds to guilds that the bot is also in + botGuilds, err := getExistingGuilds(guildIds) + if err != nil { + return nil, err + } + + botGuildIds := collections.NewSet[uint64]() + for _, guildId := range botGuilds { + botGuildIds.Add(guildId) + } + + // Get the intersection of the two sets + intersection := make([]database.UserGuild, 0, len(botGuilds)) + for _, guild := range userGuilds { + if botGuildIds.Contains(guild.GuildId) { + intersection = append(intersection, guild) + } + } + + return intersection, nil } func getExistingGuilds(userGuilds []uint64) ([]uint64, error) { diff --git a/frontend/src/components/Guild.svelte b/frontend/src/components/Guild.svelte index 9a38aa4..fc79ca4 100644 --- a/frontend/src/components/Guild.svelte +++ b/frontend/src/components/Guild.svelte @@ -1,23 +1,30 @@ -