Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions bot/button/handlers/addadmin.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ func (h *AddAdminHandler) Execute(ctx *context.ButtonContext) {
continue
}

// Placeholder overwrites must not be written back
if ch.IsObfuscated() {
ch, err = rest.GetChannel(ctx, ctx.Worker().Token, ctx.Worker().RateLimiter, *ticket.ChannelId)
if err != nil || ch.IsObfuscated() {
continue
}
}

// Apply overwrites to existing channels
overwrites := append(ch.PermissionOverwrites, channel.PermissionOverwrite{
Id: id,
Expand Down
8 changes: 8 additions & 0 deletions bot/button/handlers/addsupport.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ func updateChannelPermissions(ctx cmdregistry.CommandContext, id uint64, mention
continue
}

// Placeholder overwrites must not be written back
if ch.IsObfuscated() {
ch, err = rest.GetChannel(ctx, ctx.Worker().Token, ctx.Worker().RateLimiter, *ticket.ChannelId)
if err != nil || ch.IsObfuscated() {
continue
}
}

// Apply overwrites to existing channels
overwrites := append(ch.PermissionOverwrites, channel.PermissionOverwrite{
Id: id,
Expand Down
46 changes: 26 additions & 20 deletions bot/button/handlers/admindebug/server/modals/panelsettings.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/TicketsBot-cloud/database"
"github.com/TicketsBot-cloud/gdl/objects/interaction"
"github.com/TicketsBot-cloud/gdl/objects/interaction/component"
w "github.com/TicketsBot-cloud/worker"
"github.com/TicketsBot-cloud/worker/bot/button/registry"
"github.com/TicketsBot-cloud/worker/bot/button/registry/matcher"
"github.com/TicketsBot-cloud/worker/bot/command"
Expand Down Expand Up @@ -77,6 +78,12 @@ func (h *AdminDebugServerPanelSettingsModalHandler) Execute(ctx *context.ModalCo

selectedValues := selectData.Values

worker, err := utils.WorkerForGuild(ctx, ctx.Worker(), guildId)
if err != nil {
ctx.HandleError(err)
return
}

// Get all panels for this guild
panels, err := dbclient.Client.Panel.GetByGuild(ctx, guildId)
if err != nil {
Expand Down Expand Up @@ -113,7 +120,7 @@ func (h *AdminDebugServerPanelSettingsModalHandler) Execute(ctx *context.ModalCo
}

// Build settings for this panel
panelSettings := buildPanelSettings(ctx, selectedPanel)
panelSettings := buildPanelSettings(ctx, worker, selectedPanel)
results = append(results, panelSettings)
}

Expand All @@ -127,7 +134,21 @@ func (h *AdminDebugServerPanelSettingsModalHandler) Execute(ctx *context.ModalCo
}))
}

func buildPanelSettings(ctx *context.ModalContext, selectedPanel *database.Panel) string {
func channelLabel(worker *w.Context, channelId uint64, prefix, missing string) string {
ch, err := worker.GetChannel(channelId)
switch {
case utils.IsMissingAccess(err), err == nil && ch.IsObfuscated():
return fmt.Sprintf("`%d` (no access - bot is missing View Channel)", channelId)
case utils.IsUnknownChannel(err):
return fmt.Sprintf("`%d` (%s)", channelId, missing)
case err != nil:
return fmt.Sprintf("`%d` (could not fetch)", channelId)
default:
return fmt.Sprintf("`%s%s` (%d)", prefix, ch.Name, channelId)
}
}

func buildPanelSettings(ctx *context.ModalContext, worker *w.Context, selectedPanel *database.Panel) string {
var settings []string

// Panel header
Expand All @@ -145,32 +166,17 @@ func buildPanelSettings(ctx *context.ModalContext, selectedPanel *database.Panel

// Panel channel
if selectedPanel.ChannelId != 0 {
channel, err := ctx.Worker().GetChannel(selectedPanel.ChannelId)
if err == nil {
settings = append(settings, fmt.Sprintf("**Panel Channel:** `#%s` (%d)", channel.Name, selectedPanel.ChannelId))
} else {
settings = append(settings, fmt.Sprintf("**Panel Channel:** `%d` (channel not found)", selectedPanel.ChannelId))
}
settings = append(settings, "**Panel Channel:** "+channelLabel(worker, selectedPanel.ChannelId, "#", "channel not found"))
}

// Target category (for channel mode)
if !selectedPanel.UseThreads && selectedPanel.TargetCategory != 0 {
category, err := ctx.Worker().GetChannel(selectedPanel.TargetCategory)
if err == nil {
settings = append(settings, fmt.Sprintf("**Target Category:** `%s` (%d)", category.Name, selectedPanel.TargetCategory))
} else {
settings = append(settings, fmt.Sprintf("**Target Category:** `%d` (category not found)", selectedPanel.TargetCategory))
}
settings = append(settings, "**Target Category:** "+channelLabel(worker, selectedPanel.TargetCategory, "", "category not found"))
}

// Transcript channel
if selectedPanel.TranscriptChannelId != nil {
channel, err := ctx.Worker().GetChannel(*selectedPanel.TranscriptChannelId)
if err == nil {
settings = append(settings, fmt.Sprintf("**Transcript Channel:** `#%s` (%d)", channel.Name, *selectedPanel.TranscriptChannelId))
} else {
settings = append(settings, fmt.Sprintf("**Transcript Channel:** `%d` (channel not found)", *selectedPanel.TranscriptChannelId))
}
settings = append(settings, "**Transcript Channel:** "+channelLabel(worker, *selectedPanel.TranscriptChannelId, "#", "channel not found"))
}

// Other settings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,10 +306,14 @@ func checkPanelPermissions(worker *w.Context, guildId uint64, botMember member.M

func checkChannelPermissions(worker *w.Context, channelId uint64, botMember member.Member, guildId uint64, requiredPermissions []permission.Permission, label string) (string, bool) {
channel, err := worker.GetChannel(channelId)
if err != nil {
if err != nil && !utils.IsMissingAccess(err) {
return fmt.Sprintf("**%s**\nError: Could not fetch channel", label), false
}

if err != nil || channel.IsObfuscated() {
return fmt.Sprintf("**%s** (`%d`)\n**Missing Permissions:**\n- View Channel\n", label, channelId), true
}

// Use permissionwrapper to get missing permissions
missingPerms := permissionwrapper.GetMissingPermissionsChannel(worker, guildId, botMember.User.Id, channelId, requiredPermissions...)

Expand Down
3 changes: 1 addition & 2 deletions bot/button/handlers/admindebug/server/recache.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ func (h *AdminDebugServerRecacheHandler) Execute(ctx *context.ButtonContext) {

// purge cache
ctx.Worker().Cache.DeleteGuild(ctx, guildId)
ctx.Worker().Cache.DeleteGuildChannels(ctx, guildId)
ctx.Worker().Cache.DeleteGuildRoles(ctx, guildId)

worker, err := utils.WorkerForGuild(ctx, ctx.Worker(), guildId)
Expand All @@ -74,7 +73,7 @@ func (h *AdminDebugServerRecacheHandler) Execute(ctx *context.ButtonContext) {
return
}

guildChannels, err := worker.GetGuildChannels(guildId)
guildChannels, err := utils.RecacheGuildChannels(ctx, worker, guildId, time.Second*3, true)
if err != nil {
ctx.HandleError(err)
return
Expand Down
3 changes: 1 addition & 2 deletions bot/command/impl/admin/adminrecache.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ func (AdminRecacheCommand) Execute(ctx registry.CommandContext, guildIdRaw strin

// purge cache
ctx.Worker().Cache.DeleteGuild(ctx, guildId)
ctx.Worker().Cache.DeleteGuildChannels(ctx, guildId)
ctx.Worker().Cache.DeleteGuildRoles(ctx, guildId)

// re-cache
Expand All @@ -80,7 +79,7 @@ func (AdminRecacheCommand) Execute(ctx registry.CommandContext, guildIdRaw strin
return
}

guildChannels, err := worker.GetGuildChannels(guildId)
guildChannels, err := utils.RecacheGuildChannels(ctx, worker, guildId, time.Second*3, true)
if err != nil {
ctx.HandleError(err)
return
Expand Down
13 changes: 13 additions & 0 deletions bot/logic/claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/TicketsBot-cloud/worker/bot/command/registry"
"github.com/TicketsBot-cloud/worker/bot/customisation"
"github.com/TicketsBot-cloud/worker/bot/dbclient"
"github.com/TicketsBot-cloud/worker/bot/permissionwrapper"
"github.com/TicketsBot-cloud/worker/bot/utils"
"github.com/TicketsBot-cloud/worker/i18n"
"golang.org/x/sync/errgroup"
Expand Down Expand Up @@ -94,6 +95,18 @@ func ApplyClaim(ctx context.Context, cmd registry.CommandContext, ticket databas
return err
}

// Placeholder overwrites must not be written back
if currentChannel.IsObfuscated() {
currentChannel, err = rest.GetChannel(ctx, cmd.Worker().Token, cmd.Worker().RateLimiter, *ticket.ChannelId)
if err != nil {
return err
}

if currentChannel.IsObfuscated() {
return permissionwrapper.ErrChannelObfuscated
}
}

// Always update the name to match the new claimed naming scheme
shouldUpdateName := true
// But skip if the user has manually renamed the channel (doesn't match old unclaimed name)
Expand Down
58 changes: 48 additions & 10 deletions bot/logic/open.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/TicketsBot-cloud/worker/bot/permissionwrapper"
"github.com/TicketsBot-cloud/worker/bot/redis"
"github.com/TicketsBot-cloud/worker/bot/utils"
"github.com/TicketsBot-cloud/worker/config"
"github.com/TicketsBot-cloud/worker/i18n"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
Expand Down Expand Up @@ -165,11 +166,18 @@ func OpenTicket(ctx context.Context, cmd registry.InteractionContext, panel *dat
return database.Ticket{}, nil
}

// Check if the user can send messages in threads in the parent channel
if !permissionwrapper.HasPermissionsChannel(
cmd.Worker(), cmd.GuildId(), cmd.UserId(), cmd.ChannelId(),
permission.SendMessagesInThreads,
) {
appPermissions := cmd.InteractionMetadata().AppPermissions
if appPermissions != 0 && !permission.HasPermissionRaw(appPermissions, permission.Administrator) && !permission.HasPermissionRaw(appPermissions, permission.ViewChannel) {
docsUrl := fmt.Sprintf("%s/miscellaneous/permissions-explained", config.Conf.Bot.DocsUrl)
message := cmd.GetMessage(i18n.MessageErrorMissingPermissionsTitle) + ":\n"
message += "* `View Channel`\n"
message += "\n" + cmd.GetMessage(i18n.MessageErrorMissingPermissionsBody, docsUrl)

cmd.ReplyRaw(customisation.Red, cmd.GetMessage(i18n.Error), message)
return database.Ticket{}, nil
}

if !canSendMessagesInThreads(cmd) {
cmd.Reply(customisation.Red, i18n.Error, i18n.MessageOpenCantMessageInThreads, cmd.ChannelId())
return database.Ticket{}, nil
}
Expand Down Expand Up @@ -745,11 +753,15 @@ func checkChannelLimitAndDetermineParentId(
if !utils.ContainsFunc(channels, func(c channel.Channel) bool {
return c.Id == categoryId
}) {
if err := dbclient.Client.Panel.SetOverflow(ctx, panelId, false, nil); err != nil {
return 0, err
if _, err := worker.GetChannel(categoryId); err != nil {
if utils.IsUnknownChannel(err) {
if err := dbclient.Client.Panel.SetOverflow(ctx, panelId, false, nil); err != nil {
return 0, err
}
}

return 0, errCategoryChannelLimitReached
}

return 0, errCategoryChannelLimitReached
}

// Check that the overflow category still has space
Expand All @@ -770,13 +782,39 @@ func checkChannelLimitAndDetermineParentId(
return categoryId, nil
}

func canSendMessagesInThreads(cmd registry.InteractionContext) bool {
// Zero when the member had to be looked up outside the interaction
if member, err := cmd.Member(); err == nil && member.Permissions != 0 {
return permission.HasPermissionRaw(member.Permissions, permission.Administrator) ||
permission.HasPermissionRaw(member.Permissions, permission.SendMessagesInThreads)
}

return permissionwrapper.HasPermissionsChannel(
cmd.Worker(), cmd.GuildId(), cmd.UserId(), cmd.ChannelId(),
permission.SendMessagesInThreads,
)
}

func refreshCachedChannels(ctx context.Context, worker *worker.Context, guildId uint64) error {
channels, err := rest.GetGuildChannels(ctx, worker.Token, worker.RateLimiter, guildId)
if err != nil {
return err
}

return worker.Cache.ReplaceChannels(ctx, guildId, channels)
if len(channels) > 0 {
if err := worker.Cache.StoreChannels(ctx, channels); err != nil {
return err
}
}

// Probing would outlast the 3 second ticket open lock
go func() {
if err := utils.PruneUnlistedChannels(context.Background(), worker, guildId, channels, time.Second*10, false); err != nil {
sentry.Error(err)
}
}()

return nil
}

// TODO: Use translation of tickets
Expand Down
19 changes: 18 additions & 1 deletion bot/permissionwrapper/permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/TicketsBot-cloud/worker"
)

var ErrChannelObfuscated = errors.New("channel metadata is obfuscated")

func HasPermissionsChannel(ctx *worker.Context, guildId, userId, channelId uint64, permissions ...permission.Permission) bool {
sum, err := getEffectivePermissionsChannel(ctx, guildId, userId, channelId)
if err != nil {
Expand Down Expand Up @@ -60,7 +62,10 @@ func getAllPermissionsChannel(ctx *worker.Context, guildId, userId, channelId ui

sum, err := getEffectivePermissionsChannel(ctx, guildId, userId, channelId)
if err != nil {
sentry.Error(err)
if !errors.Is(err, ErrChannelObfuscated) {
sentry.Error(err)
}

return permissions
}

Expand Down Expand Up @@ -118,6 +123,10 @@ func GetMissingPermissionsChannel(ctx *worker.Context, guildId, userId, channelI

sum, err := getEffectivePermissionsChannel(ctx, guildId, userId, channelId)
if err != nil {
if errors.Is(err, ErrChannelObfuscated) {
return []permission.Permission{permission.ViewChannel}
}

sentry.Error(err)
return required
}
Expand Down Expand Up @@ -146,6 +155,10 @@ func getEffectivePermissionsChannel(ctx *worker.Context, guildId, userId, channe
return 0, err
}

if permission.HasPermissionRaw(permissions, permission.Administrator) {
return permissions, nil
}

permissions, err = getChannelBasePermissions(ctx, guildId, channelId, permissions)
if err != nil {
return 0, err
Expand Down Expand Up @@ -255,6 +268,10 @@ func getChannelBasePermissions(ctx *worker.Context, guildId, channelId uint64, i
return 0, err
}

if ch.IsObfuscated() {
return 0, ErrChannelObfuscated
}

for _, overwrite := range ch.PermissionOverwrites {
if overwrite.Type == channel.PermissionTypeRole && overwrite.Id == publicRole.Id {
initialPermissions &= ^overwrite.Deny
Expand Down
42 changes: 42 additions & 0 deletions bot/redis/hiddenchannelcache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package redis

import (
"context"
"fmt"
"time"
)

const hiddenChannelExpiry = time.Hour * 24

func hiddenChannelKey(channelId uint64) string {
return fmt.Sprintf("hiddenchannel:%d", channelId)
}

func SetChannelHidden(ctx context.Context, channelId uint64) error {
return Client.Set(ctx, hiddenChannelKey(channelId), 1, hiddenChannelExpiry).Err()
}

func GetHiddenChannels(ctx context.Context, channelIds []uint64) (map[uint64]bool, error) {
if len(channelIds) == 0 {
return nil, nil
}

keys := make([]string, len(channelIds))
for i, id := range channelIds {
keys[i] = hiddenChannelKey(id)
}

res, err := Client.MGet(ctx, keys...).Result()
if err != nil {
return nil, err
}

hidden := make(map[uint64]bool)
for i, value := range res {
if value != nil {
hidden[channelIds[i]] = true
}
}

return hidden, nil
}
Loading
Loading