diff --git a/app/http/endpoints/api/panel/panelcreate.go b/app/http/endpoints/api/panel/panelcreate.go index b9e5cfc..0a2e99a 100644 --- a/app/http/endpoints/api/panel/panelcreate.go +++ b/app/http/endpoints/api/panel/panelcreate.go @@ -26,8 +26,8 @@ import ( const freePanelLimit = 3 type panelBody struct { - ChannelId uint64 `json:"channel_id,string"` - MessageId uint64 `json:"message_id,string"` + ChannelId *uint64 `json:"channel_id,string"` + MessageId *uint64 `json:"message_id,string"` Title string `json:"title"` Content string `json:"content"` Colour uint32 `json:"colour"` @@ -52,9 +52,13 @@ type panelBody struct { UseThreads bool `json:"use_threads"` } -func (p *panelBody) IntoPanelMessageData(customId string, isPremium bool) panelMessageData { - return panelMessageData{ - ChannelId: p.ChannelId, +func (p *panelBody) IntoPanelMessageData(customId string, isPremium bool) *panelMessageData { + if p.ChannelId == nil { + // This should never happen due to earlier validation + return nil + } + return &panelMessageData{ + ChannelId: *p.ChannelId, Title: p.Title, Content: p.Content, CustomId: customId, @@ -87,7 +91,7 @@ func CreatePanel(c *gin.Context) { return } - data.MessageId = 0 + data.MessageId = nil // Check panel quota premiumTier, err := rpc.PremiumClient.GetTierByGuildId(c, guildId, false, botContext.Token, botContext.RateLimiter) @@ -168,20 +172,24 @@ func CreatePanel(c *gin.Context) { } messageData := data.IntoPanelMessageData(customId, premiumTier > premium.None) - msgId, err := messageData.send(botContext) - if err != nil { - var unwrapped request.RestError - if errors.As(err, &unwrapped) { - if unwrapped.StatusCode == http.StatusForbidden { - c.JSON(400, utils.ErrorStr("Bot does not have permission to send messages in channel %d", data.ChannelId)) + var newMsgId *uint64 + if messageData != nil { + msgId, err := messageData.send(botContext) + if err != nil { + var unwrapped request.RestError + if errors.As(err, &unwrapped) { + if unwrapped.StatusCode == http.StatusForbidden { + c.JSON(400, utils.ErrorStr("Bot does not have permission to send messages in channel %d", data.ChannelId)) + } else { + c.JSON(400, utils.ErrorStr("Failed to send panel message to channel %d: %s", data.ChannelId, unwrapped.ApiError.Message)) + } } else { - c.JSON(400, utils.ErrorStr("Failed to send panel message to channel %d: %s", data.ChannelId, unwrapped.ApiError.Message)) + _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to send panel message to Discord")) } - } else { - _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to send panel message to Discord")) - } - return + return + } + newMsgId = &msgId } var emojiId *uint64 @@ -214,7 +222,7 @@ func CreatePanel(c *gin.Context) { // Store in DB panel := database.Panel{ - MessageId: msgId, + MessageId: newMsgId, ChannelId: data.ChannelId, GuildId: guildId, Title: data.Title, diff --git a/app/http/endpoints/api/panel/paneldelete.go b/app/http/endpoints/api/panel/paneldelete.go index 3694fce..cf17aeb 100644 --- a/app/http/endpoints/api/panel/paneldelete.go +++ b/app/http/endpoints/api/panel/paneldelete.go @@ -1,8 +1,8 @@ package api import ( - "fmt" "errors" + "fmt" "net/http" "strconv" @@ -70,11 +70,13 @@ func DeletePanel(c *gin.Context) { } // TODO: Set timeout on context - if err := rest.DeleteMessage(c, botContext.Token, botContext.RateLimiter, panel.ChannelId, panel.MessageId); err != nil { - var unwrapped request.RestError - if !errors.As(err, &unwrapped) || unwrapped.StatusCode != 404 { - _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to delete panel")) - return + if panel.ChannelId != nil && panel.MessageId != nil { + if err := rest.DeleteMessage(c, botContext.Token, botContext.RateLimiter, *panel.ChannelId, *panel.MessageId); err != nil { + var unwrapped request.RestError + if !errors.As(err, &unwrapped) || unwrapped.StatusCode != 404 { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to delete panel")) + return + } } } diff --git a/app/http/endpoints/api/panel/panelmessagedata.go b/app/http/endpoints/api/panel/panelmessagedata.go index 14227a9..94cfc7e 100644 --- a/app/http/endpoints/api/panel/panelmessagedata.go +++ b/app/http/endpoints/api/panel/panelmessagedata.go @@ -27,7 +27,11 @@ type panelMessageData struct { IsPremium bool } -func panelIntoMessageData(panel database.Panel, isPremium bool) panelMessageData { +func panelIntoMessageData(panel database.Panel, isPremium bool) *panelMessageData { + if panel.ChannelId == nil { + return nil + } + var emote *emoji.Emoji if panel.EmojiName != nil { // No emoji = nil if panel.EmojiId == nil { // Unicode emoji @@ -42,8 +46,8 @@ func panelIntoMessageData(panel database.Panel, isPremium bool) panelMessageData } } - return panelMessageData{ - ChannelId: panel.ChannelId, + return &panelMessageData{ + ChannelId: *panel.ChannelId, Title: panel.Title, Content: panel.Content, CustomId: panel.CustomId, diff --git a/app/http/endpoints/api/panel/panelresend.go b/app/http/endpoints/api/panel/panelresend.go index 59eb58a..e9127c3 100644 --- a/app/http/endpoints/api/panel/panelresend.go +++ b/app/http/endpoints/api/panel/panelresend.go @@ -1,9 +1,9 @@ package api import ( - "fmt" "context" "errors" + "fmt" "strconv" "github.com/TicketsBot-cloud/common/premium" @@ -54,13 +54,20 @@ func ResendPanel(ctx *gin.Context) { return } + if panel.ChannelId == nil { + ctx.JSON(400, utils.ErrorStr("This panel has no selected channel.")) + return + } + // delete old message // TODO: Use proper context - if err := rest.DeleteMessage(context.Background(), botContext.Token, botContext.RateLimiter, panel.ChannelId, panel.GuildId); err != nil { - var unwrapped request.RestError - if errors.As(err, &unwrapped) && !unwrapped.IsClientError() { - ctx.JSON(500, utils.ErrorStr("Failed to send message. Please try again.")) - return + if panel.MessageId != nil { + if err := rest.DeleteMessage(context.Background(), botContext.Token, botContext.RateLimiter, *panel.ChannelId, *panel.MessageId); err != nil { + var unwrapped request.RestError + if errors.As(err, &unwrapped) && !unwrapped.IsClientError() { + ctx.JSON(500, utils.ErrorStr("Failed to send message. Please try again.")) + return + } } } @@ -71,19 +78,23 @@ func ResendPanel(ctx *gin.Context) { } messageData := panelIntoMessageData(panel, premiumTier > premium.None) - msgId, err := messageData.send(botContext) - if err != nil { - var unwrapped request.RestError - if errors.As(err, &unwrapped) && unwrapped.StatusCode == 403 { - ctx.JSON(500, utils.ErrorStr("I do not have permission to send messages in the provided channel")) - } else { - ctx.JSON(500, utils.ErrorStr("Failed to send message. Please try again.")) - } + var newMsgId *uint64 + if messageData != nil { + msgId, err := messageData.send(botContext) + if err != nil { + var unwrapped request.RestError + if errors.As(err, &unwrapped) && unwrapped.StatusCode == 403 { + ctx.JSON(500, utils.ErrorStr("I do not have permission to send messages in the provided channel")) + } else { + ctx.JSON(500, utils.ErrorStr("Failed to send message. Please try again.")) + } - return + return + } + newMsgId = &msgId } - if err = dbclient.Client.Panel.UpdateMessageId(ctx, panel.PanelId, msgId); err != nil { + if err = dbclient.Client.Panel.UpdateMessageId(ctx, panel.PanelId, newMsgId); err != nil { ctx.JSON(500, utils.ErrorStr("Failed to send message. Please try again.")) return } diff --git a/app/http/endpoints/api/panel/panelupdate.go b/app/http/endpoints/api/panel/panelupdate.go index d230323..c1ea57f 100644 --- a/app/http/endpoints/api/panel/panelupdate.go +++ b/app/http/endpoints/api/panel/panelupdate.go @@ -146,31 +146,42 @@ func UpdatePanel(c *gin.Context) { existing.ButtonLabel != data.ButtonLabel || existing.Disabled != data.Disabled - newMessageId := existing.MessageId + var newMessageId *uint64 + if existing.ChannelId != nil && data.ChannelId != nil { + // old message exists and provided channel exists + newMessageId = existing.MessageId + } if shouldUpdateMessage { - // delete old message, ignoring error // TODO: Use proper context - _ = rest.DeleteMessage(c, botContext.Token, botContext.RateLimiter, existing.ChannelId, existing.MessageId) + if existing.ChannelId != nil && existing.MessageId != nil { + // delete old message, ignoring error + _ = rest.DeleteMessage(c, botContext.Token, botContext.RateLimiter, *existing.ChannelId, *existing.MessageId) + } - messageData := data.IntoPanelMessageData(existing.CustomId, premiumTier > premium.None) - newMessageId, err = messageData.send(botContext) - if err != nil { - var unwrapped request.RestError - if errors.As(err, &unwrapped) { - if unwrapped.StatusCode == 403 { - c.JSON(403, utils.ErrorStr("I do not have permission to send messages in the specified channel")) - return - } else if unwrapped.StatusCode == 404 { - // Swallow error - // TODO: Make channel_id column nullable, and set to null - } else { - _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to update panel")) - return + if data.ChannelId == nil { + messageData := data.IntoPanelMessageData(existing.CustomId, premiumTier > premium.None) + if messageData != nil { + messageId, err := messageData.send(botContext) + if err != nil { + var unwrapped request.RestError + if errors.As(err, &unwrapped) { + if unwrapped.StatusCode == 403 { + c.JSON(403, utils.ErrorStr("I do not have permission to send messages in the specified channel")) + return + } else if unwrapped.StatusCode == 404 { + // Swallow error + dbclient.Client.Panel.UpdateChannelId(c, existing.PanelId, nil) + } else { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to update panel")) + return + } + } else { + _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to update panel")) + return + } } - } else { - _ = c.AbortWithError(http.StatusInternalServerError, app.NewError(err, "Failed to update panel")) - return + newMessageId = &messageId } } } diff --git a/app/http/endpoints/api/panel/validation.go b/app/http/endpoints/api/panel/validation.go index 50b5222..ea1f3cb 100644 --- a/app/http/endpoints/api/panel/validation.go +++ b/app/http/endpoints/api/panel/validation.go @@ -99,8 +99,11 @@ func validateContent(ctx PanelValidationContext) validation.ValidationFunc { func validateChannelId(ctx PanelValidationContext) validation.ValidationFunc { return func() error { + if ctx.Data.ChannelId == nil { + return nil + } 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.ChannelTypeGuildNews) { return nil } } diff --git a/frontend/src/components/manage/PanelCreationForm.svelte b/frontend/src/components/manage/PanelCreationForm.svelte index b298914..5eb64b4 100644 --- a/frontend/src/components/manage/PanelCreationForm.svelte +++ b/frontend/src/components/manage/PanelCreationForm.svelte @@ -409,6 +409,8 @@ label="Panel Channel" allowAnnouncementChannel col4 + withNull + nullLabel={"None"} {channels} bind:value={data.channel_id} /> diff --git a/frontend/src/views/panels/Panels.svelte b/frontend/src/views/panels/Panels.svelte index 940cdbd..623d91a 100644 --- a/frontend/src/views/panels/Panels.svelte +++ b/frontend/src/views/panels/Panels.svelte @@ -179,11 +179,11 @@ {#each panels as panel} - #{channels.find( - (c) => c.id === panel.channel_id, - )?.name ?? "Unknown Channel"} + {#if panel.channel_id == null || panel.channel_id == "null"} + None + {:else} + #{channels.find((c) => c.id === panel.channel_id)?.name ?? "Unknown Channel"} + {/if} {panel.title} {#if panel.has_support_hours} @@ -206,7 +206,7 @@