From 9818bad95e11a687c48c466c2fedc857b580f89f Mon Sep 17 00:00:00 2001
From: rxdn <29165304+rxdn@users.noreply.github.com>
Date: Mon, 27 Jun 2022 21:33:24 +0100
Subject: [PATCH] Custom embeds
---
app/http/endpoints/api/panel/panelcreate.go | 91 +++++---
app/http/endpoints/api/panel/paneldelete.go | 8 +
app/http/endpoints/api/panel/panellist.go | 33 ++-
app/http/endpoints/api/panel/panelupdate.go | 80 +++++--
frontend/src/components/Badge.svelte | 1 +
frontend/src/components/Collapsible.svelte | 80 +++++++
frontend/src/components/EmbedBuilder.svelte | 208 ++++++++++++++++++
.../src/components/form/DateTimePicker.svelte | 42 ++++
frontend/src/components/form/Input.svelte | 35 +--
.../manage/MultiPanelEditModal.svelte | 1 +
.../manage/PanelCreationForm.svelte | 82 ++++---
.../components/manage/PanelEditModal.svelte | 1 +
go.mod | 14 +-
go.sum | 49 +++--
utils/types/colour.go | 38 ++++
utils/types/customembed.go | 141 ++++++++++++
utils/types/datetimelocal.go | 49 +++++
utils/utils.go | 8 +
18 files changed, 821 insertions(+), 140 deletions(-)
create mode 100644 frontend/src/components/Collapsible.svelte
create mode 100644 frontend/src/components/EmbedBuilder.svelte
create mode 100644 frontend/src/components/form/DateTimePicker.svelte
create mode 100644 utils/types/colour.go
create mode 100644 utils/types/customembed.go
create mode 100644 utils/types/datetimelocal.go
diff --git a/app/http/endpoints/api/panel/panelcreate.go b/app/http/endpoints/api/panel/panelcreate.go
index 583c7b7..53ca441 100644
--- a/app/http/endpoints/api/panel/panelcreate.go
+++ b/app/http/endpoints/api/panel/panelcreate.go
@@ -2,6 +2,7 @@ package api
import (
"errors"
+ "fmt"
"github.com/TicketsBot/GoPanel/botcontext"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc"
@@ -12,6 +13,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/objects/channel"
"github.com/rxdn/gdl/objects/guild/emoji"
"github.com/rxdn/gdl/objects/interaction/component"
@@ -33,7 +35,7 @@ type panelBody struct {
Colour uint32 `json:"colour"`
CategoryId uint64 `json:"category_id,string"`
Emoji types.Emoji `json:"emote"`
- WelcomeMessage *string `json:"welcome_message"`
+ WelcomeMessage *types.CustomEmbed `json:"welcome_message" validate:"omitempty,dive"`
Mentions []string `json:"mentions"`
WithDefaultTeam bool `json:"default_team"`
Teams []int `json:"teams"`
@@ -66,10 +68,7 @@ func CreatePanel(ctx *gin.Context) {
botContext, err := botcontext.ContextForGuild(guildId)
if err != nil {
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.JSON(500, utils.ErrorJson(err))
return
}
@@ -137,26 +136,41 @@ func CreatePanel(ctx *gin.Context) {
}
}
+ // Store welcome message embed first
+ var welcomeMessageEmbed *int
+ if data.WelcomeMessage != nil {
+ embed, fields := data.WelcomeMessage.IntoDatabaseStruct()
+ embed.GuildId = guildId
+
+ id, err := dbclient.Client.Embeds.CreateWithFields(embed, fields)
+ if err != nil {
+ ctx.JSON(500, utils.ErrorJson(err))
+ return
+ }
+
+ welcomeMessageEmbed = &id
+ }
+
// Store in DB
panel := database.Panel{
- MessageId: msgId,
- ChannelId: data.ChannelId,
- GuildId: guildId,
- Title: data.Title,
- Content: data.Content,
- Colour: int32(data.Colour),
- TargetCategory: data.CategoryId,
- EmojiId: emojiId,
- EmojiName: emojiName,
- WelcomeMessage: data.WelcomeMessage,
- WithDefaultTeam: data.WithDefaultTeam,
- CustomId: customId,
- ImageUrl: data.ImageUrl,
- ThumbnailUrl: data.ThumbnailUrl,
- ButtonStyle: int(data.ButtonStyle),
- ButtonLabel: data.ButtonLabel,
- FormId: data.FormId,
- NamingScheme: data.NamingScheme,
+ MessageId: msgId,
+ ChannelId: data.ChannelId,
+ GuildId: guildId,
+ Title: data.Title,
+ Content: data.Content,
+ Colour: int32(data.Colour),
+ TargetCategory: data.CategoryId,
+ EmojiId: emojiId,
+ EmojiName: emojiName,
+ WelcomeMessageEmbed: welcomeMessageEmbed,
+ WithDefaultTeam: data.WithDefaultTeam,
+ CustomId: customId,
+ ImageUrl: data.ImageUrl,
+ ThumbnailUrl: data.ThumbnailUrl,
+ ButtonStyle: int(data.ButtonStyle),
+ ButtonLabel: data.ButtonLabel,
+ FormId: data.FormId,
+ NamingScheme: data.NamingScheme,
}
panelId, err := dbclient.Client.Panel.Create(panel)
@@ -214,8 +228,27 @@ func CreatePanel(ctx *gin.Context) {
}
var urlRegex = regexp.MustCompile(`^https?://([-a-zA-Z0-9@:%._+~#=]{1,256})\.[a-zA-Z0-9()]{1,63}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$`)
+var validate = validator.New()
func (p *panelBody) doValidations(ctx *gin.Context, guildId uint64) bool {
+ err := validate.Struct(p)
+ if err != nil {
+ validationErrors, ok := err.(validator.ValidationErrors)
+ if !ok {
+ ctx.JSON(500, utils.ErrorStr("An error occurred while validating the panel"))
+ return false
+ }
+
+ formatted := "Your input contained the following errors:"
+ for _, validationError := range validationErrors {
+ formatted += fmt.Sprintf("\n%s", validationError.Error())
+ }
+
+ formatted = strings.TrimSuffix(formatted, "\n")
+ ctx.JSON(400, utils.ErrorStr(formatted))
+ return false
+ }
+
botContext, err := botcontext.ContextForGuild(guildId)
if err != nil {
return false // TODO: Log error
@@ -254,14 +287,6 @@ func (p *panelBody) doValidations(ctx *gin.Context, guildId uint64) bool {
return false
}
- if !p.verifyWelcomeMessage() {
- ctx.JSON(400, gin.H{
- "success": false,
- "error": "Welcome message must be blank or between 1 - 4096 characters",
- })
- return false
- }
-
if !p.verifyImageUrl() || !p.verifyThumbnailUrl() {
ctx.JSON(400, gin.H{
"success": false,
@@ -405,10 +430,6 @@ func (p *panelBody) verifyEmoji(ctx botcontext.BotContext, guildId uint64) bool
}
}
-func (p *panelBody) verifyWelcomeMessage() bool {
- return p.WelcomeMessage == nil || (len(*p.WelcomeMessage) > 0 && len(*p.WelcomeMessage) <= 4096)
-}
-
func (p *panelBody) verifyImageUrl() bool {
if p.ImageUrl != nil && len(*p.ImageUrl) == 0 {
p.ImageUrl = nil
diff --git a/app/http/endpoints/api/panel/paneldelete.go b/app/http/endpoints/api/panel/paneldelete.go
index 04801d7..e585c3e 100644
--- a/app/http/endpoints/api/panel/paneldelete.go
+++ b/app/http/endpoints/api/panel/paneldelete.go
@@ -45,6 +45,14 @@ func DeletePanel(ctx *gin.Context) {
return
}
+ // Delete welcome message embed
+ if panel.WelcomeMessageEmbed != nil {
+ if err := database.Client.Embeds.Delete(*panel.WelcomeMessageEmbed); err != nil {
+ ctx.JSON(500, utils.ErrorJson(err))
+ return
+ }
+ }
+
if err := database.Client.Panel.Delete(panelId); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
diff --git a/app/http/endpoints/api/panel/panellist.go b/app/http/endpoints/api/panel/panellist.go
index a5a5c32..ec9c7e3 100644
--- a/app/http/endpoints/api/panel/panellist.go
+++ b/app/http/endpoints/api/panel/panellist.go
@@ -14,21 +14,25 @@ import (
func ListPanels(ctx *gin.Context) {
type panelResponse struct {
database.Panel
- UseCustomEmoji bool `json:"use_custom_emoji"`
- Emoji types.Emoji `json:"emote"`
- Mentions []string `json:"mentions"`
- Teams []int `json:"teams"`
- UseServerDefaultNamingScheme bool `json:"use_server_default_naming_scheme"`
+ WelcomeMessage *types.CustomEmbed `json:"welcome_message"`
+ UseCustomEmoji bool `json:"use_custom_emoji"`
+ Emoji types.Emoji `json:"emote"`
+ Mentions []string `json:"mentions"`
+ Teams []int `json:"teams"`
+ UseServerDefaultNamingScheme bool `json:"use_server_default_naming_scheme"`
}
guildId := ctx.Keys["guildid"].(uint64)
- panels, err := dbclient.Client.Panel.GetByGuild(guildId)
+ panels, err := dbclient.Client.Panel.GetByGuildWithWelcomeMessage(guildId)
if err != nil {
- ctx.AbortWithStatusJSON(500, gin.H{
- "success": false,
- "error": err.Error(),
- })
+ ctx.JSON(500, utils.ErrorJson(err))
+ return
+ }
+
+ allFields, err := dbclient.Client.EmbedFields.GetAllFieldsForPanels(guildId)
+ if err != nil {
+ ctx.JSON(500, utils.ErrorJson(err))
return
}
@@ -75,8 +79,15 @@ func ListPanels(ctx *gin.Context) {
teamIds = make([]int, 0)
}
+ var welcomeMessage *types.CustomEmbed
+ if p.WelcomeMessage != nil {
+ fields := allFields[p.WelcomeMessage.Id]
+ welcomeMessage = types.NewCustomEmbed(p.WelcomeMessage, fields)
+ }
+
wrapped[i] = panelResponse{
- Panel: p,
+ Panel: p.Panel,
+ WelcomeMessage: welcomeMessage,
UseCustomEmoji: p.EmojiId != nil,
Emoji: types.NewEmoji(p.EmojiName, p.EmojiId),
Mentions: mentions,
diff --git a/app/http/endpoints/api/panel/panelupdate.go b/app/http/endpoints/api/panel/panelupdate.go
index 45eb1f8..fb05388 100644
--- a/app/http/endpoints/api/panel/panelupdate.go
+++ b/app/http/endpoints/api/panel/panelupdate.go
@@ -2,6 +2,7 @@ package api
import (
"errors"
+ "fmt"
"github.com/TicketsBot/GoPanel/botcontext"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc"
@@ -105,31 +106,67 @@ func UpdatePanel(ctx *gin.Context) {
}
}
+ // Update welcome message
+ var welcomeMessageEmbed *int
+ if data.WelcomeMessage == nil {
+ if existing.WelcomeMessageEmbed != nil { // If welcome message wasn't null, but now is, delete the embed
+ if err := dbclient.Client.Embeds.Delete(*existing.WelcomeMessageEmbed); err != nil {
+ ctx.JSON(500, utils.ErrorJson(err))
+ return
+ }
+ } // else, welcomeMessageEmbed will be nil
+ } else {
+ // TODO: Upsert? Don't think we can, as no unique key in the table, panel_id is in panels table
+ if existing.WelcomeMessageEmbed == nil { // Create
+ embed, fields := data.WelcomeMessage.IntoDatabaseStruct()
+ embed.GuildId = guildId
+
+ id, err := dbclient.Client.Embeds.CreateWithFields(embed, fields)
+ if err != nil {
+ ctx.JSON(500, utils.ErrorJson(err))
+ return
+ }
+
+ welcomeMessageEmbed = &id
+ } else { // Update
+ welcomeMessageEmbed = existing.WelcomeMessageEmbed
+
+ embed, fields := data.WelcomeMessage.IntoDatabaseStruct()
+ embed.Id = *existing.WelcomeMessageEmbed
+ embed.GuildId = guildId
+
+ if err := dbclient.Client.Embeds.UpdateWithFields(embed, fields); err != nil {
+ ctx.JSON(500, utils.ErrorJson(err))
+ return
+ }
+ }
+ }
+
// Store in DB
panel := database.Panel{
- PanelId: panelId,
- MessageId: newMessageId,
- ChannelId: data.ChannelId,
- GuildId: guildId,
- Title: data.Title,
- Content: data.Content,
- Colour: int32(data.Colour),
- TargetCategory: data.CategoryId,
- EmojiName: emojiName,
- EmojiId: emojiId,
- WelcomeMessage: data.WelcomeMessage,
- WithDefaultTeam: data.WithDefaultTeam,
- CustomId: existing.CustomId,
- ImageUrl: data.ImageUrl,
- ThumbnailUrl: data.ThumbnailUrl,
- ButtonStyle: int(data.ButtonStyle),
- ButtonLabel: data.ButtonLabel,
- FormId: data.FormId,
- NamingScheme: data.NamingScheme,
+ PanelId: panelId,
+ MessageId: newMessageId,
+ ChannelId: data.ChannelId,
+ GuildId: guildId,
+ Title: data.Title,
+ Content: data.Content,
+ Colour: int32(data.Colour),
+ TargetCategory: data.CategoryId,
+ EmojiName: emojiName,
+ EmojiId: emojiId,
+ WelcomeMessageEmbed: welcomeMessageEmbed,
+ WithDefaultTeam: data.WithDefaultTeam,
+ CustomId: existing.CustomId,
+ ImageUrl: data.ImageUrl,
+ ThumbnailUrl: data.ThumbnailUrl,
+ ButtonStyle: int(data.ButtonStyle),
+ ButtonLabel: data.ButtonLabel,
+ FormId: data.FormId,
+ NamingScheme: data.NamingScheme,
}
if err = dbclient.Client.Panel.Update(panel); err != nil {
- ctx.AbortWithStatusJSON(500, utils.ErrorJson(err))
+ ctx.JSON(500, utils.ErrorJson(err))
return
}
@@ -149,7 +186,7 @@ func UpdatePanel(ctx *gin.Context) {
} else {
roleId, err := strconv.ParseUint(mention, 10, 64)
if err != nil {
- ctx.AbortWithStatusJSON(500, utils.ErrorJson(err))
+ ctx.JSON(500, utils.ErrorJson(err))
return
}
@@ -159,6 +196,7 @@ func UpdatePanel(ctx *gin.Context) {
}
}
+ fmt.Println(panel.PanelId)
if err := dbclient.Client.PanelUserMention.Set(panel.PanelId, shouldMentionUser); err != nil {
ctx.AbortWithStatusJSON(500, utils.ErrorJson(err))
return
diff --git a/frontend/src/components/Badge.svelte b/frontend/src/components/Badge.svelte
index c46063e..2e14517 100644
--- a/frontend/src/components/Badge.svelte
+++ b/frontend/src/components/Badge.svelte
@@ -12,5 +12,6 @@
font-size: 14px;
padding: 0 4px;
margin-left: 4px;
+ margin-bottom: 5px;
}
\ No newline at end of file
diff --git a/frontend/src/components/Collapsible.svelte b/frontend/src/components/Collapsible.svelte
new file mode 100644
index 0000000..b15b933
--- /dev/null
+++ b/frontend/src/components/Collapsible.svelte
@@ -0,0 +1,80 @@
+
+
+ {#if expanded}
+
+ {:else}
+
+ {/if}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/EmbedBuilder.svelte b/frontend/src/components/EmbedBuilder.svelte
new file mode 100644
index 0000000..10ab8ea
--- /dev/null
+++ b/frontend/src/components/EmbedBuilder.svelte
@@ -0,0 +1,208 @@
+
+
+
+ Embed Builder
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/form/DateTimePicker.svelte b/frontend/src/components/form/DateTimePicker.svelte
new file mode 100644
index 0000000..fc528f3
--- /dev/null
+++ b/frontend/src/components/form/DateTimePicker.svelte
@@ -0,0 +1,42 @@
+
+ {#if label !== undefined}
+
+
+ {label}
+
+
+ {/if}
+
picker.showPicker()}>
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/form/Input.svelte b/frontend/src/components/form/Input.svelte
index c84f776..ebb20fa 100644
--- a/frontend/src/components/form/Input.svelte
+++ b/frontend/src/components/form/Input.svelte
@@ -1,22 +1,23 @@
{#if label !== undefined}
-
- {label}
-
- {#if tooltipText !== undefined}
-
-
- {#if tooltipLink !== undefined}
-
-
-
- {:else}
-
- {/if}
-
-
- {/if}
+
{label}
+ {#if badge !== undefined}
+
{badge}
+ {/if}
+ {#if tooltipText !== undefined}
+
+
+ {#if tooltipLink !== undefined}
+
+
+
+ {:else}
+
+ {/if}
+
+
+ {/if}
{/if}
import Tooltip from 'svelte-tooltip';
+ import Badge from "../Badge.svelte";
export let value;
export let label;
export let placeholder;
+ export let badge;
export let disabled = false;
export let tooltipText = undefined;
diff --git a/frontend/src/components/manage/MultiPanelEditModal.svelte b/frontend/src/components/manage/MultiPanelEditModal.svelte
index 2dc04db..dc57a10 100644
--- a/frontend/src/components/manage/MultiPanelEditModal.svelte
+++ b/frontend/src/components/manage/MultiPanelEditModal.svelte
@@ -69,6 +69,7 @@
.modal-wrapper {
display: flex;
width: 75%;
+ margin: 10% auto auto auto;
}
@media only screen and (max-width: 1280px) {
diff --git a/frontend/src/components/manage/PanelCreationForm.svelte b/frontend/src/components/manage/PanelCreationForm.svelte
index fc733b1..2d06e30 100644
--- a/frontend/src/components/manage/PanelCreationForm.svelte
+++ b/frontend/src/components/manage/PanelCreationForm.svelte
@@ -1,3 +1,9 @@
+{#if welcomeMessageBuilder}
+
+{/if}
+
@@ -68,9 +74,35 @@
class:advanced-settings-hide={!advancedSettings} class:show-overflow={overflowShow}>
-
+
+
Welcome Message
+
+ Open Editor
+ data.welcome_message = null}>Clear
+
+
+
+
Naming Scheme
+
+
+ Use Server Default
+
+
+
+ {#if !data.use_server_default_naming_scheme}
+
+ {/if}
+
+
+
@@ -104,29 +136,6 @@
-
-
-
Naming Scheme
-
-
- Use Server Default
-
-
-
- {#if !data.use_server_default_naming_scheme}
-
- {/if}
-
-
-
-
@@ -137,6 +146,7 @@
import Colour from "../form/Colour.svelte";
import Button from "../Button.svelte";
import ChannelDropdown from "../ChannelDropdown.svelte";
+ import EmbedBuilder from "../EmbedBuilder.svelte";
import {createEventDispatcher, onMount} from 'svelte';
import {colourToInt, intToColour} from "../../js/util";
@@ -145,9 +155,7 @@
import EmojiItem from "../EmojiItem.svelte";
import Select from 'svelte-select';
import Dropdown from "../form/Dropdown.svelte";
- import Checkbox from "../form/Checkbox.svelte";
import Toggle from "svelte-toggle";
- import Slider from "../form/Slider.svelte";
export let guildId;
export let seedDefault = true;
@@ -173,6 +181,17 @@
let selectedTeams = seedDefault ? [{id: 'default', name: 'Default'}] : [];
let selectedMentions = [];
+ let welcomeMessageBuilder = false;
+
+ function openWelcomeMessageBuilder() {
+ welcomeMessageBuilder = true;
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ }
+
+ function closeWelcomeMessageBuilder() {
+ welcomeMessageBuilder = false;
+ }
+
// Replace spaces with dashes in naming scheme as the user types
$: if (data.naming_scheme !== undefined && data.naming_scheme !== null && data.naming_scheme.includes(' ')) {
data.naming_scheme = data.naming_scheme.replaceAll(' ', '-');
@@ -222,10 +241,9 @@
}
}
- function handleWelcomeMessageUpdate() {
- if (data.welcome_message === "") {
- data.welcome_message = null;
- }
+ function handleWelcomeMessageUpdate(e) {
+ data.welcome_message = e.detail;
+ closeWelcomeMessageBuilder();
}
function handleEmojiTypeChange(e) {
diff --git a/frontend/src/components/manage/PanelEditModal.svelte b/frontend/src/components/manage/PanelEditModal.svelte
index a8e1e63..e3ebc5e 100644
--- a/frontend/src/components/manage/PanelEditModal.svelte
+++ b/frontend/src/components/manage/PanelEditModal.svelte
@@ -74,6 +74,7 @@
.modal-wrapper {
display: flex;
width: 75%;
+ margin: 10% auto auto auto;
}
@media only screen and (max-width: 1280px) {
diff --git a/go.mod b/go.mod
index 42de3c3..e6812f2 100644
--- a/go.mod
+++ b/go.mod
@@ -6,14 +6,15 @@ require (
github.com/BurntSushi/toml v0.3.1
github.com/TicketsBot/archiverclient v0.0.0-20220326163414-558fd52746dc
github.com/TicketsBot/common v0.0.0-20220615205931-a6a31e73b52a
- github.com/TicketsBot/database v0.0.0-20220623211222-6d64f44d5cfb
+ github.com/TicketsBot/database v0.0.0-20220627203133-dd19fe34f094
github.com/TicketsBot/logarchiver v0.0.0-20220326162808-cdf0310f5e1c
- github.com/TicketsBot/worker v0.0.0-20220621165800-203b0004b733
+ github.com/TicketsBot/worker v0.0.0-20220627203254-f37bdb40b39a
github.com/apex/log v1.1.2
github.com/getsentry/sentry-go v0.13.0
github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2
github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607
github.com/gin-gonic/gin v1.7.7
+ github.com/go-playground/validator/v10 v10.11.0
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-redis/redis/v8 v8.11.3
github.com/go-redis/redis_rate/v9 v9.1.1
@@ -39,9 +40,8 @@ require (
github.com/getsentry/raven-go v0.2.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-errors/errors v1.1.0 // indirect
- github.com/go-playground/locales v0.13.0 // indirect
- github.com/go-playground/universal-translator v0.17.0 // indirect
- github.com/go-playground/validator/v10 v10.6.1 // indirect
+ github.com/go-playground/locales v0.14.0 // indirect
+ github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/gofrs/uuid v3.3.0+incompatible // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
@@ -60,7 +60,7 @@ require (
github.com/juju/ratelimit v1.0.1 // indirect
github.com/klauspost/compress v1.10.10 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
- github.com/leodido/go-urn v1.2.0 // indirect
+ github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
@@ -71,7 +71,7 @@ require (
github.com/tatsuworks/czlib v0.0.0-20190916144400-8a51758ea0d9 // indirect
github.com/ugorji/go/codec v1.2.6 // indirect
go.uber.org/atomic v1.6.0 // indirect
- golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
+ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
golang.org/x/text v0.3.7 // indirect
diff --git a/go.sum b/go.sum
index 1edc1f2..f091044 100644
--- a/go.sum
+++ b/go.sum
@@ -5,18 +5,15 @@ github.com/TicketsBot/archiverclient v0.0.0-20220326163414-558fd52746dc h1:n15W8
github.com/TicketsBot/archiverclient v0.0.0-20220326163414-558fd52746dc/go.mod h1:2KcfHS0JnSsgcxZBs3NyWMXNQzEo67mBSGOyzHPWOCc=
github.com/TicketsBot/common v0.0.0-20220615205931-a6a31e73b52a h1:SwA18cDURmnXSrKBdetNVanSsyJBMtyosDzvgYMpKP4=
github.com/TicketsBot/common v0.0.0-20220615205931-a6a31e73b52a/go.mod h1:ZAoYcDD7SQLTsZT7dbo/X0J256+pogVRAReunCGng+U=
-github.com/TicketsBot/database v0.0.0-20220623160906-a56dc9dfda90 h1:K0t6IaZdeZzEr2BaYj/NBuWIm/hA31jkqFh2c3nyDrw=
-github.com/TicketsBot/database v0.0.0-20220623160906-a56dc9dfda90/go.mod h1:F57cywrZsnper1cy56Bx0c/HEsxQBLHz3Pl98WXblWw=
-github.com/TicketsBot/database v0.0.0-20220623203124-d335941a5a86 h1:oCynNT5m2nP5y+rf52/tGGhRGI85C3YSaoI4Ox3RuLs=
-github.com/TicketsBot/database v0.0.0-20220623203124-d335941a5a86/go.mod h1:F57cywrZsnper1cy56Bx0c/HEsxQBLHz3Pl98WXblWw=
-github.com/TicketsBot/database v0.0.0-20220623211222-6d64f44d5cfb h1:nLL2HT+RZVnGIi474Al+xdLiBH9Wsr287m7tBAePJQY=
-github.com/TicketsBot/database v0.0.0-20220623211222-6d64f44d5cfb/go.mod h1:F57cywrZsnper1cy56Bx0c/HEsxQBLHz3Pl98WXblWw=
+github.com/TicketsBot/database v0.0.0-20220627203133-dd19fe34f094 h1:a272Wj4At5XjIjLoRAdn4PwfZ288+A4QzwTvFAR6fho=
+github.com/TicketsBot/database v0.0.0-20220627203133-dd19fe34f094/go.mod h1:F57cywrZsnper1cy56Bx0c/HEsxQBLHz3Pl98WXblWw=
github.com/TicketsBot/logarchiver v0.0.0-20220326162808-cdf0310f5e1c h1:OqGjFH6mbE6gd+NqI2ARJdtH3UUvhiAkD0r0fhGJK2s=
github.com/TicketsBot/logarchiver v0.0.0-20220326162808-cdf0310f5e1c/go.mod h1:jgi2OXQKsd5nUnTIRkwvPmeuD/i7OhN68LKMssuQY1c=
github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261 h1:NHD5GB6cjlkpZFjC76Yli2S63/J2nhr8MuE6KlYJpQM=
github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261/go.mod h1:2zPxDAN2TAPpxUPjxszjs3QFKreKrQh5al/R3cMXmYk=
-github.com/TicketsBot/worker v0.0.0-20220621165800-203b0004b733 h1:G4iUjk4lgGRvbRCSJshpwuQetl+z3v7PmHzIW+HII9k=
github.com/TicketsBot/worker v0.0.0-20220621165800-203b0004b733/go.mod h1:fhEqdxVhgXds/xB3ql05XSDqPLF4XWjkfyHr+JQFe0g=
+github.com/TicketsBot/worker v0.0.0-20220627203254-f37bdb40b39a h1:cx2YtngcJ7KpOBm/QotWKHb2XZbvUDJwD9azBl31e/k=
+github.com/TicketsBot/worker v0.0.0-20220627203254-f37bdb40b39a/go.mod h1:R70+F86Z+UlretKMxOX1jRqCwvVlvuem9UAyAL4EiG8=
github.com/apex/log v1.1.2 h1:bnDuVoi+o98wOdVqfEzNDlY0tcmBia7r4YkjS9EqGYk=
github.com/apex/log v1.1.2/go.mod h1:SyfRweFO+TlkIJ3DVizTSeI1xk7jOIIqOnUPZQTTsww=
github.com/apex/logs v0.0.3/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
@@ -37,6 +34,7 @@ github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMe
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -68,14 +66,16 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
-github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
+github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
-github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
+github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
-github.com/go-playground/validator/v10 v10.6.1 h1:W6TRDXt4WcWp4c4nf/G+6BkGdhiIo0k417gfr+V6u4I=
-github.com/go-playground/validator/v10 v10.6.1/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
+github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
+github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.3.4/go.mod h1:jszGxBCez8QA1HWSmQxJO9Y82kNibbUmeYhKWrBejTU=
@@ -200,13 +200,18 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
-github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
+github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@@ -234,7 +239,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -252,6 +256,7 @@ github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+t
github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c h1:Gcce/r5tSQeprxswXXOwQ/RBU1bjQWVd9dB7QKoPXBE=
github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c/go.mod h1:1iCZ0433JJMecYqCa+TdWA9Pax8MGl4ByuNDZ7eSnQY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -261,6 +266,9 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
@@ -289,6 +297,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tatsuworks/czlib v0.0.0-20190916144400-8a51758ea0d9 h1:i2aD44Moa5N5pt/WNwHLvIklzPymtr8vkkBlVdNElUE=
github.com/tatsuworks/czlib v0.0.0-20190916144400-8a51758ea0d9/go.mod h1:6HrfShlf4bKeQEFdWn4JP/yet/mHW2RhxOQf0e3HWA0=
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
@@ -324,8 +333,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
+golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -340,7 +349,8 @@ golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
-golang.org/x/net v0.0.0-20211008194852-3b03d305991f h1:1scJEYZBaF48BaG6tYbtxmLcXqwYGSfGcMoStTqkkIw=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
@@ -368,6 +378,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -411,7 +422,8 @@ gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVC
gopkg.in/alexcesaro/statsd.v2 v2.0.0/go.mod h1:i0ubccKGzBVNBpdGV5MocxyA/XlLUJzA7SLonnE4drU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
@@ -428,6 +440,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
nhooyr.io/websocket v1.8.4 h1:P43INlkmY2eCxLvHeiMFK/ROUiOm0NdzRGGDtURbe58=
nhooyr.io/websocket v1.8.4/go.mod h1:LiqdCg1Cu7TPWxEvPjPa0TGYxCsy4pHNTN9gGluwBpQ=
diff --git a/utils/types/colour.go b/utils/types/colour.go
new file mode 100644
index 0000000..da489cf
--- /dev/null
+++ b/utils/types/colour.go
@@ -0,0 +1,38 @@
+package types
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+type Colour uint32
+
+func (c Colour) Uint32() uint32 {
+ return uint32(c)
+}
+
+func (c Colour) MarshalJSON() ([]byte, error) {
+ return []byte(fmt.Sprintf(`"#%06x"`, c)), nil
+}
+
+func (c *Colour) UnmarshalJSON(b []byte) error {
+ if len(b) < 2 {
+ return fmt.Errorf("invalid colour")
+ }
+
+ s := string(b)[1 : len(b)-1] // Trim quotes
+ s = strings.TrimPrefix(s, `#`) // Trim # if present
+
+ tmp, err := strconv.ParseUint(s, 16, 32)
+ if err != nil {
+ return err
+ }
+
+ if tmp > 0xFFFFFF {
+ return fmt.Errorf("colour value %v is out of range", tmp)
+ }
+
+ *c = Colour(tmp)
+ return nil
+}
diff --git a/utils/types/customembed.go b/utils/types/customembed.go
new file mode 100644
index 0000000..3918c48
--- /dev/null
+++ b/utils/types/customembed.go
@@ -0,0 +1,141 @@
+package types
+
+import (
+ "github.com/TicketsBot/GoPanel/utils"
+ "github.com/TicketsBot/database"
+ "github.com/rxdn/gdl/objects/channel/embed"
+)
+
+type CustomEmbed struct {
+ Title *string `json:"title" validate:"omitempty,min=1,max=255"`
+ Description *string `json:"description" validate:"omitempty,min=1,max=4096"`
+ Url *string `json:"url" validate:"omitempty,url"`
+ Colour Colour `json:"colour" validate:"required,gte=0,lte=16777215"`
+ Author Author `json:"author" validate:"dive"`
+ ImageUrl *string `json:"image_url" validate:"omitempty,url"`
+ ThumbnailUrl *string `json:"thumbnail_url" validate:"omitempty,url"`
+ Footer Footer `json:"footer" validate:"dive"`
+ Timestamp *DateTimeLocal `json:"timestamp" validate:"omitempty"`
+ Fields []Field `json:"fields" validate:"dive,max=25"`
+}
+
+type Author struct {
+ Name *string `json:"name" validate:"omitempty,min=1,max=255"`
+ IconUrl *string `json:"icon_url" validate:"omitempty,url"`
+ Url *string `json:"url" validate:"omitempty,url"`
+}
+
+type Footer struct {
+ Text *string `json:"text" validate:"omitempty,min=1,max=2048"`
+ IconUrl *string `json:"icon_url" validate:"omitempty,url"`
+}
+
+type Field struct {
+ Name string `json:"name" validate:"min=1,max=255"`
+ Value string `json:"value" validate:"min=1,max=1024"`
+ Inline bool `json:"inline"`
+}
+
+func NewCustomEmbed(c *database.CustomEmbed, fields []database.EmbedField) *CustomEmbed {
+ wrappedFields := make([]Field, len(fields))
+ for i, field := range fields {
+ wrappedFields[i] = Field{
+ Name: field.Name,
+ Value: field.Value,
+ Inline: field.Inline,
+ }
+ }
+
+ return &CustomEmbed{
+ Title: c.Title,
+ Description: c.Description,
+ Url: c.Url,
+ Colour: Colour(c.Colour),
+ Author: Author{
+ Name: c.AuthorName,
+ IconUrl: c.AuthorIconUrl,
+ Url: c.AuthorUrl,
+ },
+ ImageUrl: c.ImageUrl,
+ ThumbnailUrl: c.ThumbnailUrl,
+ Footer: Footer{
+ Text: c.FooterText,
+ IconUrl: c.FooterIconUrl,
+ },
+ Timestamp: NewDateTimeLocalFromPtr(c.Timestamp),
+ Fields: wrappedFields,
+ }
+}
+
+func (c *CustomEmbed) IntoDatabaseStruct() (*database.CustomEmbed, []database.EmbedField) {
+ fields := make([]database.EmbedField, len(c.Fields))
+ for i, field := range c.Fields {
+ fields[i] = database.EmbedField{
+ Name: field.Name,
+ Value: field.Value,
+ Inline: field.Inline,
+ }
+ }
+
+ return &database.CustomEmbed{
+ Title: c.Title,
+ Description: c.Description,
+ Url: c.Url,
+ Colour: uint32(c.Colour),
+ AuthorName: c.Author.Name,
+ AuthorIconUrl: c.Author.IconUrl,
+ AuthorUrl: c.Author.Url,
+ ImageUrl: c.ImageUrl,
+ ThumbnailUrl: c.ThumbnailUrl,
+ FooterText: c.Footer.Text,
+ FooterIconUrl: c.Footer.IconUrl,
+ Timestamp: TimeOrNil(c.Timestamp),
+ }, fields
+}
+
+func (c *CustomEmbed) IntoDiscordEmbed() *embed.Embed {
+ e := &embed.Embed{
+ Title: utils.ValueOrZero(c.Title),
+ Description: utils.ValueOrZero(c.Description),
+ Url: utils.ValueOrZero(c.Url),
+ Timestamp: TimeOrNil(c.Timestamp),
+ Color: int(c.Colour),
+ }
+
+ if c.Footer.Text != nil {
+ e.Footer = &embed.EmbedFooter{
+ Text: *c.Footer.Text,
+ IconUrl: utils.ValueOrZero(c.Footer.IconUrl),
+ }
+ }
+
+ if c.ImageUrl != nil {
+ e.SetImage(*c.ImageUrl)
+ }
+
+ if c.ThumbnailUrl != nil {
+ e.SetThumbnail(*c.ThumbnailUrl)
+ }
+
+ if c.Author.Name != nil {
+ e.Author = &embed.EmbedAuthor{
+ Name: *c.Author.Name,
+ IconUrl: utils.ValueOrZero(c.Author.IconUrl),
+ Url: utils.ValueOrZero(c.Author.Url),
+ }
+ }
+
+ if len(c.Fields) > 0 {
+ e.Fields = make([]*embed.EmbedField, len(c.Fields))
+
+ for i, field := range c.Fields {
+ e.Fields[i] = &embed.EmbedField{
+ Name: field.Name,
+ Value: field.Value,
+ Inline: field.Inline,
+ }
+ }
+ }
+
+ return e
+}
diff --git a/utils/types/datetimelocal.go b/utils/types/datetimelocal.go
new file mode 100644
index 0000000..66026a6
--- /dev/null
+++ b/utils/types/datetimelocal.go
@@ -0,0 +1,49 @@
+package types
+
+import (
+ "fmt"
+ "github.com/TicketsBot/GoPanel/utils"
+ "time"
+)
+
+type DateTimeLocal time.Time
+
+var format = "2006-01-02T15:04"
+
+func NewDateTimeLocalFromPtr(t *time.Time) *DateTimeLocal {
+ if t == nil {
+ return nil
+ }
+
+ return utils.Ptr(DateTimeLocal(*t))
+}
+
+func TimeOrNil(d *DateTimeLocal) *time.Time {
+ if d == nil {
+ return nil
+ }
+
+ return utils.Ptr(d.Time())
+}
+
+func (d DateTimeLocal) Time() time.Time {
+ return time.Time(d)
+}
+
+func (d DateTimeLocal) MarshalJSON() ([]byte, error) {
+ return []byte(fmt.Sprintf(`"%s"`, time.Time(d).Format(format))), nil
+}
+
+func (d *DateTimeLocal) UnmarshalJSON(b []byte) error {
+ if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
+ return fmt.Errorf("invalid DateTimeLocal")
+ }
+
+ tmp, err := time.Parse(format, string(b[1:len(b)-1]))
+ if err != nil {
+ return err
+ }
+
+ *d = DateTimeLocal(tmp)
+ return nil
+}
diff --git a/utils/utils.go b/utils/utils.go
index 947538c..4386137 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -3,3 +3,11 @@ package utils
func Ptr[T any](v T) *T {
return &v
}
+
+func ValueOrZero[T any](v *T) T {
+ if v == nil {
+ return *new(T)
+ } else {
+ return *v
+ }
+}