Note: The panels which you wish to combine into a multi-panel must already exist
+ + {#if !$loadingScreen} +diff --git a/app/http/endpoints/api/panel/multipanelcreate.go b/app/http/endpoints/api/panel/multipanelcreate.go
index 1b2d29f..c8edf47 100644
--- a/app/http/endpoints/api/panel/multipanelcreate.go
+++ b/app/http/endpoints/api/panel/multipanelcreate.go
@@ -8,35 +8,31 @@ import (
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/TicketsBot/GoPanel/utils"
+ "github.com/TicketsBot/GoPanel/utils/types"
"github.com/TicketsBot/common/premium"
"github.com/TicketsBot/database"
"github.com/gin-gonic/gin"
+ "github.com/go-playground/validator/v10"
"github.com/rxdn/gdl/objects/channel"
"github.com/rxdn/gdl/rest/request"
"golang.org/x/sync/errgroup"
)
type multiPanelCreateData struct {
- Title string `json:"title"`
- Content string `json:"content"`
- Colour int32 `json:"colour"`
- ChannelId uint64 `json:"channel_id,string"`
- SelectMenu bool `json:"select_menu"`
- Panels []int `json:"panels"`
- ImageUrl *string `json:"image_url,omitempty"`
- ThumbnailUrl *string `json:"thumbnail_url,omitempty"`
+ ChannelId uint64 `json:"channel_id,string"`
+ SelectMenu bool `json:"select_menu"`
+ SelectMenuPlaceholder *string `json:"select_menu_placeholder,omitempty" validate:"omitempty,max=150"`
+ Panels []int `json:"panels"`
+ Embed *types.CustomEmbed `json:"embed" validate:"omitempty,dive"`
}
func (d *multiPanelCreateData) IntoMessageData(isPremium bool) multiPanelMessageData {
return multiPanelMessageData{
- ChannelId: d.ChannelId,
- Title: d.Title,
- Content: d.Content,
- Colour: int(d.Colour),
- SelectMenu: d.SelectMenu,
- IsPremium: isPremium,
- ImageUrl: d.ImageUrl,
- ThumbnailUrl: d.ThumbnailUrl,
+ IsPremium: isPremium,
+ ChannelId: d.ChannelId,
+ SelectMenu: d.SelectMenu,
+ SelectMenuPlaceholder: d.SelectMenuPlaceholder,
+ Embed: d.Embed.IntoDiscordEmbed(),
}
}
@@ -49,6 +45,18 @@ func MultiPanelCreate(ctx *gin.Context) {
return
}
+ if err := validate.Struct(data); err != nil {
+ var validationErrors validator.ValidationErrors
+ if ok := errors.As(err, &validationErrors); !ok {
+ ctx.JSON(500, utils.ErrorStr("An error occurred while validating the panel"))
+ return
+ }
+
+ formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors)
+ ctx.JSON(400, utils.ErrorStr(formatted))
+ return
+ }
+
// validate body & get sub-panels
panels, err := data.doValidations(guildId)
if err != nil {
@@ -86,14 +94,17 @@ func MultiPanelCreate(ctx *gin.Context) {
return
}
+ dbEmbed, dbEmbedFields := data.Embed.IntoDatabaseStruct()
multiPanel := database.MultiPanel{
- MessageId: messageId,
- ChannelId: data.ChannelId,
- GuildId: guildId,
- Title: data.Title,
- Content: data.Content,
- Colour: int(data.Colour),
- SelectMenu: data.SelectMenu,
+ MessageId: messageId,
+ ChannelId: data.ChannelId,
+ GuildId: guildId,
+ SelectMenu: data.SelectMenu,
+ SelectMenuPlaceholder: data.SelectMenuPlaceholder,
+ Embed: &database.CustomEmbedWithFields{
+ CustomEmbed: dbEmbed,
+ Fields: dbEmbedFields,
+ },
}
multiPanel.Id, err = dbclient.Client.MultiPanels.Create(ctx, multiPanel)
@@ -123,10 +134,12 @@ func MultiPanelCreate(ctx *gin.Context) {
}
func (d *multiPanelCreateData) doValidations(guildId uint64) (panels []database.Panel, err error) {
+ if err := validateEmbed(d.Embed); err != nil {
+ return nil, err
+ }
+
group, _ := errgroup.WithContext(context.Background())
- group.Go(d.validateTitle)
- group.Go(d.validateContent)
group.Go(d.validateChannel(guildId))
group.Go(func() (e error) {
panels, e = d.validatePanels(guildId)
@@ -137,26 +150,6 @@ func (d *multiPanelCreateData) doValidations(guildId uint64) (panels []database.
return
}
-func (d *multiPanelCreateData) validateTitle() (err error) {
- if len(d.Title) > 255 {
- err = errors.New("Embed title must be between 1 and 255 characters")
- } else if len(d.Title) == 0 {
- d.Title = "Click to open a ticket"
- }
-
- return
-}
-
-func (d *multiPanelCreateData) validateContent() (err error) {
- if len(d.Content) > 4096 {
- err = errors.New("Embed content must be between 1 and 4096 characters")
- } else if len(d.Content) == 0 { // Fill default
- d.Content = "Click on the button corresponding to the type of ticket you wish to open"
- }
-
- return
-}
-
func (d *multiPanelCreateData) validateChannel(guildId uint64) func() error {
return func() error {
// TODO: Use proper context
diff --git a/app/http/endpoints/api/panel/multipanelmessagedata.go b/app/http/endpoints/api/panel/multipanelmessagedata.go
index ee34b6a..c82e2c1 100644
--- a/app/http/endpoints/api/panel/multipanelmessagedata.go
+++ b/app/http/endpoints/api/panel/multipanelmessagedata.go
@@ -13,46 +13,32 @@ import (
)
type multiPanelMessageData struct {
+ IsPremium bool
+
ChannelId uint64
- Title string
- Content string
- Colour int
- SelectMenu bool
- IsPremium bool
- ImageUrl, ThumbnailUrl *string
+ SelectMenu bool
+ SelectMenuPlaceholder *string
+
+ Embed *embed.Embed
}
func multiPanelIntoMessageData(panel database.MultiPanel, isPremium bool) multiPanelMessageData {
return multiPanelMessageData{
- ChannelId: panel.ChannelId,
- Title: panel.Title,
- Content: panel.Content,
- Colour: panel.Colour,
- SelectMenu: panel.SelectMenu,
- IsPremium: isPremium,
- ImageUrl: panel.ImageUrl,
- ThumbnailUrl: panel.ThumbnailUrl,
+ IsPremium: isPremium,
+
+ ChannelId: panel.ChannelId,
+
+ SelectMenu: panel.SelectMenu,
+ SelectMenuPlaceholder: panel.SelectMenuPlaceholder,
+ Embed: types.NewCustomEmbed(panel.Embed.CustomEmbed, panel.Embed.Fields).IntoDiscordEmbed(),
}
}
func (d *multiPanelMessageData) send(ctx *botcontext.BotContext, panels []database.Panel) (uint64, error) {
- e := embed.NewEmbed().
- SetTitle(d.Title).
- SetDescription(d.Content).
- SetColor(d.Colour)
-
- if d.ImageUrl != nil {
- e.SetImage(*d.ImageUrl)
- }
-
- if d.ThumbnailUrl != nil {
- e.SetThumbnail(*d.ThumbnailUrl)
- }
-
if !d.IsPremium {
// TODO: Don't harcode
- e.SetFooter("Powered by ticketsbot.net", "https://ticketsbot.net/assets/img/logo.png")
+ d.Embed.SetFooter("Powered by ticketsbot.net", "https://ticketsbot.net/assets/img/logo.png")
}
var components []component.Component
@@ -68,13 +54,20 @@ func (d *multiPanelMessageData) send(ctx *botcontext.BotContext, panels []databa
}
}
+ var placeholder string
+ if d.SelectMenuPlaceholder == nil {
+ placeholder = "Select a topic..."
+ } else {
+ placeholder = *d.SelectMenuPlaceholder
+ }
+
components = []component.Component{
component.BuildActionRow(
component.BuildSelectMenu(
component.SelectMenu{
CustomId: "multipanel",
Options: options,
- Placeholder: "Select a topic...",
+ Placeholder: placeholder,
MinValues: utils.IntPtr(1),
MaxValues: utils.IntPtr(1),
Disabled: false,
@@ -116,7 +109,7 @@ func (d *multiPanelMessageData) send(ctx *botcontext.BotContext, panels []databa
}
data := rest.CreateMessageData{
- Embeds: []*embed.Embed{e},
+ Embeds: []*embed.Embed{d.Embed},
Components: components,
}
diff --git a/app/http/endpoints/api/panel/multipanelupdate.go b/app/http/endpoints/api/panel/multipanelupdate.go
index a5b4d08..df921b0 100644
--- a/app/http/endpoints/api/panel/multipanelupdate.go
+++ b/app/http/endpoints/api/panel/multipanelupdate.go
@@ -11,6 +11,7 @@ import (
"github.com/TicketsBot/common/premium"
"github.com/TicketsBot/database"
"github.com/gin-gonic/gin"
+ "github.com/go-playground/validator/v10"
"github.com/rxdn/gdl/rest"
"github.com/rxdn/gdl/rest/request"
"golang.org/x/sync/errgroup"
@@ -53,6 +54,18 @@ func MultiPanelUpdate(c *gin.Context) {
return
}
+ if err := validate.Struct(data); err != nil {
+ var validationErrors validator.ValidationErrors
+ if ok := errors.As(err, &validationErrors); !ok {
+ c.JSON(500, utils.ErrorStr("An error occurred while validating the panel"))
+ return
+ }
+
+ formatted := "Your input contained the following errors:\n" + utils.FormatValidationErrors(validationErrors)
+ c.JSON(400, utils.ErrorStr(formatted))
+ return
+ }
+
// validate body & get sub-panels
panels, err := data.doValidations(guildId)
if err != nil {
@@ -115,17 +128,18 @@ func MultiPanelUpdate(c *gin.Context) {
}
// update DB
+ dbEmbed, dbEmbedFields := data.Embed.IntoDatabaseStruct()
updated := database.MultiPanel{
- Id: multiPanel.Id,
- MessageId: messageId,
- ChannelId: data.ChannelId,
- GuildId: guildId,
- Title: data.Title,
- Content: data.Content,
- Colour: int(data.Colour),
- SelectMenu: data.SelectMenu,
- ImageUrl: data.ImageUrl,
- ThumbnailUrl: data.ThumbnailUrl,
+ Id: multiPanel.Id,
+ MessageId: messageId,
+ ChannelId: data.ChannelId,
+ GuildId: guildId,
+ SelectMenu: data.SelectMenu,
+ SelectMenuPlaceholder: data.SelectMenuPlaceholder,
+ Embed: &database.CustomEmbedWithFields{
+ CustomEmbed: dbEmbed,
+ Fields: dbEmbedFields,
+ },
}
if err = dbclient.Client.MultiPanels.Update(c, multiPanel.Id, updated); err != nil {
diff --git a/app/http/endpoints/api/panel/paneldelete.go b/app/http/endpoints/api/panel/paneldelete.go
index 42f2fc5..b58ade4 100644
--- a/app/http/endpoints/api/panel/paneldelete.go
+++ b/app/http/endpoints/api/panel/paneldelete.go
@@ -95,14 +95,7 @@ func DeletePanel(ctx *gin.Context) {
return
}
- messageData := multiPanelMessageData{
- Title: multiPanel.Title,
- Content: multiPanel.Content,
- Colour: multiPanel.Colour,
- ChannelId: multiPanel.ChannelId,
- SelectMenu: multiPanel.SelectMenu,
- IsPremium: premiumTier > premium.None,
- }
+ messageData := multiPanelIntoMessageData(multiPanel, premiumTier > premium.None)
messageId, err := messageData.send(botContext, panels)
if err != nil {
diff --git a/app/http/endpoints/api/panel/panelupdate.go b/app/http/endpoints/api/panel/panelupdate.go
index cb71ad5..c15a385 100644
--- a/app/http/endpoints/api/panel/panelupdate.go
+++ b/app/http/endpoints/api/panel/panelupdate.go
@@ -306,16 +306,7 @@ func UpdatePanel(ctx *gin.Context) {
return
}
- messageData := multiPanelMessageData{
- Title: multiPanel.Title,
- Content: multiPanel.Content,
- Colour: multiPanel.Colour,
- ChannelId: multiPanel.ChannelId,
- SelectMenu: multiPanel.SelectMenu,
- IsPremium: premiumTier > premium.None,
- ImageUrl: multiPanel.ImageUrl,
- ThumbnailUrl: multiPanel.ThumbnailUrl,
- }
+ messageData := multiPanelIntoMessageData(multiPanel, premiumTier > premium.None)
messageId, err := messageData.send(botContext, panels)
if err != nil {
diff --git a/app/http/endpoints/api/panel/validation.go b/app/http/endpoints/api/panel/validation.go
index 0fa1001..2053dac 100644
--- a/app/http/endpoints/api/panel/validation.go
+++ b/app/http/endpoints/api/panel/validation.go
@@ -10,6 +10,7 @@ import (
"github.com/TicketsBot/GoPanel/botcontext"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/utils"
+ "github.com/TicketsBot/GoPanel/utils/types"
"github.com/TicketsBot/database"
"github.com/rxdn/gdl/objects/channel"
"github.com/rxdn/gdl/objects/guild"
@@ -289,13 +290,7 @@ func validateNamingScheme(ctx PanelValidationContext) validation.ValidationFunc
func validateWelcomeMessage(ctx PanelValidationContext) validation.ValidationFunc {
return func() error {
- wm := ctx.Data.WelcomeMessage
-
- if wm == nil || wm.Title != nil || wm.Description != nil || len(wm.Fields) > 0 || wm.ImageUrl != nil || wm.ThumbnailUrl != nil {
- return nil
- }
-
- return validation.NewInvalidInputError("Welcome message has no content")
+ return validateEmbed(ctx.Data.WelcomeMessage)
}
}
@@ -339,3 +334,11 @@ func validateAccessControlList(ctx PanelValidationContext) validation.Validation
return nil
}
}
+
+func validateEmbed(e *types.CustomEmbed) error {
+ if e == nil || e.Title != nil || e.Description != nil || len(e.Fields) > 0 || e.ImageUrl != nil || e.ThumbnailUrl != nil {
+ return nil
+ }
+
+ return validation.NewInvalidInputError("Your embed message does not contain any content")
+}
diff --git a/app/http/endpoints/api/tags/tagcreate.go b/app/http/endpoints/api/tags/tagcreate.go
index 2bec71c..7e89304 100644
--- a/app/http/endpoints/api/tags/tagcreate.go
+++ b/app/http/endpoints/api/tags/tagcreate.go
@@ -1,6 +1,7 @@
package api
import (
+ "errors"
"fmt"
"github.com/TicketsBot/GoPanel/botcontext"
dbclient "github.com/TicketsBot/GoPanel/database"
@@ -59,8 +60,8 @@ func CreateTag(ctx *gin.Context) {
// TODO: Limit command amount
if err := validate.Struct(data); err != nil {
- validationErrors, ok := err.(validator.ValidationErrors)
- if !ok {
+ var validationErrors validator.ValidationErrors
+ if ok := errors.As(err, &validationErrors); !ok {
ctx.JSON(500, utils.ErrorStr("An error occurred while validating the integration"))
return
}
diff --git a/frontend/src/components/Collapsible.svelte b/frontend/src/components/Collapsible.svelte
index fd637fa..94ecc5d 100644
--- a/frontend/src/components/Collapsible.svelte
+++ b/frontend/src/components/Collapsible.svelte
@@ -36,7 +36,7 @@
Your panel quota: {panels.length} / {isPremium ? '∞' : '3'}
- -Channel | -Panel Title | -Ticket Channel Category | -Resend | -Edit | -Delete | -
---|---|---|---|---|---|
#{channels.find((c) => c.id === panel.channel_id)?.name ?? 'Unknown Channel'} | -{panel.title} | -{channels.find((c) => c.id === panel.category_id)?.name ?? 'Unknown Category'} | -- - | -- - | -- - | -
Embed Title | -Resend | -Edit | -Delete | -
---|---|---|---|
{panel.title} | -- - | -- - | -- - | -
Note: The panels which you wish to combine into a multi-panel must already exist
- - {#if !$loadingScreen} -Note: The panels which you wish to combine into a multi-panel must already exist
+ + {#if !$loadingScreen} +Note: The panels which you wish to combine into a multi-panel must already exist
+ + {#if multiPanelData && !$loadingScreen} +Your panel quota: {panels.length} / {isPremium ? '∞' : '3'}
+Channel | +Panel Title | ++ | + | + |
---|---|---|---|---|
#{channels.find((c) => c.id === panel.channel_id)?.name ?? 'Unknown Channel'} | +{panel.title} | ++ + | +
+ |
+ + + | +
Panel Title | ++ | + | + |
---|---|---|---|
{panel.title || 'Open a ticket!'} | ++ + | +
+ |
+ + + | +