From 0ff992f0062f0fe3d1266a39785b570637b4c347 Mon Sep 17 00:00:00 2001 From: biast12 Date: Wed, 4 Mar 2026 23:21:09 +0100 Subject: [PATCH 1/5] gdl --- app/http/endpoints/api/channels.go | 8 ++++---- app/http/endpoints/api/guild.go | 2 +- app/http/endpoints/api/panel/multipanelcreate.go | 2 +- app/http/endpoints/api/panel/panelcreate.go | 2 +- app/http/endpoints/api/panel/panelmessagedata.go | 4 ++-- app/http/endpoints/api/panel/panelupdate.go | 2 +- app/http/endpoints/api/panel/validation.go | 4 ++-- app/http/endpoints/api/settings/updatesettings.go | 4 ++-- app/http/endpoints/api/ticket/placeholders.go | 4 ++-- app/http/endpoints/api/ticket/sendmessage.go | 6 +++--- app/http/endpoints/api/ticket/sendtag.go | 6 +++--- go.mod | 8 ++++---- go.sum | 10 ++-------- utils/guildutils.go | 4 ++-- utils/types/emoji.go | 6 ++++-- 15 files changed, 34 insertions(+), 38 deletions(-) diff --git a/app/http/endpoints/api/channels.go b/app/http/endpoints/api/channels.go index 4674b701..5e2308c3 100644 --- a/app/http/endpoints/api/channels.go +++ b/app/http/endpoints/api/channels.go @@ -59,9 +59,9 @@ func ChannelsHandler(ctx *gin.Context) { filtered := make([]channel.Channel, 0, len(channels)) for _, ch := range channels { // Filter out threads - if ch.Type == channel.ChannelTypeGuildNewsThread || - ch.Type == channel.ChannelTypeGuildPrivateThread || - ch.Type == channel.ChannelTypeGuildPublicThread { + if ch.Type == channel.ChannelTypeAnnouncementThread || + ch.Type == channel.ChannelTypePrivateThread || + ch.Type == channel.ChannelTypePublicThread { continue } @@ -69,7 +69,7 @@ func ChannelsHandler(ctx *gin.Context) { } sort.Slice(filtered, func(i, j int) bool { - return filtered[i].Position < filtered[j].Position + return *filtered[i].Position < *filtered[j].Position }) ctx.JSON(200, filtered) diff --git a/app/http/endpoints/api/guild.go b/app/http/endpoints/api/guild.go index 15896fa7..518e2b2e 100644 --- a/app/http/endpoints/api/guild.go +++ b/app/http/endpoints/api/guild.go @@ -24,6 +24,6 @@ func GuildHandler(ctx *gin.Context) { ctx.JSON(200, gin.H{ "id": guild.Id, "name": guild.Name, - "icon": guild.Icon, + "icon": guild.IconUrl(), }) } diff --git a/app/http/endpoints/api/panel/multipanelcreate.go b/app/http/endpoints/api/panel/multipanelcreate.go index 02fa2550..05fae64c 100644 --- a/app/http/endpoints/api/panel/multipanelcreate.go +++ b/app/http/endpoints/api/panel/multipanelcreate.go @@ -232,7 +232,7 @@ func (d *multiPanelCreateData) validateChannel(guildId uint64) func() error { var valid bool for _, ch := range channels { - if ch.Id == d.ChannelId && (ch.Type == channel.ChannelTypeGuildText || ch.Type == channel.ChannelTypeGuildNews) { + if ch.Id == d.ChannelId && (ch.Type == channel.ChannelTypeGuildText || ch.Type == channel.ChannelTypeGuildAnnouncement) { valid = true break } diff --git a/app/http/endpoints/api/panel/panelcreate.go b/app/http/endpoints/api/panel/panelcreate.go index 793e7ab5..2d66ff59 100644 --- a/app/http/endpoints/api/panel/panelcreate.go +++ b/app/http/endpoints/api/panel/panelcreate.go @@ -197,7 +197,7 @@ func CreatePanel(c *gin.Context) { { emoji := data.getEmoji() if emoji != nil { - emojiName = &emoji.Name + emojiName = emoji.Name if emoji.Id.Value != 0 { emojiId = &emoji.Id.Value diff --git a/app/http/endpoints/api/panel/panelmessagedata.go b/app/http/endpoints/api/panel/panelmessagedata.go index 27355b4a..3681edb4 100644 --- a/app/http/endpoints/api/panel/panelmessagedata.go +++ b/app/http/endpoints/api/panel/panelmessagedata.go @@ -32,12 +32,12 @@ func panelIntoMessageData(panel database.Panel, isPremium bool) panelMessageData if panel.EmojiName != nil { // No emoji = nil if panel.EmojiId == nil { // Unicode emoji emote = &emoji.Emoji{ - Name: *panel.EmojiName, + Name: panel.EmojiName, } } else { // Custom emoji emote = &emoji.Emoji{ Id: objects.NewNullableSnowflake(*panel.EmojiId), - Name: *panel.EmojiName, + Name: panel.EmojiName, } } } diff --git a/app/http/endpoints/api/panel/panelupdate.go b/app/http/endpoints/api/panel/panelupdate.go index 9bd95128..d0b540f8 100644 --- a/app/http/endpoints/api/panel/panelupdate.go +++ b/app/http/endpoints/api/panel/panelupdate.go @@ -126,7 +126,7 @@ func UpdatePanel(c *gin.Context) { { emoji := data.getEmoji() if emoji != nil { - emojiName = &emoji.Name + emojiName = emoji.Name if emoji.Id.Value != 0 { emojiId = &emoji.Id.Value diff --git a/app/http/endpoints/api/panel/validation.go b/app/http/endpoints/api/panel/validation.go index 9d849f4d..bf934290 100644 --- a/app/http/endpoints/api/panel/validation.go +++ b/app/http/endpoints/api/panel/validation.go @@ -104,7 +104,7 @@ func validateContent(ctx PanelValidationContext) validation.ValidationFunc { func validateChannelId(ctx PanelValidationContext) validation.ValidationFunc { return func() error { for _, ch := range ctx.Channels { - if ch.Id == ctx.Data.ChannelId && (ch.Type == channel.ChannelTypeGuildText || ch.Type == channel.ChannelTypeGuildNews) { + if ch.Id == ctx.Data.ChannelId && (ch.Type == channel.ChannelTypeGuildText || ch.Type == channel.ChannelTypeGuildAnnouncement) { return nil } } @@ -151,7 +151,7 @@ func validateEmoji(c PanelValidationContext) validation.ValidationFunc { return validation.NewInvalidInputError("Emoji not found") } - if resolvedEmoji.Name != emoji.Name { + if resolvedEmoji.Name == nil || *resolvedEmoji.Name != emoji.Name { return validation.NewInvalidInputError("Emoji name mismatch") } } else { diff --git a/app/http/endpoints/api/settings/updatesettings.go b/app/http/endpoints/api/settings/updatesettings.go index 76ce1ace..0d305357 100644 --- a/app/http/endpoints/api/settings/updatesettings.go +++ b/app/http/endpoints/api/settings/updatesettings.go @@ -262,7 +262,7 @@ func (s *Settings) Validate(ctx context.Context, guildId uint64, premiumTier pre return fmt.Errorf("Invalid overflow category") } - if ch.GuildId != guildId { + if *ch.GuildId != guildId { return fmt.Errorf("Overflow category guild ID does not match") } @@ -284,7 +284,7 @@ func (s *Settings) Validate(ctx context.Context, guildId uint64, premiumTier pre return fmt.Errorf("Invalid ticket notification channel") } - if ch.GuildId != guildId { + if *ch.GuildId != guildId { return fmt.Errorf("Ticket notification channel guild ID does not match") } diff --git a/app/http/endpoints/api/ticket/placeholders.go b/app/http/endpoints/api/ticket/placeholders.go index 811d632d..776af3f2 100644 --- a/app/http/endpoints/api/ticket/placeholders.go +++ b/app/http/endpoints/api/ticket/placeholders.go @@ -34,8 +34,8 @@ func replacePlaceholders(ctx context.Context, content string, ticket *database.T } if member, err := botCtx.GetGuildMember(ctx, ticket.GuildId, ticket.UserId); err == nil { - if member.Nick != "" { - content = strings.ReplaceAll(content, "%nickname%", member.Nick) + if member.Nick != nil { + content = strings.ReplaceAll(content, "%nickname%", *member.Nick) } } diff --git a/app/http/endpoints/api/ticket/sendmessage.go b/app/http/endpoints/api/ticket/sendmessage.go index c786454f..214893a7 100644 --- a/app/http/endpoints/api/ticket/sendmessage.go +++ b/app/http/endpoints/api/ticket/sendmessage.go @@ -115,7 +115,7 @@ func SendMessage(ctx *gin.Context) { Username: guild.Name, AvatarUrl: guild.IconUrl(), AllowedMentions: messagetypes.AllowedMention{ - Parse: []messagetypes.AllowedMentionType{messagetypes.USERS, messagetypes.ROLES, messagetypes.EVERYONE}, + Parse: []messagetypes.AllowedMentionType{messagetypes.AllowedMentionTypeRoles, messagetypes.AllowedMentionTypeUsers, messagetypes.AllowedMentionTypeEveryone}, }, } } else { @@ -130,7 +130,7 @@ func SendMessage(ctx *gin.Context) { Username: user.EffectiveName(), AvatarUrl: user.AvatarUrl(256), AllowedMentions: messagetypes.AllowedMention{ - Parse: []messagetypes.AllowedMentionType{messagetypes.USERS, messagetypes.ROLES, messagetypes.EVERYONE}, + Parse: []messagetypes.AllowedMentionType{messagetypes.AllowedMentionTypeRoles, messagetypes.AllowedMentionTypeUsers, messagetypes.AllowedMentionTypeEveryone}, }, } } @@ -175,7 +175,7 @@ func SendMessage(ctx *gin.Context) { if _, err = rest.CreateMessage(ctx, botContext.Token, botContext.RateLimiter, *ticket.ChannelId, rest.CreateMessageData{ Content: message, AllowedMentions: messagetypes.AllowedMention{ - Parse: []messagetypes.AllowedMentionType{messagetypes.USERS, messagetypes.ROLES, messagetypes.EVERYONE}, + Parse: []messagetypes.AllowedMentionType{messagetypes.AllowedMentionTypeUsers, messagetypes.AllowedMentionTypeRoles, messagetypes.AllowedMentionTypeEveryone}, }, }); err != nil { ctx.JSON(500, utils.ErrorStr("Failed to send message to ticket #%d in channel %d", ticketId, *ticket.ChannelId)) diff --git a/app/http/endpoints/api/ticket/sendtag.go b/app/http/endpoints/api/ticket/sendtag.go index 018c1a29..2c7a53ce 100644 --- a/app/http/endpoints/api/ticket/sendtag.go +++ b/app/http/endpoints/api/ticket/sendtag.go @@ -142,7 +142,7 @@ func SendTag(ctx *gin.Context) { Username: guild.Name, AvatarUrl: guild.IconUrl(), AllowedMentions: messagetypes.AllowedMention{ - Parse: []messagetypes.AllowedMentionType{messagetypes.USERS, messagetypes.ROLES, messagetypes.EVERYONE}, + Parse: []messagetypes.AllowedMentionType{messagetypes.AllowedMentionTypeUsers, messagetypes.AllowedMentionTypeRoles, messagetypes.AllowedMentionTypeEveryone}, }, } } else { @@ -158,7 +158,7 @@ func SendTag(ctx *gin.Context) { Username: user.EffectiveName(), AvatarUrl: user.AvatarUrl(256), AllowedMentions: messagetypes.AllowedMention{ - Parse: []messagetypes.AllowedMentionType{messagetypes.USERS, messagetypes.ROLES, messagetypes.EVERYONE}, + Parse: []messagetypes.AllowedMentionType{messagetypes.AllowedMentionTypeUsers, messagetypes.AllowedMentionTypeRoles, messagetypes.AllowedMentionTypeEveryone}, }, } } @@ -204,7 +204,7 @@ func SendTag(ctx *gin.Context) { Content: message, Embeds: embeds, AllowedMentions: messagetypes.AllowedMention{ - Parse: []messagetypes.AllowedMentionType{messagetypes.USERS, messagetypes.ROLES, messagetypes.EVERYONE}, + Parse: []messagetypes.AllowedMentionType{messagetypes.AllowedMentionTypeUsers, messagetypes.AllowedMentionTypeRoles, messagetypes.AllowedMentionTypeEveryone}, }, }); err != nil { ctx.JSON(500, utils.ErrorStr("Failed to send tag '%s' to ticket #%d in channel %d", body.TagId, ticketId, *ticket.ChannelId)) diff --git a/go.mod b/go.mod index 0aefd5e6..9d155b26 100644 --- a/go.mod +++ b/go.mod @@ -8,13 +8,13 @@ toolchain go1.24.2 //replace github.com/TicketsBot-cloud/common => ../common -//replace github.com/TicketsBot-cloud/gdl => ../gdl +replace github.com/TicketsBot-cloud/gdl => ../gdl -//replace github.com/TicketsBot-cloud/logarchiver => ../logarchiver +replace github.com/TicketsBot-cloud/logarchiver => ../logarchiver //replace github.com/TicketsBot-cloud/archiverclient => ../archiverclient -//replace github.com/TicketsBot-cloud/worker => ../worker +replace github.com/TicketsBot-cloud/worker => ../worker replace github.com/go-playground/validator/v10 => github.com/go-playground/validator/v10 v10.14.0 @@ -22,7 +22,7 @@ require ( github.com/BurntSushi/toml v1.2.1 github.com/TicketsBot-cloud/archiverclient v0.0.0-20251015181023-f0b66a074704 github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e - github.com/TicketsBot-cloud/database v0.0.0-20260224195559-d1e01011ada7 + github.com/TicketsBot-cloud/database v0.0.0-20260226214338-bf6af6ba954e github.com/TicketsBot-cloud/gdl v0.0.0-20260213180045-11af01c262ca github.com/TicketsBot-cloud/logarchiver v0.0.0-20251018211319-7a7df5cacbdc github.com/TicketsBot-cloud/worker v0.0.0-20251212162840-a9cc9bbf5692 diff --git a/go.sum b/go.sum index 90bc11fe..6dbd73f6 100644 --- a/go.sum +++ b/go.sum @@ -47,14 +47,8 @@ github.com/TicketsBot-cloud/archiverclient v0.0.0-20251015181023-f0b66a074704 h1 github.com/TicketsBot-cloud/archiverclient v0.0.0-20251015181023-f0b66a074704/go.mod h1:Mux1bEPpOHwRw1wo6Fa6qJLJH9Erk9qv1yAIfLi1Wmw= github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e h1:nFKV7yEm8MWbCP7dtsJ88+agcxDUD0YKIotVHMVvytw= github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e/go.mod h1:tGrTHFz09OM3eDWF+62hIi9ELpT4igCFi868FKSvKBg= -github.com/TicketsBot-cloud/database v0.0.0-20260224195559-d1e01011ada7 h1:OBjtVnQ+h6Hec6psRgClafcA32y2xYImlgyBu/eIhDM= -github.com/TicketsBot-cloud/database v0.0.0-20260224195559-d1e01011ada7/go.mod h1:HQXAgmNSm7/FmBYwcsa6qpZqMrDhbLoEl+AyqFQ+RwY= -github.com/TicketsBot-cloud/gdl v0.0.0-20260213180045-11af01c262ca h1:/HRqcgOPfv6d9NzE6CqHXN4U1QgElyJ9DcxNNT8kV6g= -github.com/TicketsBot-cloud/gdl v0.0.0-20260213180045-11af01c262ca/go.mod h1:CdwBR2egPtxUXjD2CgC9ZwfuB8dz9HPePM8nuG6dt7Y= -github.com/TicketsBot-cloud/logarchiver v0.0.0-20251018211319-7a7df5cacbdc h1:qTLNpCvIqM7UwZ6MdWQ9EztcDsIJfHh+VJdG+ULLEaA= -github.com/TicketsBot-cloud/logarchiver v0.0.0-20251018211319-7a7df5cacbdc/go.mod h1:pZqkzPNNTqnwKZvCT8kCaTHxrG7HJbxZV83S0p7mmzM= -github.com/TicketsBot-cloud/worker v0.0.0-20251212162840-a9cc9bbf5692 h1:80COkRlCghOngYJYHE6lyxQteldz30LpEfjxoPrFlxM= -github.com/TicketsBot-cloud/worker v0.0.0-20251212162840-a9cc9bbf5692/go.mod h1:mL0jcnGqmNZEGw2M6IZIgiX7rkEYj26IF5EglEwVGYY= +github.com/TicketsBot-cloud/database v0.0.0-20260226214338-bf6af6ba954e h1:BWHHGDDxhZmsGl6KP4xXmvrTyvUbO9nCgUAgAunAxEI= +github.com/TicketsBot-cloud/database v0.0.0-20260226214338-bf6af6ba954e/go.mod h1:HQXAgmNSm7/FmBYwcsa6qpZqMrDhbLoEl+AyqFQ+RwY= github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1 h1:FqC1KGOsmB+ikvbmDkyNQU6bGUWyfYq8Ip9r4KxTveY= github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1/go.mod h1:N7zwetwx8B3RK/ZajWwMroJSyv2ZJ+bIOZWv/z8DhaM= github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261 h1:NHD5GB6cjlkpZFjC76Yli2S63/J2nhr8MuE6KlYJpQM= diff --git a/utils/guildutils.go b/utils/guildutils.go index e063a42e..3d51875b 100644 --- a/utils/guildutils.go +++ b/utils/guildutils.go @@ -66,7 +66,7 @@ func LoadGuilds(ctx context.Context, accessToken string, userId uint64) ([]Guild dtos = append(dtos, GuildDto{ Id: guild.Id, Name: guild.Name, - Icon: guild.Icon, + Icon: guild.IconUrl(), PermissionLevel: permLevel, }) mu.Unlock() @@ -106,7 +106,7 @@ func storeGuildsInDb(ctx context.Context, userId uint64, guilds []guild.Guild) e Name: guild.Name, Owner: guild.Owner, UserPermissions: guild.Permissions, - Icon: guild.Icon, + Icon: guild.IconUrl(), }) } diff --git a/utils/types/emoji.go b/utils/types/emoji.go index 8be1e734..11fe3af2 100644 --- a/utils/types/emoji.go +++ b/utils/types/emoji.go @@ -32,16 +32,18 @@ func NewEmoji(emojiName *string, emojiId *uint64) Emoji { func (e Emoji) IntoGdl() *emoji.Emoji { if e.IsCustomEmoji { + name := e.Name return &emoji.Emoji{ Id: objects.NewNullableSnowflake(*e.Id), - Name: e.Name, + Name: &name, } } else { if e.Name == "" { return nil } else { + name := e.Name return &emoji.Emoji{ - Name: e.Name, + Name: &name, } } } From 840224a1e61c51c9618cb68102aeb9d426ff3c87 Mon Sep 17 00:00:00 2001 From: biast12 Date: Sun, 26 Apr 2026 16:19:18 +0200 Subject: [PATCH 2/5] Allow announcement channels for transcripts Update transcript channel validation to accept ChannelTypeGuildAnnouncement (announcement channels) instead of ChannelTypeGuildNews. Also enable a local replace for github.com/TicketsBot-cloud/database (../database) and bump/update several TicketsBot-cloud module versions and their checksums in go.mod/go.sum. --- app/http/endpoints/api/panel/validation.go | 2 +- go.mod | 8 ++++---- go.sum | 6 ++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/http/endpoints/api/panel/validation.go b/app/http/endpoints/api/panel/validation.go index fd841830..7a963bc5 100644 --- a/app/http/endpoints/api/panel/validation.go +++ b/app/http/endpoints/api/panel/validation.go @@ -447,7 +447,7 @@ func validateTranscriptChannelId(ctx PanelValidationContext) validation.Validati for _, ch := range ctx.Channels { if ch.Id == *ctx.Data.TranscriptChannelId { - if ch.Type != channel.ChannelTypeGuildText && ch.Type != channel.ChannelTypeGuildNews { + if ch.Type != channel.ChannelTypeGuildText && ch.Type != channel.ChannelTypeGuildAnnouncement { return validation.NewInvalidInputError("Transcript channel must be a text channel") } return nil diff --git a/go.mod b/go.mod index 838e5fdf..2fd64d86 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/TicketsBot-cloud/dashboard go 1.25.0 -//replace github.com/TicketsBot-cloud/database => ../database +replace github.com/TicketsBot-cloud/database => ../database replace github.com/TicketsBot-cloud/gdl => ../gdl @@ -17,9 +17,9 @@ replace github.com/go-playground/validator/v10 => github.com/go-playground/valid require ( github.com/BurntSushi/toml v1.2.1 github.com/TicketsBot-cloud/archiverclient v0.0.0-20251015181023-f0b66a074704 - github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e - github.com/TicketsBot-cloud/database v0.0.0-20260226214338-bf6af6ba954e - github.com/TicketsBot-cloud/gdl v0.0.0-20260213180045-11af01c262ca + github.com/TicketsBot-cloud/common v0.0.0-20260412182419-83b9a6ea08e7 + github.com/TicketsBot-cloud/database v0.0.0-20260423165031-495c2e8a5bc7 + github.com/TicketsBot-cloud/gdl v0.0.0-20260306134952-cccb0116fef6 github.com/TicketsBot-cloud/logarchiver v0.0.0-20251018211319-7a7df5cacbdc github.com/TicketsBot-cloud/worker v0.0.0-20260423165809-3a23e8fb9fc3 github.com/apex/log v1.1.2 diff --git a/go.sum b/go.sum index bc8b9b06..1e1d5fae 100644 --- a/go.sum +++ b/go.sum @@ -45,10 +45,8 @@ github.com/TicketsBot-cloud/analytics-client v0.0.0-20250604180646-6606dfc8fc8c github.com/TicketsBot-cloud/analytics-client v0.0.0-20250604180646-6606dfc8fc8c/go.mod h1:zecIz09jVDSHyhV6NYgTko0NEN0QJGiZbzcxHRjQLzc= github.com/TicketsBot-cloud/archiverclient v0.0.0-20251015181023-f0b66a074704 h1:liLfvCrzoJ89DXFHzsd1iK3cyP8s4i0CnZPRFEj53zg= github.com/TicketsBot-cloud/archiverclient v0.0.0-20251015181023-f0b66a074704/go.mod h1:Mux1bEPpOHwRw1wo6Fa6qJLJH9Erk9qv1yAIfLi1Wmw= -github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e h1:nFKV7yEm8MWbCP7dtsJ88+agcxDUD0YKIotVHMVvytw= -github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e/go.mod h1:tGrTHFz09OM3eDWF+62hIi9ELpT4igCFi868FKSvKBg= -github.com/TicketsBot-cloud/database v0.0.0-20260226214338-bf6af6ba954e h1:BWHHGDDxhZmsGl6KP4xXmvrTyvUbO9nCgUAgAunAxEI= -github.com/TicketsBot-cloud/database v0.0.0-20260226214338-bf6af6ba954e/go.mod h1:HQXAgmNSm7/FmBYwcsa6qpZqMrDhbLoEl+AyqFQ+RwY= +github.com/TicketsBot-cloud/common v0.0.0-20260412182419-83b9a6ea08e7 h1:dFmPLk9KXGRVSfiuKK6kusVhRKG7nGfiGld+WRuiX7w= +github.com/TicketsBot-cloud/common v0.0.0-20260412182419-83b9a6ea08e7/go.mod h1:jXGcmAuRvv92YqITskvClgoCpFVqYw14CKJdYhiLtVU= github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1 h1:FqC1KGOsmB+ikvbmDkyNQU6bGUWyfYq8Ip9r4KxTveY= github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1/go.mod h1:N7zwetwx8B3RK/ZajWwMroJSyv2ZJ+bIOZWv/z8DhaM= github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261 h1:NHD5GB6cjlkpZFjC76Yli2S63/J2nhr8MuE6KlYJpQM= From c3c99f5767219fee51f8a171fce3fd6512ec4c70 Mon Sep 17 00:00:00 2001 From: biast12 Date: Mon, 27 Apr 2026 19:39:19 +0200 Subject: [PATCH 3/5] Improve context usage, error formatting, and cleanup Standardize and clean up multiple API handlers and middleware: pass cancellable contexts to Redis/wrapper calls to avoid leaks, replace string concatenations/unused fmt.Sprintf with formatted utils.ErrorStr calls, switch some if/else chains to switch for clarity, use strings.EqualFold for domain comparisons, and simplify nil/len checks for slices. Misc fixes: use time.Since for latency, io.ReadAll instead of ioutil, update imports, fix validator goroutine capture, adjust generic default applicator usage, and minor regex/import/type cleanups across many files for consistency and reliability. --- app/http/endpoints/api/channels.go | 4 ++-- app/http/endpoints/api/forms/updateinputs.go | 17 ++++++-------- app/http/endpoints/api/guild.go | 2 +- .../api/integrations/createintegration.go | 4 ++-- .../api/integrations/listintegrations.go | 5 ----- .../api/integrations/updateintegration.go | 2 +- .../endpoints/api/panel/multipanelcreate.go | 4 ++-- .../endpoints/api/panel/multipanelupdate.go | 5 ++--- app/http/endpoints/api/panel/panelcreate.go | 14 ++++++------ app/http/endpoints/api/panel/paneldelete.go | 3 +-- app/http/endpoints/api/panel/panelresend.go | 3 +-- app/http/endpoints/api/panel/panelupdate.go | 22 ++++++++++--------- app/http/endpoints/api/panel/supporthours.go | 21 +++++++++--------- app/http/endpoints/api/panel/validation.go | 6 ++--- app/http/endpoints/api/reloadguilds.go | 7 ++++-- app/http/endpoints/api/tags/tagcreate.go | 6 ++--- app/http/endpoints/api/tags/tagdelete.go | 6 ++--- app/http/endpoints/api/tags/tagslist.go | 3 +-- app/http/endpoints/api/team/addmember.go | 12 +++++----- app/http/endpoints/api/team/create.go | 3 +-- app/http/endpoints/api/team/delete.go | 3 +-- app/http/endpoints/api/team/getmembers.go | 2 +- app/http/endpoints/api/team/getpermissions.go | 3 +-- app/http/endpoints/api/team/getteams.go | 3 +-- app/http/endpoints/api/team/removemember.go | 18 ++++++++------- .../endpoints/api/team/updatepermissions.go | 3 +-- app/http/endpoints/api/ticket/getticket.go | 2 +- .../endpoints/api/ticket/labelassignments.go | 2 +- app/http/endpoints/api/ticket/labels.go | 3 +-- .../endpoints/api/ticket/updateclosereason.go | 5 ++++- app/http/endpoints/api/transcripts/get.go | 7 +++--- app/http/endpoints/api/transcripts/render.go | 8 +------ app/http/endpoints/api/user.go | 5 ++--- .../whitelabelcreateinteractions.go | 6 +++-- app/http/middleware/logging.go | 2 +- app/http/middleware/multireadbody.go | 6 ++--- app/http/middleware/ratelimit.go | 5 ++++- app/http/middleware/verifywhitelabel.go | 8 +++---- app/http/session/redisstore.go | 16 ++++++++++---- app/http/validation/defaults/defaults_test.go | 2 +- app/http/validation/validator.go | 1 - botcontext/botcontext.go | 2 +- botcontext/get.go | 6 ++--- redis/panelcreate.go | 5 ++++- redis/redis.go | 5 ++--- redis/ticketclose.go | 5 ++++- utils/fileutils.go | 4 ++-- utils/guildutils.go | 6 ++--- utils/requestutils.go | 5 ++++- 49 files changed, 147 insertions(+), 150 deletions(-) diff --git a/app/http/endpoints/api/channels.go b/app/http/endpoints/api/channels.go index 5e2308c3..7d07a0d5 100644 --- a/app/http/endpoints/api/channels.go +++ b/app/http/endpoints/api/channels.go @@ -25,7 +25,7 @@ func ChannelsHandler(ctx *gin.Context) { if ctx.Query("refresh") == "true" { hasToken, err := redis.Client.TakeChannelRefreshToken(ctx, guildId) if err != nil { - ctx.JSON(500, utils.ErrorStr("Failed to take channel refresh token for guild %d. Please try again.")) + ctx.JSON(500, utils.ErrorStr("Failed to take channel refresh token for guild %d. Please try again.", guildId)) return } @@ -37,7 +37,7 @@ func ChannelsHandler(ctx *gin.Context) { } if err := cache.Instance.StoreChannels(ctx, channels); err != nil { - ctx.JSON(500, utils.ErrorStr("Failed to store channels in cache for guild %d. Please try again.")) + ctx.JSON(500, utils.ErrorStr("Failed to store channels in cache for guild %d. Please try again.", guildId)) return } } else { diff --git a/app/http/endpoints/api/forms/updateinputs.go b/app/http/endpoints/api/forms/updateinputs.go index 452b536c..4d1f3b87 100644 --- a/app/http/endpoints/api/forms/updateinputs.go +++ b/app/http/endpoints/api/forms/updateinputs.go @@ -77,7 +77,7 @@ func UpdateInputs(c *gin.Context) { } formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors) - c.JSON(400, utils.ErrorStr(formatted)) + c.JSON(400, utils.ErrorStr("%s", formatted)) return } @@ -270,21 +270,22 @@ func validateInputOptions(input inputCreateBody, optionTypes map[int]string) err } // Radio Group (type 21) requires 2-10 options, Checkbox Group (type 22) requires 1-10 options - if input.Type == 21 { + switch input.Type { + case 21: if len(input.Options) < 2 { return fmt.Errorf("Radio group inputs must have at least 2 options") } if len(input.Options) > 10 { return fmt.Errorf("Radio group inputs must have at most 10 options") } - } else if input.Type == 22 { + case 22: if len(input.Options) == 0 { return fmt.Errorf("%s inputs must have at least one option", typeName) } if len(input.Options) > 10 { return fmt.Errorf("Checkbox group inputs must have at most 10 options") } - } else { + default: if len(input.Options) == 0 { return fmt.Errorf("%s inputs must have at least one option", typeName) } @@ -321,9 +322,7 @@ func saveInputs(ctx context.Context, formId int, data updateInputsBody, existing // Handle select types (3, 5-8, 22) if input.Type == 3 || (input.Type >= 5 && input.Type <= 8) || input.Type == 22 { // Enforce min_length constraints (0-25) - if minLength < 0 { - minLength = 0 - } else if minLength > 25 { + if minLength > 25 { minLength = 25 } @@ -426,9 +425,7 @@ func saveInputs(ctx context.Context, formId int, data updateInputsBody, existing // Handle select types (3, 5-8, 22) if input.Type == 3 || (input.Type >= 5 && input.Type <= 8) || input.Type == 22 { // Enforce min_length constraints (0-25) - if minLength < 0 { - minLength = 0 - } else if minLength > 25 { + if minLength > 25 { minLength = 25 } diff --git a/app/http/endpoints/api/guild.go b/app/http/endpoints/api/guild.go index 518e2b2e..69a78893 100644 --- a/app/http/endpoints/api/guild.go +++ b/app/http/endpoints/api/guild.go @@ -17,7 +17,7 @@ func GuildHandler(ctx *gin.Context) { guild, err := botContext.GetGuild(ctx, guildId) if err != nil { - ctx.JSON(500, utils.ErrorStr("Failed to fetch guild information from Discord for guild %d. Please try again.")) + ctx.JSON(500, utils.ErrorStr("Failed to fetch guild information from Discord for guild %d. Please try again.", guildId)) return } diff --git a/app/http/endpoints/api/integrations/createintegration.go b/app/http/endpoints/api/integrations/createintegration.go index 854e7691..56959840 100644 --- a/app/http/endpoints/api/integrations/createintegration.go +++ b/app/http/endpoints/api/integrations/createintegration.go @@ -70,7 +70,7 @@ func CreateIntegrationHandler(ctx *gin.Context) { } formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors) - ctx.JSON(400, utils.ErrorStr(formatted)) + ctx.JSON(400, utils.ErrorStr("%s", formatted)) return } @@ -159,5 +159,5 @@ func isSameValidationUrlHost(webhookUrl, validationUrl string) (bool, error) { return false, errors.New("invalid webhook or validation URL") } - return strings.ToLower(utils.SecondLevelDomain(webhookStripped)) == strings.ToLower(utils.SecondLevelDomain(validationStripped)), nil + return strings.EqualFold(utils.SecondLevelDomain(webhookStripped), utils.SecondLevelDomain(validationStripped)), nil } diff --git a/app/http/endpoints/api/integrations/listintegrations.go b/app/http/endpoints/api/integrations/listintegrations.go index 49487bd3..adbc281e 100644 --- a/app/http/endpoints/api/integrations/listintegrations.go +++ b/app/http/endpoints/api/integrations/listintegrations.go @@ -106,10 +106,5 @@ func ListIntegrationsHandler(ctx *gin.Context) { } } - // Don't serve null - if integrations == nil { - integrations = make([]integrationWithMetadata, 0) - } - ctx.JSON(200, integrations) } diff --git a/app/http/endpoints/api/integrations/updateintegration.go b/app/http/endpoints/api/integrations/updateintegration.go index af0be62a..d6e401c2 100644 --- a/app/http/endpoints/api/integrations/updateintegration.go +++ b/app/http/endpoints/api/integrations/updateintegration.go @@ -70,7 +70,7 @@ func UpdateIntegrationHandler(ctx *gin.Context) { } formatted = strings.TrimSuffix(formatted, "\n") - ctx.JSON(400, utils.ErrorStr(formatted)) + ctx.JSON(400, utils.ErrorStr("%s", formatted)) return } diff --git a/app/http/endpoints/api/panel/multipanelcreate.go b/app/http/endpoints/api/panel/multipanelcreate.go index 05fae64c..a12695e4 100644 --- a/app/http/endpoints/api/panel/multipanelcreate.go +++ b/app/http/endpoints/api/panel/multipanelcreate.go @@ -74,7 +74,7 @@ func MultiPanelCreate(c *gin.Context) { } formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors) - c.JSON(400, utils.ErrorStr(formatted)) + c.JSON(400, utils.ErrorStr("%s", formatted)) return } @@ -99,7 +99,7 @@ func MultiPanelCreate(c *gin.Context) { effectiveLabel := getEffectiveLabelForValidation(panel.ButtonLabel, panelConfig.CustomLabel) if effectiveLabel == "" { - c.JSON(400, utils.ErrorStr(fmt.Sprintf("Panel '%s' must have a label when using dropdown mode. Please add a custom label or ensure the panel has a button label.", panel.Title))) + c.JSON(400, utils.ErrorStr("Panel '%s' must have a label when using dropdown mode. Please add a custom label or ensure the panel has a button label.", panel.Title)) return } } diff --git a/app/http/endpoints/api/panel/multipanelupdate.go b/app/http/endpoints/api/panel/multipanelupdate.go index 4c4dd6fa..9df5c2ef 100644 --- a/app/http/endpoints/api/panel/multipanelupdate.go +++ b/app/http/endpoints/api/panel/multipanelupdate.go @@ -3,7 +3,6 @@ package api import ( "context" "errors" - "fmt" "net/http" "strconv" @@ -67,7 +66,7 @@ func MultiPanelUpdate(c *gin.Context) { } formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors) - c.JSON(400, utils.ErrorStr(formatted)) + c.JSON(400, utils.ErrorStr("%s", formatted)) return } @@ -97,7 +96,7 @@ func MultiPanelUpdate(c *gin.Context) { } if effectiveLabel == "" { - c.JSON(400, utils.ErrorStr(fmt.Sprintf("Panel '%s' must have a label when using dropdown mode. Please add a custom label or ensure the panel has a button label.", panel.Title))) + c.JSON(400, utils.ErrorStr("Panel '%s' must have a label when using dropdown mode. Please add a custom label or ensure the panel has a button label.", panel.Title)) return } } diff --git a/app/http/endpoints/api/panel/panelcreate.go b/app/http/endpoints/api/panel/panelcreate.go index 0588a890..7344a6a9 100644 --- a/app/http/endpoints/api/panel/panelcreate.go +++ b/app/http/endpoints/api/panel/panelcreate.go @@ -149,7 +149,7 @@ func CreatePanel(c *gin.Context) { if err := ValidatePanelBody(validationContext); err != nil { var validationError *validation.InvalidInputError if errors.As(err, &validationError) { - c.JSON(400, utils.ErrorStr(validationError.Error())) + c.JSON(400, utils.ErrorStr("%s", validationError.Error())) } else { _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Panel validation failed unexpectedly")) } @@ -170,7 +170,7 @@ func CreatePanel(c *gin.Context) { } formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors) - c.JSON(400, utils.ErrorStr(formatted)) + c.JSON(400, utils.ErrorStr("%s", formatted)) return } @@ -275,13 +275,13 @@ func CreatePanel(c *gin.Context) { // string is role ID or "user" to mention the ticket opener or "here" to mention @here validRoles := utils.ToSet(utils.Map(roles, utils.RoleToId)) - var roleMentions []uint64 for _, mention := range data.Mentions { - if mention == "user" { + switch mention { + case "user": createOptions.ShouldMentionUser = true - } else if mention == "here" { + case "here": createOptions.ShouldMentionHere = true - } else { + default: roleId, err := strconv.ParseUint(mention, 10, 64) if err != nil { c.JSON(400, utils.ErrorStr("Invalid role ID in mentions: %s", mention)) @@ -289,7 +289,7 @@ func CreatePanel(c *gin.Context) { } if validRoles.Contains(roleId) { - createOptions.RoleMentions = append(roleMentions, roleId) + createOptions.RoleMentions = append(createOptions.RoleMentions, roleId) } } } diff --git a/app/http/endpoints/api/panel/paneldelete.go b/app/http/endpoints/api/panel/paneldelete.go index 773a13c6..158588ff 100644 --- a/app/http/endpoints/api/panel/paneldelete.go +++ b/app/http/endpoints/api/panel/paneldelete.go @@ -2,7 +2,6 @@ package api import ( "errors" - "fmt" "net/http" "strconv" @@ -42,7 +41,7 @@ func DeletePanel(c *gin.Context) { } if panel.PanelId == 0 { - c.JSON(404, utils.ErrorStr(fmt.Sprintf("Panel not found: %d", panelId))) + c.JSON(404, utils.ErrorStr("Panel not found: %d", panelId)) return } diff --git a/app/http/endpoints/api/panel/panelresend.go b/app/http/endpoints/api/panel/panelresend.go index b54f1c7d..370d6b4f 100644 --- a/app/http/endpoints/api/panel/panelresend.go +++ b/app/http/endpoints/api/panel/panelresend.go @@ -3,7 +3,6 @@ package api import ( "context" "errors" - "fmt" "strconv" "github.com/TicketsBot-cloud/common/premium" @@ -42,7 +41,7 @@ func ResendPanel(ctx *gin.Context) { } if panel.PanelId == 0 { - ctx.JSON(404, utils.ErrorStr(fmt.Sprintf("Panel not found: %d", panelId))) + ctx.JSON(404, utils.ErrorStr("Panel not found: %d", panelId)) return } diff --git a/app/http/endpoints/api/panel/panelupdate.go b/app/http/endpoints/api/panel/panelupdate.go index fa700eff..2aa05af6 100644 --- a/app/http/endpoints/api/panel/panelupdate.go +++ b/app/http/endpoints/api/panel/panelupdate.go @@ -100,7 +100,7 @@ func UpdatePanel(c *gin.Context) { if err := ValidatePanelBody(validationContext); err != nil { var validationError *validation.InvalidInputError if errors.As(err, &validationError) { - c.JSON(400, utils.ErrorStr(validationError.Error())) + c.JSON(400, utils.ErrorStr("%s", validationError.Error())) } else { _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to update panel")) } @@ -121,7 +121,7 @@ func UpdatePanel(c *gin.Context) { } formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors) - c.JSON(400, utils.ErrorStr(formatted)) + c.JSON(400, utils.ErrorStr("%s", formatted)) return } @@ -148,13 +148,14 @@ func UpdatePanel(c *gin.Context) { if err != nil { var unwrapped request.RestError if errors.As(err, &unwrapped) { - if unwrapped.StatusCode == 403 { + switch unwrapped.StatusCode { + case 403: c.JSON(403, utils.ErrorStr("I do not have permission to send messages in the specified channel")) return - } else if unwrapped.StatusCode == 404 { + case 404: // Swallow error // TODO: Make channel_id column nullable, and set to null - } else { + default: _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to update panel")) return } @@ -278,11 +279,12 @@ func UpdatePanel(c *gin.Context) { var shouldMentionHere bool var roleMentions []uint64 for _, mention := range data.Mentions { - if mention == "user" { + switch mention { + case "user": shouldMentionUser = true - } else if mention == "here" { + case "here": shouldMentionHere = true - } else { + default: roleId, err := strconv.ParseUint(mention, 10, 64) if err != nil { _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to update panel")) @@ -374,7 +376,7 @@ func UpdatePanel(c *gin.Context) { } else { log.Logger.Error("Body", zap.Any("body", messageData)) log.Logger.Error("Error sending panel message", zap.Any("errs", unwrapped2.ApiError.Errors)) - c.JSON(400, utils.ErrorStr("Error sending panel message: "+unwrapped2.ApiError.Message)) + c.JSON(400, utils.ErrorStr("Error sending panel message: %s", unwrapped2.ApiError.Message)) } } else { _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to update panel")) @@ -389,7 +391,7 @@ func UpdatePanel(c *gin.Context) { log.Logger.Error("Body", zap.Any("body", messageData)) if errors.As(err, &unwrapped) { log.Logger.Error("Error editing panel message", zap.Any("errs", unwrapped.ApiError.Errors)) - c.JSON(400, utils.ErrorStr("Error editing panel message: "+unwrapped.ApiError.Message)) + c.JSON(400, utils.ErrorStr("Error editing panel message: %s", unwrapped.ApiError.Message)) } else { _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to update panel")) } diff --git a/app/http/endpoints/api/panel/supporthours.go b/app/http/endpoints/api/panel/supporthours.go index f4bea5ba..1d32b2a3 100644 --- a/app/http/endpoints/api/panel/supporthours.go +++ b/app/http/endpoints/api/panel/supporthours.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "net/http" "strconv" "time" @@ -47,7 +46,7 @@ func GetSupportHours(c *gin.Context) { panelIdStr := c.Param("panelid") panelId, err := strconv.Atoi(panelIdStr) if err != nil { - c.JSON(http.StatusBadRequest, utils.ErrorStr(fmt.Sprintf("Invalid panel ID provided: %s", c.Param("panelId")))) + c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid panel ID provided: %s", panelIdStr)) return } @@ -59,7 +58,7 @@ func GetSupportHours(c *gin.Context) { } if panel.GuildId != guildId { - c.JSON(http.StatusNotFound, utils.ErrorStr(fmt.Sprintf("Panel not found: %d", panelId))) + c.JSON(http.StatusNotFound, utils.ErrorStr("Panel not found: %d", panelId)) return } @@ -80,7 +79,7 @@ func GetSupportHours(c *gin.Context) { var timezone string = "Europe/London" var hourConfigs []supportHoursHourConfig - if hours != nil && len(hours) > 0 { + if len(hours) > 0 { timezone = hours[0].Timezone for _, h := range hours { hourConfigs = append(hourConfigs, supportHoursHourConfig{ @@ -144,7 +143,7 @@ func SetSupportHours(c *gin.Context) { panelIdStr := c.Param("panelid") panelId, err := strconv.Atoi(panelIdStr) if err != nil { - c.JSON(http.StatusBadRequest, utils.ErrorStr(fmt.Sprintf("Invalid panel ID provided: %s", c.Param("panelId")))) + c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid panel ID provided: %s", panelIdStr)) return } @@ -201,7 +200,7 @@ func SetSupportHours(c *gin.Context) { } if panel.GuildId != guildId { - c.JSON(http.StatusNotFound, utils.ErrorStr(fmt.Sprintf("Panel not found: %d", panelId))) + c.JSON(http.StatusNotFound, utils.ErrorStr("Panel not found: %d", panelId)) return } @@ -213,7 +212,7 @@ func SetSupportHours(c *gin.Context) { // Validate timezone if !database.IsValidTimezone(requestBody.Timezone) { - c.JSON(http.StatusBadRequest, utils.ErrorStr(fmt.Sprintf("Invalid timezone: %s", requestBody.Timezone))) + c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid timezone: %s", requestBody.Timezone)) return } @@ -361,7 +360,7 @@ func DeleteSupportHours(c *gin.Context) { panelIdStr := c.Param("panelid") panelId, err := strconv.Atoi(panelIdStr) if err != nil { - c.JSON(http.StatusBadRequest, utils.ErrorStr(fmt.Sprintf("Invalid panel ID provided: %s", c.Param("panelId")))) + c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid panel ID provided: %s", panelIdStr)) return } @@ -373,7 +372,7 @@ func DeleteSupportHours(c *gin.Context) { } if panel.GuildId != guildId { - c.JSON(http.StatusNotFound, utils.ErrorStr(fmt.Sprintf("Panel not found: %d", panelId))) + c.JSON(http.StatusNotFound, utils.ErrorStr("Panel not found: %d", panelId)) return } @@ -423,7 +422,7 @@ func IsPanelActive(c *gin.Context) { panelIdStr := c.Param("panelid") panelId, err := strconv.Atoi(panelIdStr) if err != nil { - c.JSON(http.StatusBadRequest, utils.ErrorStr(fmt.Sprintf("Invalid panel ID provided: %s", c.Param("panelId")))) + c.JSON(http.StatusBadRequest, utils.ErrorStr("Invalid panel ID provided: %s", c.Param("panelId"))) return } @@ -435,7 +434,7 @@ func IsPanelActive(c *gin.Context) { } if panel.GuildId != guildId { - c.JSON(http.StatusNotFound, utils.ErrorStr(fmt.Sprintf("Panel not found: %d", panelId))) + c.JSON(http.StatusNotFound, utils.ErrorStr("Panel not found: %d", panelId)) return } diff --git a/app/http/endpoints/api/panel/validation.go b/app/http/endpoints/api/panel/validation.go index 7a963bc5..27775cb7 100644 --- a/app/http/endpoints/api/panel/validation.go +++ b/app/http/endpoints/api/panel/validation.go @@ -34,9 +34,9 @@ func DefaultApplicators(data *panelBody) []defaults.DefaultApplicator { return []defaults.DefaultApplicator{ defaults.NewDefaultApplicator(defaults.EmptyStringCheck, &data.Title, "Open a ticket!"), defaults.NewDefaultApplicator(defaults.EmptyStringCheck, &data.Content, "By clicking the button, a ticket will be opened for you."), - defaults.NewDefaultApplicator[*string](defaults.NilOrEmptyStringCheck, &data.ImageUrl, nil), - defaults.NewDefaultApplicator[*string](defaults.NilOrEmptyStringCheck, &data.ThumbnailUrl, nil), - defaults.NewDefaultApplicator[*string](defaults.NilOrEmptyStringCheck, &data.NamingScheme, nil), + defaults.NewDefaultApplicator(defaults.NilOrEmptyStringCheck, &data.ImageUrl, nil), + defaults.NewDefaultApplicator(defaults.NilOrEmptyStringCheck, &data.ThumbnailUrl, nil), + defaults.NewDefaultApplicator(defaults.NilOrEmptyStringCheck, &data.NamingScheme, nil), } } diff --git a/app/http/endpoints/api/reloadguilds.go b/app/http/endpoints/api/reloadguilds.go index 13318b38..9df5aa69 100644 --- a/app/http/endpoints/api/reloadguilds.go +++ b/app/http/endpoints/api/reloadguilds.go @@ -21,14 +21,17 @@ func ReloadGuildsHandler(c *gin.Context) { userId := c.Keys["userid"].(uint64) key := fmt.Sprintf("tickets:dashboard:guildreload:%d", userId) - res, err := redis.Client.SetNX(wrapper.DefaultContext(), key, 1, time.Second*10).Result() + ctx, cancel := wrapper.DefaultContext() + defer cancel() + + res, err := redis.Client.SetNX(ctx, key, 1, time.Second*10).Result() if err != nil { _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to check rate limit")) return } if !res { - ttl, err := redis.Client.TTL(wrapper.DefaultContext(), key).Result() + ttl, err := redis.Client.TTL(ctx, key).Result() if err != nil { c.JSON(500, utils.ErrorStr("Failed to process request. Please try again.")) return diff --git a/app/http/endpoints/api/tags/tagcreate.go b/app/http/endpoints/api/tags/tagcreate.go index b6be9afb..ed43a9f9 100644 --- a/app/http/endpoints/api/tags/tagcreate.go +++ b/app/http/endpoints/api/tags/tagcreate.go @@ -40,7 +40,7 @@ func CreateTag(ctx *gin.Context) { // Max of 200 tags count, err := dbclient.Client.Tag.GetTagCount(ctx, guildId) if err != nil { - ctx.JSON(500, utils.ErrorStr(fmt.Sprintf("Failed to fetch tag from database: %v", err))) + ctx.JSON(500, utils.ErrorStr("Failed to fetch tag from database: %v", err)) return } @@ -75,7 +75,7 @@ func CreateTag(ctx *gin.Context) { } formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors) - ctx.JSON(400, utils.ErrorStr(formatted)) + ctx.JSON(400, utils.ErrorStr("%s", formatted)) return } @@ -93,7 +93,7 @@ func CreateTag(ctx *gin.Context) { if data.Embed != nil { totalChars := data.Embed.TotalCharacterCount() if totalChars > 6000 { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Total embed characters (%d) exceeds Discord's 6000 character limit", totalChars))) + ctx.JSON(400, utils.ErrorStr("Total embed characters (%d) exceeds Discord's 6000 character limit", totalChars)) return } } diff --git a/app/http/endpoints/api/tags/tagdelete.go b/app/http/endpoints/api/tags/tagdelete.go index a5849f19..9ca8b466 100644 --- a/app/http/endpoints/api/tags/tagdelete.go +++ b/app/http/endpoints/api/tags/tagdelete.go @@ -1,8 +1,6 @@ package api import ( - "fmt" - "github.com/TicketsBot-cloud/dashboard/app/http/audit" "github.com/TicketsBot-cloud/dashboard/botcontext" "github.com/TicketsBot-cloud/dashboard/database" @@ -34,12 +32,12 @@ func DeleteTag(ctx *gin.Context) { // Fetch tag to see if we need to delete a guild command tag, exists, err := database.Client.Tag.Get(ctx, guildId, body.TagId) if err != nil { - ctx.JSON(500, utils.ErrorStr(fmt.Sprintf("Failed to fetch tag from database: %v", err))) + ctx.JSON(500, utils.ErrorStr("Failed to fetch tag from database: %v", err)) return } if !exists { - ctx.JSON(404, utils.ErrorStr(fmt.Sprintf("Tag not found: %s", body.TagId))) + ctx.JSON(404, utils.ErrorStr("Tag not found: %s", body.TagId)) return } diff --git a/app/http/endpoints/api/tags/tagslist.go b/app/http/endpoints/api/tags/tagslist.go index 50921fc4..f4abdc99 100644 --- a/app/http/endpoints/api/tags/tagslist.go +++ b/app/http/endpoints/api/tags/tagslist.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "github.com/TicketsBot-cloud/dashboard/database" "github.com/TicketsBot-cloud/dashboard/utils" "github.com/TicketsBot-cloud/dashboard/utils/types" @@ -13,7 +12,7 @@ func TagsListHandler(ctx *gin.Context) { tags, err := database.Client.Tag.GetByGuild(ctx, guildId) if err != nil { - ctx.JSON(500, utils.ErrorStr(fmt.Sprintf("Failed to fetch tag from database: %v", err))) + ctx.JSON(500, utils.ErrorStr("Failed to fetch tag from database: %v", err)) return } diff --git a/app/http/endpoints/api/team/addmember.go b/app/http/endpoints/api/team/addmember.go index 1d999f9c..4e6b9038 100644 --- a/app/http/endpoints/api/team/addmember.go +++ b/app/http/endpoints/api/team/addmember.go @@ -7,7 +7,7 @@ import ( "github.com/TicketsBot-cloud/dashboard/app/http/audit" dbclient "github.com/TicketsBot-cloud/dashboard/database" "github.com/TicketsBot-cloud/dashboard/utils" - dbmodel "github.com/TicketsBot-cloud/database" + "github.com/TicketsBot-cloud/database" "github.com/gin-gonic/gin" ) @@ -50,7 +50,7 @@ func AddMember(ctx *gin.Context) { } else { parsed, err := strconv.Atoi(teamId) if err != nil { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Invalid team ID provided: %s", ctx.Param("id")))) + ctx.JSON(400, utils.ErrorStr("Invalid team ID provided: %s", ctx.Param("id"))) return } @@ -75,8 +75,8 @@ func addDefaultMember(ctx *gin.Context, guildId, userId, snowflake uint64, entit audit.Log(audit.LogEntry{ GuildId: audit.Uint64Ptr(guildId), UserId: userId, - ActionType: dbmodel.AuditActionTeamMemberAdd, - ResourceType: dbmodel.AuditResourceTeamMember, + ActionType: database.AuditActionTeamMemberAdd, + ResourceType: database.AuditResourceTeamMember, ResourceId: audit.StringPtr(fmt.Sprintf("default/%d", snowflake)), NewData: map[string]interface{}{"snowflake": snowflake, "type": typeParsed}, }) @@ -110,8 +110,8 @@ func addTeamMember(ctx *gin.Context, teamId int, guildId, userId, snowflake uint audit.Log(audit.LogEntry{ GuildId: audit.Uint64Ptr(guildId), UserId: userId, - ActionType: dbmodel.AuditActionTeamMemberAdd, - ResourceType: dbmodel.AuditResourceTeamMember, + ActionType: database.AuditActionTeamMemberAdd, + ResourceType: database.AuditResourceTeamMember, ResourceId: audit.StringPtr(fmt.Sprintf("%d/%d", teamId, snowflake)), NewData: map[string]interface{}{"team_id": teamId, "snowflake": snowflake, "type": typeParsed}, }) diff --git a/app/http/endpoints/api/team/create.go b/app/http/endpoints/api/team/create.go index 3ac23ffd..056198fc 100644 --- a/app/http/endpoints/api/team/create.go +++ b/app/http/endpoints/api/team/create.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "strconv" "github.com/TicketsBot-cloud/dashboard/app/http/audit" @@ -32,7 +31,7 @@ func CreateTeam(ctx *gin.Context) { _, exists, err := dbclient.Client.SupportTeam.GetByName(ctx, guildId, data.Name) if err != nil { - ctx.JSON(500, utils.ErrorStr(fmt.Sprintf("Failed to fetch team from database: %v", err))) + ctx.JSON(500, utils.ErrorStr("Failed to fetch team from database: %v", err)) return } diff --git a/app/http/endpoints/api/team/delete.go b/app/http/endpoints/api/team/delete.go index 3c7c9896..cf586208 100644 --- a/app/http/endpoints/api/team/delete.go +++ b/app/http/endpoints/api/team/delete.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "strconv" "github.com/TicketsBot-cloud/dashboard/app/http/audit" @@ -29,7 +28,7 @@ func DeleteTeam(ctx *gin.Context) { } if !exists { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Team not found: %d", teamId))) + ctx.JSON(400, utils.ErrorStr("Team not found: %d", teamId)) return } diff --git a/app/http/endpoints/api/team/getmembers.go b/app/http/endpoints/api/team/getmembers.go index e03171c5..c9fff592 100644 --- a/app/http/endpoints/api/team/getmembers.go +++ b/app/http/endpoints/api/team/getmembers.go @@ -24,7 +24,7 @@ func GetMembers(ctx *gin.Context) { } else { parsed, err := strconv.Atoi(teamId) if err != nil { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Invalid team ID provided: %s", ctx.Param("id")))) + ctx.JSON(400, utils.ErrorStr("Invalid team ID provided: %s", ctx.Param("id"))) return } diff --git a/app/http/endpoints/api/team/getpermissions.go b/app/http/endpoints/api/team/getpermissions.go index 8312ed54..e1ea9382 100644 --- a/app/http/endpoints/api/team/getpermissions.go +++ b/app/http/endpoints/api/team/getpermissions.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "strconv" dbclient "github.com/TicketsBot-cloud/dashboard/database" @@ -20,7 +19,7 @@ func GetTeamPermissions(ctx *gin.Context) { teamId, err := strconv.Atoi(teamIdParam) if err != nil { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Invalid team ID provided: %s", teamIdParam))) + ctx.JSON(400, utils.ErrorStr("Invalid team ID provided: %s", teamIdParam)) return } diff --git a/app/http/endpoints/api/team/getteams.go b/app/http/endpoints/api/team/getteams.go index bf444427..b1abb88c 100644 --- a/app/http/endpoints/api/team/getteams.go +++ b/app/http/endpoints/api/team/getteams.go @@ -1,7 +1,6 @@ package api import ( - "fmt" dbclient "github.com/TicketsBot-cloud/dashboard/database" "github.com/TicketsBot-cloud/dashboard/utils" "github.com/TicketsBot-cloud/database" @@ -13,7 +12,7 @@ func GetTeams(ctx *gin.Context) { teams, err := dbclient.Client.SupportTeam.Get(ctx, guildId) if err != nil { - ctx.JSON(500, utils.ErrorStr(fmt.Sprintf("Failed to fetch team from database: %v", err))) + ctx.JSON(500, utils.ErrorStr("Failed to fetch team from database: %v", err)) return } diff --git a/app/http/endpoints/api/team/removemember.go b/app/http/endpoints/api/team/removemember.go index 62172fb7..21f182be 100644 --- a/app/http/endpoints/api/team/removemember.go +++ b/app/http/endpoints/api/team/removemember.go @@ -44,7 +44,7 @@ func RemoveMember(ctx *gin.Context) { } else { parsed, err := strconv.Atoi(teamId) if err != nil { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Invalid team ID provided: %s", ctx.Param("id")))) + ctx.JSON(400, utils.ErrorStr("Invalid team ID provided: %s", ctx.Param("id"))) return } @@ -115,7 +115,8 @@ func removeDefaultMember(ctx *gin.Context, guildId, selfId, snowflake uint64, en return } - if entityType == entityTypeUser { + switch entityType { + case entityTypeUser: // If the member is not in the guild we do not have to worry // TODO: Use proper context member, err := botContext.GetGuildMember(context.Background(), guildId, snowflake) @@ -131,7 +132,7 @@ func removeDefaultMember(ctx *gin.Context, guildId, selfId, snowflake uint64, en return } } - } else if entityType == entityTypeRole { + case entityTypeRole: // Recreate role if err := dbclient.Client.GuildMetadata.SetOnCallRole(ctx, guildId, nil); err != nil { ctx.JSON(500, utils.ErrorStr("Failed to delete team. Please try again.")) @@ -148,7 +149,7 @@ func removeDefaultMember(ctx *gin.Context, guildId, selfId, snowflake uint64, en ctx.JSON(500, utils.ErrorStr("Failed to delete team. Please try again.")) return } - } else { + default: ctx.JSON(500, utils.ErrorStr("Infallible")) return } @@ -168,7 +169,7 @@ func removeDefaultMember(ctx *gin.Context, guildId, selfId, snowflake uint64, en func removeTeamMember(ctx *gin.Context, teamId int, guildId, selfId, snowflake uint64, entityType entityType) { team, exists, err := dbclient.Client.SupportTeam.GetById(ctx, guildId, teamId) if err != nil { - ctx.JSON(500, utils.ErrorStr(fmt.Sprintf("Failed to fetch team from database: %v", err))) + ctx.JSON(500, utils.ErrorStr("Failed to fetch team from database: %v", err)) return } @@ -198,7 +199,8 @@ func removeTeamMember(ctx *gin.Context, teamId int, guildId, selfId, snowflake u return } - if entityType == entityTypeUser { + switch entityType { + case entityTypeUser: // If the member is not in the guild we do not have to worry // TODO: Use proper context member, err := botContext.GetGuildMember(context.Background(), guildId, snowflake) @@ -218,7 +220,7 @@ func removeTeamMember(ctx *gin.Context, teamId int, guildId, selfId, snowflake u // TODO: Use proper context _ = botContext.RemoveGuildMemberRole(context.Background(), guildId, snowflake, *team.OnCallRole) - } else if entityType == entityTypeRole { + case entityTypeRole: // Recreate role if err := dbclient.Client.SupportTeam.SetOnCallRole(ctx, teamId, nil); err != nil { ctx.JSON(500, utils.ErrorStr("Failed to delete team. Please try again.")) @@ -235,7 +237,7 @@ func removeTeamMember(ctx *gin.Context, teamId int, guildId, selfId, snowflake u ctx.JSON(500, utils.ErrorStr("Failed to delete team. Please try again.")) return } - } else { + default: ctx.JSON(500, utils.ErrorStr("Infallible")) } } diff --git a/app/http/endpoints/api/team/updatepermissions.go b/app/http/endpoints/api/team/updatepermissions.go index f3032891..95339c79 100644 --- a/app/http/endpoints/api/team/updatepermissions.go +++ b/app/http/endpoints/api/team/updatepermissions.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "strconv" "github.com/TicketsBot-cloud/dashboard/app/http/audit" @@ -23,7 +22,7 @@ func UpdateTeamPermissions(ctx *gin.Context) { teamId, err := strconv.Atoi(teamIdParam) if err != nil { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Invalid team ID provided: %s", teamIdParam))) + ctx.JSON(400, utils.ErrorStr("Invalid team ID provided: %s", teamIdParam)) return } diff --git a/app/http/endpoints/api/ticket/getticket.go b/app/http/endpoints/api/ticket/getticket.go index c8bd2775..d34725cb 100644 --- a/app/http/endpoints/api/ticket/getticket.go +++ b/app/http/endpoints/api/ticket/getticket.go @@ -21,7 +21,7 @@ import ( "github.com/gin-gonic/gin" ) -var MentionRegex, _ = regexp.Compile("<@(\\d+)>") +var MentionRegex, _ = regexp.Compile(`<@(\d+)>`) func GetTicket(c *gin.Context) { guildId := c.Keys["guildid"].(uint64) diff --git a/app/http/endpoints/api/ticket/labelassignments.go b/app/http/endpoints/api/ticket/labelassignments.go index 1bea016a..9c6e8733 100644 --- a/app/http/endpoints/api/ticket/labelassignments.go +++ b/app/http/endpoints/api/ticket/labelassignments.go @@ -76,7 +76,7 @@ func SetTicketLabels(ctx *gin.Context) { for _, id := range body.LabelIds { if !validIds[id] { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Label ID %d does not exist.", id))) + ctx.JSON(400, utils.ErrorStr("Label ID %d does not exist.", id)) return } } diff --git a/app/http/endpoints/api/ticket/labels.go b/app/http/endpoints/api/ticket/labels.go index 3061b32e..b66806c0 100644 --- a/app/http/endpoints/api/ticket/labels.go +++ b/app/http/endpoints/api/ticket/labels.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "strconv" "github.com/TicketsBot-cloud/dashboard/app/http/audit" @@ -72,7 +71,7 @@ func CreateTicketLabel(ctx *gin.Context) { } if count >= maxLabelsPerGuild { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("You can only have up to %d labels per server.", maxLabelsPerGuild))) + ctx.JSON(400, utils.ErrorStr("You can only have up to %d labels per server.", maxLabelsPerGuild)) return } diff --git a/app/http/endpoints/api/ticket/updateclosereason.go b/app/http/endpoints/api/ticket/updateclosereason.go index 31b21280..11c775d4 100644 --- a/app/http/endpoints/api/ticket/updateclosereason.go +++ b/app/http/endpoints/api/ticket/updateclosereason.go @@ -88,7 +88,10 @@ func UpdateCloseReason(c *gin.Context) { // Notify worker to update the archive channel message if payload, err := json.Marshal(closeReasonUpdatePayload{GuildId: guildId, TicketId: ticketId}); err == nil { - redis.Client.Publish(redis.DefaultContext(), "tickets:close_reason_update", payload) + ctx, cancel := redis.DefaultContext() + defer cancel() + + redis.Client.Publish(ctx, "tickets:close_reason_update", payload) } audit.Log(audit.LogEntry{ diff --git a/app/http/endpoints/api/transcripts/get.go b/app/http/endpoints/api/transcripts/get.go index ba7671eb..dd475539 100644 --- a/app/http/endpoints/api/transcripts/get.go +++ b/app/http/endpoints/api/transcripts/get.go @@ -1,13 +1,12 @@ package api import ( - "fmt" "context" "errors" "strconv" "github.com/TicketsBot-cloud/archiverclient" - dbclient "github.com/TicketsBot-cloud/dashboard/database" + "github.com/TicketsBot-cloud/dashboard/database" "github.com/TicketsBot-cloud/dashboard/utils" "github.com/gin-gonic/gin" ) @@ -19,12 +18,12 @@ func GetTranscriptHandler(ctx *gin.Context) { // format ticket ID ticketId, err := strconv.Atoi(ctx.Param("ticketId")) if err != nil { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Invalid ticket ID provided: %s", ctx.Param("ticketId")))) + ctx.JSON(400, utils.ErrorStr("Invalid ticket ID provided: %s", ctx.Param("ticketId"))) return } // get ticket object - ticket, err := dbclient.Client.Tickets.Get(ctx, ticketId, guildId) + ticket, err := database.Client.Tickets.Get(ctx, ticketId, guildId) if err != nil { ctx.JSON(500, utils.ErrorStr("Unable to load ticket. Please try again.")) return diff --git a/app/http/endpoints/api/transcripts/render.go b/app/http/endpoints/api/transcripts/render.go index 22440630..923b3c35 100644 --- a/app/http/endpoints/api/transcripts/render.go +++ b/app/http/endpoints/api/transcripts/render.go @@ -1,7 +1,6 @@ package api import ( - "fmt" "errors" "strconv" @@ -19,7 +18,7 @@ func GetTranscriptRenderHandler(ctx *gin.Context) { // format ticket ID ticketId, err := strconv.Atoi(ctx.Param("ticketId")) if err != nil { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Invalid ticket ID provided: %s", ctx.Param("ticketId")))) + ctx.JSON(400, utils.ErrorStr("Invalid ticket ID provided: %s", ctx.Param("ticketId"))) return } @@ -65,11 +64,6 @@ func GetTranscriptRenderHandler(ctx *gin.Context) { // Render payload := chatreplica.FromTranscript(transcript, ticketId) - // html, err := chatreplica.Render(payload) - if err != nil { - ctx.JSON(500, utils.ErrorStr("Failed to process request. Please try again.")) - return - } ctx.JSON(200, payload) } diff --git a/app/http/endpoints/api/user.go b/app/http/endpoints/api/user.go index caa8fb78..b6e772a6 100644 --- a/app/http/endpoints/api/user.go +++ b/app/http/endpoints/api/user.go @@ -2,7 +2,6 @@ package api import ( "context" - "fmt" "strconv" "github.com/TicketsBot-cloud/dashboard/rpc/cache" @@ -15,13 +14,13 @@ func UserHandler(ctx *gin.Context) { userId, err := strconv.ParseUint(ctx.Param("user"), 10, 64) if err != nil { - ctx.JSON(400, utils.ErrorStr(fmt.Sprintf("Invalid user ID provided: %s", ctx.Param("user")))) + ctx.JSON(400, utils.ErrorStr("Invalid user ID provided: %s", ctx.Param("user"))) return } var username string if err := cache.Instance.QueryRow(context.Background(), `SELECT "data"->>'Username' FROM users WHERE users.user_id=$1 AND EXISTS(SELECT FROM members WHERE members.guild_id=$2);`, userId, guildId).Scan(&username); err != nil { - ctx.JSON(404, utils.ErrorStr(fmt.Sprintf("User %d not found in guild %d", userId, guildId))) + ctx.JSON(404, utils.ErrorStr("User %d not found in guild %d", userId, guildId)) return } diff --git a/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go b/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go index 6592b159..b9ddee2a 100644 --- a/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go +++ b/app/http/endpoints/api/whitelabel/whitelabelcreateinteractions.go @@ -65,16 +65,18 @@ var ErrInteractionCreateCooldown = errors.New("Interaction creation on cooldown" func createInteractions(cm *manager.CommandManager, botId uint64, token string) error { // Cooldown key := fmt.Sprintf("tickets:interaction-create-cooldown:%d", botId) + ctx, cancel := redis.DefaultContext() + defer cancel() // try to set first, prevent race condition - wasSet, err := redis.Client.SetNX(redis.DefaultContext(), key, 1, time.Minute).Result() + wasSet, err := redis.Client.SetNX(ctx, key, 1, time.Minute).Result() if err != nil { return err } // on cooldown, tell user how long left if !wasSet { - expiration, err := redis.Client.TTL(redis.DefaultContext(), key).Result() + expiration, err := redis.Client.TTL(ctx, key).Result() if err != nil { return err } diff --git a/app/http/middleware/logging.go b/app/http/middleware/logging.go index 8330060d..41dbc34d 100644 --- a/app/http/middleware/logging.go +++ b/app/http/middleware/logging.go @@ -33,7 +33,7 @@ func Logging(logger *zap.Logger) gin.HandlerFunc { zap.String("query", raw), zap.Int("status", c.Writer.Status()), zap.String("timestamp", start.String()), - zap.Duration("latency", time.Now().Sub(start)), + zap.Duration("latency", time.Since(start)), zap.String("client_ip", c.ClientIP()), } diff --git a/app/http/middleware/multireadbody.go b/app/http/middleware/multireadbody.go index 6d9d087f..20b729dd 100644 --- a/app/http/middleware/multireadbody.go +++ b/app/http/middleware/multireadbody.go @@ -3,10 +3,10 @@ package middleware import ( "bytes" "github.com/gin-gonic/gin" - "io/ioutil" + "io" ) func MultiReadBody(ctx *gin.Context) { - body, _ := ioutil.ReadAll(ctx.Request.Body) - ctx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + body, _ := io.ReadAll(ctx.Request.Body) + ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body)) } diff --git a/app/http/middleware/ratelimit.go b/app/http/middleware/ratelimit.go index 05fa31bd..7bc20707 100644 --- a/app/http/middleware/ratelimit.go +++ b/app/http/middleware/ratelimit.go @@ -36,7 +36,10 @@ func CreateRateLimiter(rlType RateLimitType, max int, period time.Duration) gin. return } - res, err := limiter.Allow(redis.DefaultContext(), name, limit) + redisCtx, cancel := redis.DefaultContext() + defer cancel() + + res, err := limiter.Allow(redisCtx, name, limit) if err != nil { ctx.AbortWithStatusJSON(500, utils.ErrorStr("Failed to check rate limit: %v", err)) return diff --git a/app/http/middleware/verifywhitelabel.go b/app/http/middleware/verifywhitelabel.go index 550d48c8..05e168d3 100644 --- a/app/http/middleware/verifywhitelabel.go +++ b/app/http/middleware/verifywhitelabel.go @@ -1,6 +1,7 @@ package middleware import ( + "slices" "fmt" "github.com/TicketsBot-cloud/common/premium" @@ -22,11 +23,8 @@ func VerifyWhitelabel(isApi bool) func(ctx *gin.Context) { if tier < premium.Whitelabel { var isForced bool - for _, id := range config.Conf.ForceWhitelabel { - if id == userId { - isForced = true - break - } + if slices.Contains(config.Conf.ForceWhitelabel, userId) { + isForced = true } if !isForced { diff --git a/app/http/session/redisstore.go b/app/http/session/redisstore.go index 61a1a25d..9831b22b 100644 --- a/app/http/session/redisstore.go +++ b/app/http/session/redisstore.go @@ -25,7 +25,10 @@ func NewRedisStore() *RedisStore { var keyPrefix = "panel:session:" func (s *RedisStore) Get(userId uint64) (SessionData, error) { - raw, err := s.client.Get(wrapper.DefaultContext(), fmt.Sprintf("%s:%d", keyPrefix, userId)).Result() + ctx, cancel := wrapper.DefaultContext() + defer cancel() + + raw, err := s.client.Get(ctx, fmt.Sprintf("%s:%d", keyPrefix, userId)).Result() if err != nil { if err == redis.Nil { err = ErrNoSession @@ -48,11 +51,16 @@ func (s *RedisStore) Set(userId uint64, data SessionData) error { return err } - expiration := time.Unix(data.Expiry, 0).Sub(time.Now()) + expiration := time.Until(time.Unix(data.Expiry, 0)) + ctx, cancel := wrapper.DefaultContext() + defer cancel() - return s.client.Set(wrapper.DefaultContext(), fmt.Sprintf("%s:%d", keyPrefix, userId), encoded, expiration).Err() + return s.client.Set(ctx, fmt.Sprintf("%s:%d", keyPrefix, userId), encoded, expiration).Err() } func (s *RedisStore) Clear(userId uint64) error { - return s.client.Del(wrapper.DefaultContext(), fmt.Sprintf("%s:%d", keyPrefix, userId)).Err() + ctx, cancel := wrapper.DefaultContext() + defer cancel() + + return s.client.Del(ctx, fmt.Sprintf("%s:%d", keyPrefix, userId)).Err() } diff --git a/app/http/validation/defaults/defaults_test.go b/app/http/validation/defaults/defaults_test.go index 35c39502..2cb66216 100644 --- a/app/http/validation/defaults/defaults_test.go +++ b/app/http/validation/defaults/defaults_test.go @@ -9,6 +9,6 @@ import ( func TestNil(t *testing.T) { var myString *string - ApplyDefaults(NewDefaultApplicator[*string](NilCheck[string], &myString, utils.Ptr("hello"))) + ApplyDefaults(NewDefaultApplicator(NilCheck[string], &myString, utils.Ptr("hello"))) assert.Equal(t, "hello", *myString) } diff --git a/app/http/validation/validator.go b/app/http/validation/validator.go index fe18553a..b6d6bc09 100644 --- a/app/http/validation/validator.go +++ b/app/http/validation/validator.go @@ -12,7 +12,6 @@ func Validate[T any](ctx context.Context, validationContext T, validators ...Val group, _ := errgroup.WithContext(ctx) for _, validator := range validators { - validator := validator group.Go(validator(validationContext)) } diff --git a/botcontext/botcontext.go b/botcontext/botcontext.go index 0aecf033..850d271f 100644 --- a/botcontext/botcontext.go +++ b/botcontext/botcontext.go @@ -11,7 +11,7 @@ import ( "github.com/TicketsBot-cloud/dashboard/redis" cacheclient "github.com/TicketsBot-cloud/dashboard/rpc/cache" "github.com/TicketsBot-cloud/database" - cache "github.com/TicketsBot-cloud/gdl/cache" + "github.com/TicketsBot-cloud/gdl/cache" "github.com/TicketsBot-cloud/gdl/objects/channel" "github.com/TicketsBot-cloud/gdl/objects/guild" "github.com/TicketsBot-cloud/gdl/objects/guild/emoji" diff --git a/botcontext/get.go b/botcontext/get.go index 9eefdf1c..b18276ca 100644 --- a/botcontext/get.go +++ b/botcontext/get.go @@ -6,19 +6,19 @@ import ( "github.com/TicketsBot-cloud/common/restcache" "github.com/TicketsBot-cloud/dashboard/config" - dbclient "github.com/TicketsBot-cloud/dashboard/database" + "github.com/TicketsBot-cloud/dashboard/database" "github.com/TicketsBot-cloud/dashboard/redis" "github.com/TicketsBot-cloud/gdl/rest/ratelimit" ) func ContextForGuild(guildId uint64) (*BotContext, error) { - whitelabelBotId, isWhitelabel, err := dbclient.Client.WhitelabelGuilds.GetBotByGuild(context.Background(), guildId) + whitelabelBotId, isWhitelabel, err := database.Client.WhitelabelGuilds.GetBotByGuild(context.Background(), guildId) if err != nil { return nil, err } if isWhitelabel { - res, err := dbclient.Client.Whitelabel.GetByBotId(context.Background(), whitelabelBotId) + res, err := database.Client.Whitelabel.GetByBotId(context.Background(), whitelabelBotId) if err != nil { return nil, err } diff --git a/redis/panelcreate.go b/redis/panelcreate.go index 2804ae23..2d036026 100644 --- a/redis/panelcreate.go +++ b/redis/panelcreate.go @@ -14,5 +14,8 @@ func (c *RedisClient) PublishPanelCreate(settings database.Panel) { return } - c.Publish(DefaultContext(), "tickets:panel:create", string(encoded)) + ctx, cancel := DefaultContext() + defer cancel() + + c.Publish(ctx, "tickets:panel:create", string(encoded)) } diff --git a/redis/redis.go b/redis/redis.go index 78a1e433..77e907e3 100644 --- a/redis/redis.go +++ b/redis/redis.go @@ -27,7 +27,6 @@ func NewRedisClient() *RedisClient { } } -func DefaultContext() context.Context { - ctx, _ := context.WithTimeout(context.Background(), time.Second*3) - return ctx +func DefaultContext() (context.Context, context.CancelFunc) { + return context.WithTimeout(context.Background(), time.Second*3) } diff --git a/redis/ticketclose.go b/redis/ticketclose.go index 9a24c7d2..56482b94 100644 --- a/redis/ticketclose.go +++ b/redis/ticketclose.go @@ -26,5 +26,8 @@ func (c *RedisClient) PublishTicketClose(guildId uint64, ticketId int, userId ui return } - c.Publish(DefaultContext(), "tickets:close", string(encoded)) + ctx, cancel := DefaultContext() + defer cancel() + + c.Publish(ctx, "tickets:close", string(encoded)) } diff --git a/utils/fileutils.go b/utils/fileutils.go index 07f515d0..d2fbc9e3 100644 --- a/utils/fileutils.go +++ b/utils/fileutils.go @@ -1,9 +1,9 @@ package utils -import "io/ioutil" +import "os" func ReadFile(path string) (string, error) { - content, err := ioutil.ReadFile(path) + content, err := os.ReadFile(path) if err != nil { return "", err } diff --git a/utils/guildutils.go b/utils/guildutils.go index 3d51875b..3503dc49 100644 --- a/utils/guildutils.go +++ b/utils/guildutils.go @@ -44,7 +44,7 @@ func LoadGuilds(ctx context.Context, accessToken string, userId uint64) ([]Guild return nil, err } - userGuilds, err := getGuildIntersection(ctx, userId, guilds) + userGuilds, err := getGuildIntersection(ctx, guilds) if err != nil { return nil, err } @@ -54,8 +54,6 @@ func LoadGuilds(ctx context.Context, accessToken string, userId uint64) ([]Guild var mu sync.Mutex dtos := make([]GuildDto, 0, len(userGuilds)) for _, guild := range userGuilds { - guild := guild - group.Go(func() error { permLevel, err := GetPermissionLevel(ctx, guild.Id, userId) if err != nil { @@ -113,7 +111,7 @@ func storeGuildsInDb(ctx context.Context, userId uint64, guilds []guild.Guild) e return dbclient.Client.UserGuilds.Set(ctx, userId, wrappedGuilds) } -func getGuildIntersection(ctx context.Context, userId uint64, userGuilds []guild.Guild) ([]guild.Guild, error) { +func getGuildIntersection(ctx context.Context, userGuilds []guild.Guild) ([]guild.Guild, error) { guildIds := make([]uint64, len(userGuilds)) for i, guild := range userGuilds { guildIds[i] = guild.Id diff --git a/utils/requestutils.go b/utils/requestutils.go index 17435dc9..cdd702b1 100644 --- a/utils/requestutils.go +++ b/utils/requestutils.go @@ -10,7 +10,10 @@ import ( func ErrorJson(err error) map[string]any { log.Logger.Error(err.Error(), zap.Error(err)) - return ErrorStr(err.Error()) + return gin.H{ + "success": false, + "error": err.Error(), + } } func ErrorStr(err string, format ...any) map[string]any { From f92c248e20d5a0440e56797625463f8f0a4f7a26 Mon Sep 17 00:00:00 2001 From: biast12 Date: Mon, 27 Apr 2026 20:53:24 +0200 Subject: [PATCH 4/5] Format: align struct fields, imports, whitespace Non-functional formatting changes across several files: aligned struct field spacing in panel and transcripts responses, reordered/cleaned imports, removed extra blank lines, and adjusted minor whitespace in embeds/components. Files touched: multipanellist.go, multipanelmessagedata.go, panelcreate.go, panelmessagedata.go, panelupdate.go, transcripts/list.go, verifywhitelabel.go. No behavioral changes. --- app/http/endpoints/api/panel/multipanellist.go | 16 ++++++++-------- .../endpoints/api/panel/multipanelmessagedata.go | 4 ++-- app/http/endpoints/api/panel/panelcreate.go | 1 - app/http/endpoints/api/panel/panelmessagedata.go | 2 +- app/http/endpoints/api/panel/panelupdate.go | 1 - app/http/endpoints/api/transcripts/list.go | 6 +++--- app/http/middleware/verifywhitelabel.go | 2 +- 7 files changed, 15 insertions(+), 17 deletions(-) diff --git a/app/http/endpoints/api/panel/multipanellist.go b/app/http/endpoints/api/panel/multipanellist.go index 351a61aa..802504b3 100644 --- a/app/http/endpoints/api/panel/multipanellist.go +++ b/app/http/endpoints/api/panel/multipanellist.go @@ -20,14 +20,14 @@ func MultiPanelList(ctx *gin.Context) { } type multiPanelResponse struct { - Id int `json:"id"` - MessageId uint64 `json:"message_id,string"` - ChannelId uint64 `json:"channel_id,string"` - GuildId uint64 `json:"guild_id,string"` - SelectMenu bool `json:"select_menu"` - SelectMenuPlaceholder *string `json:"select_menu_placeholder"` - Embed *types.CustomEmbed `json:"embed"` - Panels []panelConfiguration `json:"panels"` + Id int `json:"id"` + MessageId uint64 `json:"message_id,string"` + ChannelId uint64 `json:"channel_id,string"` + GuildId uint64 `json:"guild_id,string"` + SelectMenu bool `json:"select_menu"` + SelectMenuPlaceholder *string `json:"select_menu_placeholder"` + Embed *types.CustomEmbed `json:"embed"` + Panels []panelConfiguration `json:"panels"` } guildId := ctx.Keys["guildid"].(uint64) diff --git a/app/http/endpoints/api/panel/multipanelmessagedata.go b/app/http/endpoints/api/panel/multipanelmessagedata.go index c1bed1a6..7f0bd15b 100644 --- a/app/http/endpoints/api/panel/multipanelmessagedata.go +++ b/app/http/endpoints/api/panel/multipanelmessagedata.go @@ -2,12 +2,12 @@ package api import ( "context" - "math" "fmt" + "math" "github.com/TicketsBot-cloud/dashboard/botcontext" - "github.com/TicketsBot-cloud/dashboard/utils/types" "github.com/TicketsBot-cloud/dashboard/config" + "github.com/TicketsBot-cloud/dashboard/utils/types" "github.com/TicketsBot-cloud/database" "github.com/TicketsBot-cloud/gdl/objects/channel/embed" "github.com/TicketsBot-cloud/gdl/objects/interaction/component" diff --git a/app/http/endpoints/api/panel/panelcreate.go b/app/http/endpoints/api/panel/panelcreate.go index 7344a6a9..a5438154 100644 --- a/app/http/endpoints/api/panel/panelcreate.go +++ b/app/http/endpoints/api/panel/panelcreate.go @@ -265,7 +265,6 @@ func CreatePanel(c *gin.Context) { HideClaimButton: data.HideClaimButton, } - createOptions := panelCreateOptions{ TeamIds: data.Teams, // Already validated AccessControlRules: data.AccessControlList, // Already validated diff --git a/app/http/endpoints/api/panel/panelmessagedata.go b/app/http/endpoints/api/panel/panelmessagedata.go index 3681edb4..e865a035 100644 --- a/app/http/endpoints/api/panel/panelmessagedata.go +++ b/app/http/endpoints/api/panel/panelmessagedata.go @@ -120,7 +120,7 @@ func (p *panelMessageData) edit(c *botcontext.BotContext, messageId uint64) erro } data := rest.EditMessageData{ - Embeds: []*embed.Embed{e}, + Embeds: []*embed.Embed{e}, Components: []component.Component{ component.BuildActionRow(component.BuildButton(component.Button{ Label: p.ButtonLabel, diff --git a/app/http/endpoints/api/panel/panelupdate.go b/app/http/endpoints/api/panel/panelupdate.go index 2aa05af6..57533346 100644 --- a/app/http/endpoints/api/panel/panelupdate.go +++ b/app/http/endpoints/api/panel/panelupdate.go @@ -270,7 +270,6 @@ func UpdatePanel(c *gin.Context) { HideClaimButton: data.HideClaimButton, } - // insert mention data validRoles := utils.ToSet(utils.Map(roles, utils.RoleToId)) diff --git a/app/http/endpoints/api/transcripts/list.go b/app/http/endpoints/api/transcripts/list.go index ff950e77..9e4734a1 100644 --- a/app/http/endpoints/api/transcripts/list.go +++ b/app/http/endpoints/api/transcripts/list.go @@ -32,9 +32,9 @@ type transcriptMetadata struct { type paginatedTranscripts struct { Transcripts []transcriptMetadata `json:"transcripts"` - TotalCount int `json:"total_count"` - TotalPages int `json:"total_pages"` - CurrentPage int `json:"current_page"` + TotalCount int `json:"total_count"` + TotalPages int `json:"total_pages"` + CurrentPage int `json:"current_page"` } func ListTranscripts(ctx *gin.Context) { diff --git a/app/http/middleware/verifywhitelabel.go b/app/http/middleware/verifywhitelabel.go index 05e168d3..24c31cd9 100644 --- a/app/http/middleware/verifywhitelabel.go +++ b/app/http/middleware/verifywhitelabel.go @@ -1,8 +1,8 @@ package middleware import ( - "slices" "fmt" + "slices" "github.com/TicketsBot-cloud/common/premium" "github.com/TicketsBot-cloud/dashboard/config" From ae4f2f14a6db8342ba147a1d5d55d1ed54dd841c Mon Sep 17 00:00:00 2001 From: biast12 Date: Mon, 27 Apr 2026 20:58:39 +0200 Subject: [PATCH 5/5] Use io.ReadAll instead of ioutil.ReadAll Replace deprecated ioutil import and ReadAll call with io.ReadAll. This updates the render proxy to use the io package (Go 1.16+) and removes reliance on ioutil.ReadAll. --- chatreplica/proxy.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chatreplica/proxy.go b/chatreplica/proxy.go index d4bc5568..56296464 100644 --- a/chatreplica/proxy.go +++ b/chatreplica/proxy.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "time" @@ -42,7 +42,7 @@ func Render(payload Payload) ([]byte, error) { return nil, fmt.Errorf("render service returned status code %d", res.StatusCode) } - bytes, err := ioutil.ReadAll(res.Body) + bytes, err := io.ReadAll(res.Body) defer res.Body.Close() if err != nil { return nil, err