Show servers with no permission
This commit is contained in:
parent
a933f2e124
commit
b67adc8a4e
@ -1,19 +1,21 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
dbclient "github.com/TicketsBot/GoPanel/database"
|
dbclient "github.com/TicketsBot/GoPanel/database"
|
||||||
"github.com/TicketsBot/GoPanel/rpc/cache"
|
"github.com/TicketsBot/GoPanel/rpc/cache"
|
||||||
"github.com/TicketsBot/GoPanel/utils"
|
"github.com/TicketsBot/GoPanel/utils"
|
||||||
|
"github.com/TicketsBot/common/collections"
|
||||||
"github.com/TicketsBot/common/permission"
|
"github.com/TicketsBot/common/permission"
|
||||||
syncutils "github.com/TicketsBot/common/utils"
|
syncutils "github.com/TicketsBot/common/utils"
|
||||||
"github.com/TicketsBot/database"
|
"github.com/TicketsBot/database"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jackc/pgtype"
|
"github.com/jackc/pgtype"
|
||||||
"github.com/rxdn/gdl/rest/request"
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"sort"
|
"slices"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type wrappedGuild struct {
|
type wrappedGuild struct {
|
||||||
@ -23,101 +25,101 @@ type wrappedGuild struct {
|
|||||||
PermissionLevel permission.PermissionLevel `json:"permission_level"`
|
PermissionLevel permission.PermissionLevel `json:"permission_level"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGuilds(ctx *gin.Context) {
|
func GetGuilds(c *gin.Context) {
|
||||||
userId := ctx.Keys["userid"].(uint64)
|
userId := c.Keys["userid"].(uint64)
|
||||||
|
|
||||||
// Get all guilds the user is in
|
// Get the guilds that the user is in, that the bot is also in
|
||||||
guilds, err := dbclient.Client.UserGuilds.Get(userId)
|
userGuilds, err := getGuildIntersection(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.JSON(500, utils.ErrorJson(err))
|
c.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))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wg := syncutils.NewChannelWaitGroup()
|
wg := syncutils.NewChannelWaitGroup()
|
||||||
wg.Add(len(botGuilds))
|
wg.Add(len(userGuilds))
|
||||||
|
|
||||||
group, _ := errgroup.WithContext(context.Background())
|
ctx, cancel := context.WithTimeout(c, time.Second*10)
|
||||||
ch := make(chan wrappedGuild)
|
defer cancel()
|
||||||
for _, guildId := range botGuilds {
|
|
||||||
guildId := guildId
|
group, ctx := errgroup.WithContext(ctx)
|
||||||
g := guildMap[guildId]
|
|
||||||
|
var mu sync.Mutex
|
||||||
|
guilds := make([]wrappedGuild, 0, len(userGuilds))
|
||||||
|
for _, guild := range userGuilds {
|
||||||
|
guild := guild
|
||||||
|
|
||||||
group.Go(func() error {
|
group.Go(func() error {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
// Determine the user's permission level in this guild
|
permLevel, err := utils.GetPermissionLevel(ctx, guild.GuildId, userId)
|
||||||
var permLevel permission.PermissionLevel
|
|
||||||
if g.Owner {
|
|
||||||
permLevel = permission.Admin
|
|
||||||
} else {
|
|
||||||
tmp, err := utils.GetPermissionLevel(context.Background(), g.GuildId, userId)
|
|
||||||
if err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
permLevel = tmp
|
mu.Lock()
|
||||||
}
|
guilds = append(guilds, wrappedGuild{
|
||||||
|
Id: guild.GuildId,
|
||||||
if permLevel >= permission.Support {
|
Name: guild.Name,
|
||||||
wrapped := wrappedGuild{
|
Icon: guild.Icon,
|
||||||
Id: g.GuildId,
|
|
||||||
Name: g.Name,
|
|
||||||
Icon: g.Icon,
|
|
||||||
PermissionLevel: permLevel,
|
PermissionLevel: permLevel,
|
||||||
}
|
})
|
||||||
|
mu.Unlock()
|
||||||
ch <- wrapped
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
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 {
|
if err := group.Wait(); err != nil {
|
||||||
ctx.JSON(500, utils.ErrorJson(err))
|
c.JSON(500, utils.ErrorJson(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort
|
// Sort the guilds by name, but put the guilds with permission_level=0 last
|
||||||
sort.Slice(adminGuilds, func(i, j int) bool {
|
slices.SortFunc(guilds, func(a, b wrappedGuild) int {
|
||||||
return adminGuilds[i].Name < adminGuilds[j].Name
|
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) {
|
func getExistingGuilds(userGuilds []uint64) ([]uint64, error) {
|
||||||
|
@ -1,23 +1,30 @@
|
|||||||
<div class="guild-badge" on:click={goto(guild.id)}>
|
<div class="guild-badge" on:click={goto(guild.id)} class:disabled={guild.permission_level === 0}>
|
||||||
<div class="guild-icon-bg">
|
<div class="guild-icon-bg">
|
||||||
{#if guild.icon === undefined || guild.icon === ""}
|
{#if guild.icon === undefined || guild.icon === ""}
|
||||||
<i class="fas fa-question guild-icon-fa"></i>
|
<i class="fas fa-question guild-icon-fa" class:disabled={guild.permission_level === 0}></i>
|
||||||
{:else}
|
{:else}
|
||||||
<img class="guild-icon" src="{getIconUrl()}" alt="Guild Icon"/>
|
<img class="guild-icon" src="{getIconUrl()}" alt="Guild Icon"
|
||||||
|
class:disabled={guild.permission_level === 0}/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="text-wrapper" class:disabled={guild.permission_level === 0}>
|
||||||
<span class="guild-name">
|
<span class="guild-name">
|
||||||
{guild.name}
|
{guild.name}
|
||||||
</span>
|
</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>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
import Tooltip from "svelte-tooltip";
|
||||||
import {API_URL} from "../js/constants";
|
|
||||||
import {notifyError} from "../js/util";
|
|
||||||
|
|
||||||
export let guild;
|
export let guild;
|
||||||
|
|
||||||
@ -40,19 +47,11 @@
|
|||||||
async function goto(guildId) {
|
async function goto(guildId) {
|
||||||
if (guild.permission_level === 2) {
|
if (guild.permission_level === 2) {
|
||||||
window.location.href = `/manage/${guildId}/settings`;
|
window.location.href = `/manage/${guildId}/settings`;
|
||||||
} else {
|
} else if (guild.permission_level === 1) {
|
||||||
window.location.href = `/manage/${guildId}/transcripts`;
|
window.location.href = `/manage/${guildId}/transcripts`;
|
||||||
}
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.data.permission_level;
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -70,6 +69,10 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.guild-badge.disabled {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 950px) {
|
@media (max-width: 950px) {
|
||||||
:global(.guild-badge) {
|
:global(.guild-badge) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -102,6 +105,27 @@
|
|||||||
|
|
||||||
:global(.guild-name) {
|
:global(.guild-name) {
|
||||||
color: white !important;
|
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;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-wrapper > .no-permission {
|
||||||
|
opacity: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-wrapper > .no-permission.disabled {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<i class="fas fa-plus fa-2x guild-icon-fa"></i>
|
<i class="fas fa-plus fa-2x guild-icon-fa"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div style="padding-left: 10px">
|
||||||
<span class="guild-name">Invite to your server</span>
|
<span class="guild-name">Invite to your server</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user