Show servers with no permission

This commit is contained in:
rxdn 2024-06-21 21:00:50 +01:00
parent a933f2e124
commit b67adc8a4e
3 changed files with 126 additions and 100 deletions

View File

@ -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) {

View File

@ -1,23 +1,30 @@
<div class="guild-badge" on:click={goto(guild.id)}>
<div class="guild-icon-bg">
{#if guild.icon === undefined || guild.icon === ""}
<i class="fas fa-question guild-icon-fa"></i>
{:else}
<img class="guild-icon" src="{getIconUrl()}" alt="Guild Icon"/>
{/if}
</div>
<div class="guild-badge" on:click={goto(guild.id)} class:disabled={guild.permission_level === 0}>
<div class="guild-icon-bg">
{#if guild.icon === undefined || guild.icon === ""}
<i class="fas fa-question guild-icon-fa" class:disabled={guild.permission_level === 0}></i>
{:else}
<img class="guild-icon" src="{getIconUrl()}" alt="Guild Icon"
class:disabled={guild.permission_level === 0}/>
{/if}
</div>
<div>
<span class="guild-name">
{guild.name}
<div class="text-wrapper" class:disabled={guild.permission_level === 0}>
<span class="guild-name">
{guild.name}
</span>
<span class="no-permission" class:disabled={guild.permission_level > 0}>
No permission
<Tooltip tip="You do not have permission to manage this server." top color="#121212">
<a href="https://docs.ticketsbot.net/miscellaneous/dashboard-no-permission" target="_blank">
<i class="fas fa-circle-question form-label tooltip-icon"></i>
</a>
</Tooltip>
</span>
</div>
</div>
</div>
<script>
import axios from 'axios';
import {API_URL} from "../js/constants";
import {notifyError} from "../js/util";
import Tooltip from "svelte-tooltip";
export let guild;
@ -40,19 +47,11 @@
async function goto(guildId) {
if (guild.permission_level === 2) {
window.location.href = `/manage/${guildId}/settings`;
} else {
} else if (guild.permission_level === 1) {
window.location.href = `/manage/${guildId}/transcripts`;
}
}
async function getPermissionLevel(guildId) {
const res = await axios.get(`${API_URL}/user/permissionlevel?guild=${guildId}`);
if (res.status !== 200 || !res.data.success) {
notifyError(res.data.error);
} else {
return;
}
return res.data.permission_level;
}
</script>
@ -70,6 +69,10 @@
cursor: pointer;
}
.guild-badge.disabled {
cursor: default;
}
@media (max-width: 950px) {
:global(.guild-badge) {
width: 100%;
@ -102,6 +105,27 @@
:global(.guild-name) {
color: white !important;
}
.text-wrapper.disabled > .guild-name {
opacity: 45%;
}
.guild-icon-bg > *.disabled {
opacity: 25%;
}
.text-wrapper {
display: flex;
flex-direction: column;
padding-left: 10px;
}
.text-wrapper > .no-permission {
opacity: 75%;
}
.text-wrapper > .no-permission.disabled {
visibility: hidden;
}
</style>

View File

@ -3,7 +3,7 @@
<i class="fas fa-plus fa-2x guild-icon-fa"></i>
</div>
<div>
<div style="padding-left: 10px">
<span class="guild-name">Invite to your server</span>
</div>
</div>