feat: Importing from v1

Signed-off-by: Ben Hall <ben@benh.codes>
This commit is contained in:
Ben Hall 2025-02-09 12:34:15 +00:00
parent d32d07287b
commit 8e99630165
20 changed files with 1435 additions and 72 deletions

View File

@ -0,0 +1,762 @@
package api
import (
"bytes"
"context"
"crypto/ed25519"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"os"
"github.com/TicketsBot/GoPanel/app/http/endpoints/api/export/validator"
"github.com/TicketsBot/GoPanel/botcontext"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/common/premium"
"github.com/TicketsBot/database"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)
func ImportHandler(ctx *gin.Context) {
// Parse request body from multipart form
var transcriptOutput *validator.GuildTranscriptsOutput
var data *validator.GuildData
dataFile, _, err := ctx.Request.FormFile("data_file")
dataFileExists := err == nil
transcriptsFile, _, err := ctx.Request.FormFile("transcripts_file")
transcriptFileExists := err == nil
// Decrypt file
publicKeyBlock, _ := pem.Decode([]byte(os.Getenv("V1_PUBLIC_KEY")))
if publicKeyBlock == nil {
ctx.JSON(400, utils.ErrorStr("Invalid public key"))
return
}
parsedKey, err := x509.ParsePKIXPublicKey(publicKeyBlock.Bytes)
if err != nil {
ctx.JSON(400, utils.ErrorJson(err))
return
}
decryptedPublicKey, ok := parsedKey.(ed25519.PublicKey)
if !ok {
ctx.JSON(400, utils.ErrorStr("Invalid public key"))
return
}
v := validator.NewValidator(
decryptedPublicKey,
validator.WithMaxUncompressedSize(250*1024*1024),
validator.WithMaxIndividualFileSize(1*1024*1024),
)
if dataFileExists {
defer dataFile.Close()
dataBuffer := bytes.NewBuffer(nil)
if _, err := io.Copy(dataBuffer, dataFile); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
dataReader := bytes.NewReader(dataBuffer.Bytes())
data, err = v.ValidateGuildData(dataReader, dataReader.Size())
if err != nil {
ctx.JSON(400, utils.ErrorJson(err))
return
}
}
if transcriptFileExists {
defer transcriptsFile.Close()
transcriptsBuffer := bytes.NewBuffer(nil)
if _, err := io.Copy(transcriptsBuffer, transcriptsFile); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
transcriptReader := bytes.NewReader(transcriptsBuffer.Bytes())
transcriptOutput, err = v.ValidateGuildTranscripts(transcriptReader, transcriptReader.Size())
if err != nil {
ctx.JSON(400, utils.ErrorJson(err))
return
}
}
guildId, selfId := ctx.Keys["guildid"].(uint64), ctx.Keys["userid"].(uint64)
botCtx, err := botcontext.ContextForGuild(guildId)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
guild, err := botCtx.GetGuild(context.Background(), guildId)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
if guild.OwnerId != selfId {
ctx.JSON(403, utils.ErrorStr("Only the server owner import server data"))
return
}
if dataFileExists && data.GuildId != guildId || transcriptFileExists && transcriptOutput.GuildId != guildId {
ctx.JSON(400, utils.ErrorStr("Invalid guild Id"))
return
}
premiumTier, err := rpc.PremiumClient.GetTierByGuildId(ctx, guildId, true, botCtx.Token, botCtx.RateLimiter)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
// Get ticket maps
mapping, err := dbclient.Client2.ImportMappingTable.GetMapping(ctx, guildId)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
var (
ticketIdMap = mapping["ticket"]
formIdMap = mapping["form"]
formInputIdMap = mapping["form_input"]
)
if ticketIdMap == nil {
ticketIdMap = make(map[int]int)
}
if formIdMap == nil {
formIdMap = make(map[int]int)
}
if formInputIdMap == nil {
formInputIdMap = make(map[int]int)
}
if dataFileExists {
group, _ := errgroup.WithContext(ctx)
// Import active language
group.Go(func() (err error) {
lang := "en"
if data.ActiveLanguage != nil {
lang = *data.ActiveLanguage
}
_ = dbclient.Client.ActiveLanguage.Set(ctx, guildId, lang)
return
})
// Import archive channel
group.Go(func() (err error) {
if data.ArchiveChannel != nil {
err = dbclient.Client.ArchiveChannel.Set(ctx, guildId, data.ArchiveChannel)
}
return
})
// import AutocloseSettings
group.Go(func() (err error) {
if data.AutocloseSettings != nil {
if premiumTier < premium.Premium {
data.AutocloseSettings.Enabled = false
}
err = dbclient.Client.AutoClose.Set(ctx, guildId, *data.AutocloseSettings)
}
return
})
// Import blacklisted users
group.Go(func() (err error) {
for _, user := range data.GuildBlacklistedUsers {
err = dbclient.Client.Blacklist.Add(ctx, guildId, user)
if err != nil {
return
}
}
return
})
// Import channel category
group.Go(func() (err error) {
if data.ChannelCategory != nil {
err = dbclient.Client.ChannelCategory.Set(ctx, guildId, *data.ChannelCategory)
}
return
})
// Import claim settings
group.Go(func() (err error) {
if data.ClaimSettings != nil {
err = dbclient.Client.ClaimSettings.Set(ctx, guildId, *data.ClaimSettings)
}
return
})
// Import close confirmation enabled
group.Go(func() (err error) {
err = dbclient.Client.CloseConfirmation.Set(ctx, guildId, data.CloseConfirmationEnabled)
return
})
// Import custom colours
group.Go(func() (err error) {
if premiumTier < premium.Premium {
return
}
for k, v := range data.CustomColors {
err = dbclient.Client.CustomColours.Set(ctx, guildId, k, v)
if err != nil {
return
}
}
return
})
// Import feedback enabled
group.Go(func() (err error) {
err = dbclient.Client.FeedbackEnabled.Set(ctx, guildId, data.FeedbackEnabled)
return
})
// Import is globally blacklisted
group.Go(func() (err error) {
if data.GuildIsGloballyBlacklisted {
reason := "Blacklisted on v1"
err = dbclient.Client.ServerBlacklist.Add(ctx, guildId, &reason)
}
return
})
// Import Guild Metadata
group.Go(func() (err error) {
err = dbclient.Client.GuildMetadata.Set(ctx, guildId, data.GuildMetadata)
return
})
// Import Naming Scheme
group.Go(func() (err error) {
if data.NamingScheme != nil {
err = dbclient.Client.NamingScheme.Set(ctx, guildId, *data.NamingScheme)
}
return
})
// Import On Call Users
group.Go(func() (err error) {
for _, user := range data.OnCallUsers {
if isOnCall, oncallerr := dbclient.Client.OnCall.IsOnCall(ctx, guildId, user); oncallerr != nil {
return oncallerr
} else if !isOnCall {
_, err = dbclient.Client.OnCall.Toggle(ctx, guildId, user)
if err != nil {
return
}
}
}
return
})
// Import User Permissions
group.Go(func() (err error) {
for _, perm := range data.UserPermissions {
if perm.IsSupport {
err = dbclient.Client.Permissions.AddSupport(ctx, guildId, perm.Snowflake)
}
if perm.IsAdmin {
err = dbclient.Client.Permissions.AddAdmin(ctx, guildId, perm.Snowflake)
}
if err != nil {
return
}
}
return
})
// Import Guild Blacklisted Roles
group.Go(func() (err error) {
for _, role := range data.GuildBlacklistedRoles {
err = dbclient.Client.RoleBlacklist.Add(ctx, guildId, role)
if err != nil {
return
}
}
return
})
// Import Role Permissions
group.Go(func() (err error) {
for _, perm := range data.RolePermissions {
if perm.IsSupport {
err = dbclient.Client.RolePermissions.AddSupport(ctx, guildId, perm.Snowflake)
}
if perm.IsAdmin {
err = dbclient.Client.RolePermissions.AddAdmin(ctx, guildId, perm.Snowflake)
}
if err != nil {
return
}
}
return
})
// Import Tags
group.Go(func() (err error) {
for _, tag := range data.Tags {
err = dbclient.Client.Tag.Set(ctx, tag)
if err != nil {
return
}
}
return
})
// Import Ticket Limit
group.Go(func() (err error) {
if data.TicketLimit != nil {
err = dbclient.Client.TicketLimit.Set(ctx, guildId, uint8(*data.TicketLimit))
}
return
})
// Import Ticket Permissions
group.Go(func() (err error) {
err = dbclient.Client.TicketPermissions.Set(ctx, guildId, data.TicketPermissions)
return
})
// Import Users Can Close
group.Go(func() (err error) {
err = dbclient.Client.UsersCanClose.Set(ctx, guildId, data.UsersCanClose)
return
})
// Import Welcome Message
group.Go(func() (err error) {
if data.WelcomeMessage != nil {
err = dbclient.Client.WelcomeMessages.Set(ctx, guildId, *data.WelcomeMessage)
}
return
})
if err := group.Wait(); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
supportTeamIdMap := make(map[int]int)
// Import Support Teams
for _, team := range data.SupportTeams {
teamId, err := dbclient.Client.SupportTeam.Create(ctx, guildId, team.Name)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
supportTeamIdMap[team.Id] = teamId
}
// Import Support Team Users
for teamId, users := range data.SupportTeamUsers {
for _, user := range users {
if err := dbclient.Client.SupportTeamMembers.Add(ctx, supportTeamIdMap[teamId], user); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
}
// Import Support Team Roles
for teamId, roles := range data.SupportTeamRoles {
for _, role := range roles {
if err := dbclient.Client.SupportTeamRoles.Add(ctx, supportTeamIdMap[teamId], role); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
}
// Import forms
for _, form := range data.Forms {
if _, ok := formIdMap[form.Id]; !ok {
formId, err := dbclient.Client.Forms.Create(ctx, guildId, form.Title, form.CustomId)
if err != nil {
return
}
formIdMap[form.Id] = formId
}
}
// Import form inputs
for _, input := range data.FormInputs {
if _, ok := formInputIdMap[input.Id]; !ok {
newInputId, err := dbclient.Client.FormInput.Create(ctx, formIdMap[input.FormId], input.CustomId, input.Style, input.Label, input.Placeholder, input.Required, input.MinLength, input.MaxLength)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
formInputIdMap[input.Id] = newInputId
}
}
embedMap := make(map[int]int)
// Import embeds
for _, embed := range data.Embeds {
var embedFields []database.EmbedField
for _, field := range data.EmbedFields {
if field.EmbedId == embed.Id {
embedFields = append(embedFields, field)
}
}
embed.GuildId = guildId
embedId, err := dbclient.Client.Embeds.CreateWithFields(ctx, &embed, embedFields)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
fmt.Println("Embed ID", embed.Id, "New ID", embedId)
embedMap[embed.Id] = embedId
}
// Panel id map
existingPanels, err := dbclient.Client.Panel.GetByGuild(ctx, guildId)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
panelTx, err := dbclient.Client.Panel.Begin(ctx)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
panelCount := len(existingPanels)
panelIdMap := make(map[int]int)
for _, panel := range data.Panels {
if premiumTier < premium.Premium && panelCount > 2 {
panel.ForceDisabled = true
panel.Disabled = true
}
if panel.FormId != nil {
newFormId := formIdMap[*panel.FormId]
panel.FormId = &newFormId
}
if panel.ExitSurveyFormId != nil {
newFormId := formIdMap[*panel.ExitSurveyFormId]
panel.ExitSurveyFormId = &newFormId
}
if panel.WelcomeMessageEmbed != nil {
fmt.Println(*panel.WelcomeMessageEmbed)
newEmbedId := embedMap[*panel.WelcomeMessageEmbed]
fmt.Println(newEmbedId)
panel.WelcomeMessageEmbed = &newEmbedId
}
panelId, err := dbclient.Client.Panel.CreateWithTx(ctx, panelTx, panel)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
panelIdMap[panel.PanelId] = panelId
panelCount++
}
// Import Panel Access Control Rules
for panelId, rules := range data.PanelAccessControlRules {
if err := dbclient.Client.PanelAccessControlRules.ReplaceWithTx(ctx, panelTx, panelIdMap[panelId], rules); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
// Import Panel Mention User
for panelId, shouldMention := range data.PanelMentionUser {
if err := dbclient.Client.PanelUserMention.SetWithTx(ctx, panelTx, panelIdMap[panelId], shouldMention); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
// Import Panel Role Mentions
for panelId, roles := range data.PanelRoleMentions {
if err := dbclient.Client.PanelRoleMentions.ReplaceWithTx(ctx, panelTx, panelIdMap[panelId], roles); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
// Import Panel Teams
for panelId, teams := range data.PanelTeams {
teamsToAdd := make([]int, len(teams))
for _, team := range teams {
teamsToAdd = append(teamsToAdd, supportTeamIdMap[team])
}
if err := dbclient.Client.PanelTeams.ReplaceWithTx(ctx, panelTx, panelIdMap[panelId], teamsToAdd); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
if err := panelTx.Commit(ctx); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
// Import Multi panels
multiPanelIdMap := make(map[int]int)
for _, multiPanel := range data.MultiPanels {
multiPanelId, err := dbclient.Client.MultiPanels.Create(ctx, multiPanel)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
multiPanelIdMap[multiPanel.Id] = multiPanelId
}
// Import Multi Panel Targets
for multiPanelId, panelIds := range data.MultiPanelTargets {
for _, panelId := range panelIds {
if err := dbclient.Client.MultiPanelTargets.Insert(ctx, multiPanelIdMap[multiPanelId], panelIdMap[panelId]); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
}
newContextMenuPanel := panelIdMap[*data.Settings.ContextMenuPanel]
data.Settings.ContextMenuPanel = &newContextMenuPanel
if err := dbclient.Client.Settings.Set(ctx, guildId, data.Settings); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
// Import tickets
for _, ticket := range data.Tickets {
if _, ok := ticketIdMap[ticket.Id]; !ok {
newPanelId := panelIdMap[*ticket.PanelId]
newTicketId, err := dbclient.Client.Tickets.Create(ctx, guildId, ticket.UserId, ticket.IsThread, &newPanelId)
if err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
ticketIdMap[ticket.Id] = newTicketId
if ticket.Open {
if err := dbclient.Client.Tickets.SetOpen(ctx, guildId, newTicketId); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
} else {
if err := dbclient.Client.Tickets.Close(ctx, newTicketId, guildId); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
if err := dbclient.Client.Tickets.SetChannelId(ctx, guildId, newTicketId, *ticket.ChannelId); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
if err := dbclient.Client.Tickets.SetHasTranscript(ctx, guildId, newTicketId, ticket.HasTranscript); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
if ticket.NotesThreadId != nil {
if err := dbclient.Client.Tickets.SetNotesThreadId(ctx, guildId, newTicketId, *ticket.NotesThreadId); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
if err := dbclient.Client.Tickets.SetStatus(ctx, guildId, newTicketId, ticket.Status); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
}
}
// Upload transcripts
if transcriptFileExists {
for ticketId, transcript := range transcriptOutput.Transcripts {
if err := utils.ArchiverClient.ImportTranscript(ctx, guildId, ticketIdMap[ticketId], transcript); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
}
if dataFileExists {
ticketsExtrasGroup, _ := errgroup.WithContext(ctx)
ticketsExtrasGroup.Go(func() (err error) {
// Import ticket additional members
for ticketId, members := range data.TicketAdditionalMembers {
for _, member := range members {
err = dbclient.Client.TicketMembers.Add(ctx, guildId, ticketIdMap[ticketId], member)
return
}
}
return
})
// Import ticket last messages
ticketsExtrasGroup.Go(func() (err error) {
for _, msg := range data.TicketLastMessages {
err = dbclient.Client.TicketLastMessage.Set(ctx, guildId, ticketIdMap[msg.TicketId], *msg.Data.LastMessageId, *msg.Data.UserId, *msg.Data.UserIsStaff)
}
return
})
// Import ticket claims
ticketsExtrasGroup.Go(func() (err error) {
for _, claim := range data.TicketClaims {
err = dbclient.Client.TicketClaims.Set(ctx, guildId, ticketIdMap[claim.TicketId], claim.Data)
}
return
})
// Import ticket ratings
ticketsExtrasGroup.Go(func() (err error) {
for _, rating := range data.ServiceRatings {
err = dbclient.Client.ServiceRatings.Set(ctx, guildId, ticketIdMap[rating.TicketId], uint8(rating.Data))
}
return
})
// Import participants
ticketsExtrasGroup.Go(func() (err error) {
for ticketId, participants := range data.Participants {
err = dbclient.Client.Participants.SetBulk(ctx, guildId, ticketIdMap[ticketId], participants)
}
return
})
// Import First Response Times
ticketsExtrasGroup.Go(func() (err error) {
for _, frt := range data.FirstResponseTimes {
err = dbclient.Client.FirstResponseTime.Set(ctx, guildId, frt.UserId, ticketIdMap[frt.TicketId], frt.ResponseTime)
}
return
})
ticketsExtrasGroup.Go(func() (err error) {
for _, response := range data.ExitSurveyResponses {
resps := map[int]string{
*response.Data.QuestionId: *response.Data.Response,
}
err = dbclient.Client.ExitSurveyResponses.AddResponses(ctx, guildId, ticketIdMap[response.TicketId], formIdMap[*response.Data.FormId], resps)
}
return
})
// Import Close Reasons
ticketsExtrasGroup.Go(func() (err error) {
for _, reason := range data.CloseReasons {
err = dbclient.Client.CloseReason.Set(ctx, guildId, ticketIdMap[reason.TicketId], reason.Data)
}
return
})
// Import Autoclose Excluded Tickets
ticketsExtrasGroup.Go(func() (err error) {
for _, ticketId := range data.AutocloseExcluded {
err = dbclient.Client.AutoCloseExclude.Exclude(ctx, guildId, ticketIdMap[ticketId])
}
return
})
// Import Archive Messages
ticketsExtrasGroup.Go(func() (err error) {
for _, message := range data.ArchiveMessages {
err = dbclient.Client.ArchiveMessages.Set(ctx, guildId, ticketIdMap[message.TicketId], message.Data.ChannelId, message.Data.MessageId)
}
return
})
if err := ticketsExtrasGroup.Wait(); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
// Imported successfully, update the import mapping
newMapping := make(map[string]map[int]int)
newMapping["ticket"] = ticketIdMap
newMapping["form"] = formIdMap
newMapping["form_input"] = formInputIdMap
for area, m := range newMapping {
for sourceId, targetId := range m {
if err := dbclient.Client2.ImportMappingTable.Set(ctx, guildId, area, sourceId, targetId); err != nil {
ctx.JSON(500, utils.ErrorJson(err))
return
}
}
}
ctx.JSON(200, utils.SuccessResponse)
}

View File

@ -0,0 +1 @@
package api

View File

@ -0,0 +1,8 @@
package validator
import "errors"
var (
ErrValidationFailed = errors.New("validation failed")
ErrMaximumSizeExceeded = errors.New("maximum size exceeded")
)

View File

@ -0,0 +1,37 @@
package validator
import (
"archive/zip"
"encoding/json"
"io"
)
func (v *Validator) ValidateGuildData(input io.ReaderAt, size int64) (*GuildData, error) {
reader, err := zip.NewReader(input, size)
if err != nil {
return nil, err
}
f, err := reader.Open("data.json")
if err != nil {
return nil, err
}
defer f.Close()
data, err := io.ReadAll(v.newLimitReader(f))
if err != nil {
return nil, err
}
if _, err := v.validateSignature(reader, "data.json", data); err != nil {
return nil, err
}
var guildData GuildData
if err := json.Unmarshal(data, &guildData); err != nil {
return nil, err
}
return &guildData, nil
}

View File

@ -0,0 +1,97 @@
package validator
import (
"archive/zip"
"io"
"regexp"
"strconv"
)
type GuildTranscriptsOutput struct {
GuildId uint64
// Ticket ID -> Transcript
Transcripts map[int][]byte
}
var transcriptFileRegex = regexp.MustCompile(`^transcripts/(\d+)\.json$`)
func (v *Validator) ValidateGuildTranscripts(input io.ReaderAt, size int64) (*GuildTranscriptsOutput, error) {
reader, err := zip.NewReader(input, size)
if err != nil {
return nil, err
}
guildId, n, err := v.readGuildId(reader)
if err != nil {
return nil, err
}
transcripts := make(map[int][]byte)
for _, f := range reader.File {
matches := transcriptFileRegex.FindStringSubmatch(f.Name)
if len(matches) != 2 {
continue
}
ticketId, err := strconv.Atoi(matches[1])
if err != nil {
continue
}
file, err := f.Open()
if err != nil {
return nil, err
}
b, err := io.ReadAll(v.newLimitReader(file))
if err != nil {
return nil, err
}
n += int64(len(b))
if n > v.maxUncompressedSize {
return nil, ErrMaximumSizeExceeded
}
sigSize, err := v.validateTranscriptSignature(reader, f.Name, guildId, ticketId, b)
if err != nil {
return nil, err
}
n += sigSize
if n > v.maxUncompressedSize {
return nil, ErrMaximumSizeExceeded
}
transcripts[ticketId] = b
}
return &GuildTranscriptsOutput{
GuildId: guildId,
Transcripts: transcripts,
}, nil
}
func (v *Validator) readGuildId(reader *zip.Reader) (uint64, int64, error) {
f, err := reader.Open("guild_id.txt")
if err != nil {
return 0, 0, err
}
b, err := io.ReadAll(v.newLimitReader(f))
if err != nil {
return 0, 0, err
}
guildId, err := strconv.ParseUint(string(b), 10, 64)
if err != nil {
return 0, 0, err
}
sigSize, err := v.validateSignature(reader, "guild_id.txt", b)
if err != nil {
return 0, 0, err
}
return guildId, int64(len(b)) + sigSize, nil
}

View File

@ -0,0 +1,83 @@
package validator
import (
"time"
"github.com/TicketsBot/database"
)
type TicketUnion[T any] struct {
TicketId int `json:"ticket_id"`
Data T `json:"data"`
}
type GuildData struct {
GuildId uint64 `json:"guild_id,string"`
ActiveLanguage *string `json:"active_language"`
ArchiveChannel *uint64 `json:"archive_channel,string"`
ArchiveMessages []TicketUnion[database.ArchiveMessage] `json:"archive_messages"`
AutocloseSettings *database.AutoCloseSettings `json:"autoclose_settings"`
AutocloseExcluded []int `json:"autoclose_excluded"` // ticket IDs
GuildBlacklistedUsers []uint64 `json:"guild_blacklisted_users"`
ChannelCategory *uint64 `json:"channel_category,string"`
ClaimSettings *database.ClaimSettings `json:"claim_settings"`
CloseConfirmationEnabled bool `json:"close_confirmation_enabled"`
CloseReasons []TicketUnion[database.CloseMetadata] `json:"close_reasons"`
CustomColors map[int16]int `json:"custom_colors"`
EmbedFields []database.EmbedField `json:"embed_fields"`
Embeds []database.CustomEmbed `json:"embeds"`
ExitSurveyResponses []TicketUnion[ExitSurveyResponse] `json:"exit_survey_responses"`
FeedbackEnabled bool `json:"feedback_enabled"`
FirstResponseTimes []FirstResponseTime `json:"first_response_times"`
FormInputs []database.FormInput `json:"form_inputs"`
Forms []database.Form `json:"forms"`
GuildIsGloballyBlacklisted bool `json:"guild_is_globally_blacklisted"`
GuildMetadata database.GuildMetadata `json:"guild_metadata"`
MultiPanels []database.MultiPanel `json:"multi_panels"`
MultiPanelTargets map[int][]int `json:"multi_panel_targets"` // multi_panel_id -> [panel_ids]
NamingScheme *database.NamingScheme `json:"naming_scheme"`
OnCallUsers []uint64 `json:"on_call_users"`
PanelAccessControlRules map[int][]database.PanelAccessControlRule `json:"panel_access_control_rules"` // panel_id -> rules
PanelMentionUser map[int]bool `json:"panel_mention_user"`
PanelRoleMentions map[int][]uint64 `json:"panel_role_mentions"`
Panels []database.Panel `json:"panels"`
PanelTeams map[int][]int `json:"panel_teams"` // panel_id -> [team_ids]
Participants map[int][]uint64 `json:"participants"` // ticket_id -> [user_ids]
UserPermissions []Permission `json:"user_permissions"`
GuildBlacklistedRoles []uint64 `json:"guild_blacklisted_roles"`
RolePermissions []Permission `json:"role_permissions"`
ServiceRatings []TicketUnion[int16] `json:"service_ratings"`
Settings database.Settings `json:"settings"`
SupportTeamUsers map[int][]uint64 `json:"support_team_users"` // team_id -> [user_ids]
SupportTeamRoles map[int][]uint64 `json:"support_team_roles"` // team_id -> [role_ids]
SupportTeams []database.SupportTeam `json:"support_teams"`
Tags []database.Tag `json:"tags"`
TicketClaims []TicketUnion[uint64] `json:"ticket_claims"`
TicketLastMessages []TicketUnion[database.TicketLastMessage] `json:"ticket_last_messages"`
TicketLimit *int `json:"ticket_limit"`
TicketAdditionalMembers map[int][]uint64 `json:"ticket_additional_members"` // ticket_id -> [user_ids]
TicketPermissions database.TicketPermissions `json:"ticket_permissions"`
Tickets []database.Ticket `json:"tickets"`
UsersCanClose bool `json:"users_can_close"`
WelcomeMessage *string `json:"welcome_message"`
}
// Shims
type FirstResponseTime struct {
TicketId int `json:"ticket_id"`
UserId uint64 `json:"user_id,string"`
ResponseTime time.Duration `json:"response_time"`
}
type Permission struct {
Snowflake uint64 `json:"snowflake,string"`
IsSupport bool `json:"is_support"`
IsAdmin bool `json:"is_admin"`
}
type ExitSurveyResponse struct {
FormId *int `json:"form_id"`
QuestionId *int `json:"question_id"`
Response *string `json:"response"`
}

View File

@ -0,0 +1,52 @@
package validator
import (
"archive/zip"
"crypto/ed25519"
"encoding/base64"
"io"
"strconv"
)
func (v *Validator) validateSignature(zipReader *zip.Reader, fileName string, data []byte) (int64, error) {
f, err := zipReader.Open(fileName + ".sig")
if err != nil {
return 0, err
}
signature, err := io.ReadAll(v.newLimitReader(f))
if err != nil {
return 0, err
}
decoded, err := base64.RawURLEncoding.DecodeString(string(signature))
if err != nil {
return 0, err
}
if !ed25519.Verify(v.publicKey, data, decoded) {
return 0, ErrValidationFailed
}
return int64(len(signature)), nil
}
func (v *Validator) validateTranscriptSignature(
zipReader *zip.Reader,
fileName string,
guildId uint64,
ticketId int,
data []byte,
) (int64, error) {
guildIdStr := strconv.FormatUint(guildId, 10)
ticketIdStr := strconv.Itoa(ticketId)
sigData := make([]byte, 0, len(guildIdStr)+len(ticketIdStr)+len(data)+2)
sigData = append(sigData, guildIdStr...)
sigData = append(sigData, '|')
sigData = append(sigData, ticketIdStr...)
sigData = append(sigData, '|')
sigData = append(sigData, data...)
return v.validateSignature(zipReader, fileName, sigData)
}

View File

@ -0,0 +1,45 @@
package validator
import (
"crypto/ed25519"
"io"
)
type Validator struct {
publicKey ed25519.PublicKey
maxUncompressedSize int64
maxIndividualFileSize int64
}
type Option func(*Validator)
func NewValidator(publicKey ed25519.PublicKey, options ...Option) *Validator {
v := &Validator{
publicKey: publicKey,
maxUncompressedSize: 250 * 1024 * 1024,
maxIndividualFileSize: 1 * 1024 * 1024,
}
for _, option := range options {
option(v)
}
return v
}
func WithMaxUncompressedSize(size int64) Option {
return func(v *Validator) {
v.maxUncompressedSize = size
}
}
func WithMaxIndividualFileSize(size int64) Option {
return func(v *Validator) {
v.maxIndividualFileSize = size
}
}
func (v *Validator) newLimitReader(r io.Reader) io.Reader {
return io.LimitReader(r, v.maxIndividualFileSize)
}

View File

@ -3,11 +3,12 @@ package api
import (
"context"
"errors"
"strconv"
"github.com/TicketsBot-cloud/archiverclient"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/archiverclient"
"github.com/gin-gonic/gin"
"strconv"
)
func GetTranscriptHandler(ctx *gin.Context) {

View File

@ -2,12 +2,13 @@ package api
import (
"errors"
"strconv"
"github.com/TicketsBot-cloud/archiverclient"
"github.com/TicketsBot/GoPanel/chatreplica"
dbclient "github.com/TicketsBot/GoPanel/database"
"github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/archiverclient"
"github.com/gin-gonic/gin"
"strconv"
)
func GetTranscriptRenderHandler(ctx *gin.Context) {

View File

@ -1,9 +1,12 @@
package http
import (
"time"
"github.com/TicketsBot/GoPanel/app/http/endpoints/api"
"github.com/TicketsBot/GoPanel/app/http/endpoints/api/admin/botstaff"
api_blacklist "github.com/TicketsBot/GoPanel/app/http/endpoints/api/blacklist"
api_import "github.com/TicketsBot/GoPanel/app/http/endpoints/api/export"
api_forms "github.com/TicketsBot/GoPanel/app/http/endpoints/api/forms"
api_integrations "github.com/TicketsBot/GoPanel/app/http/endpoints/api/integrations"
api_panels "github.com/TicketsBot/GoPanel/app/http/endpoints/api/panel"
@ -24,7 +27,6 @@ import (
"github.com/gin-gonic/gin"
"github.com/penglongli/gin-metrics/ginmetrics"
"go.uber.org/zap"
"time"
)
func StartServer(logger *zap.Logger, sm *livechat.SocketManager) {
@ -120,6 +122,8 @@ func StartServer(logger *zap.Logger, sm *livechat.SocketManager) {
guildAuthApiSupport.GET("/settings", api_settings.GetSettingsHandler)
guildAuthApiAdmin.POST("/settings", api_settings.UpdateSettingsHandler)
guildAuthApiAdmin.POST("/import", api_import.ImportHandler)
guildAuthApiSupport.GET("/blacklist", api_blacklist.GetBlacklistHandler)
guildAuthApiSupport.POST("/blacklist", api_blacklist.AddBlacklistHandler)
guildAuthApiSupport.DELETE("/blacklist/user/:user", api_blacklist.RemoveUserBlacklistHandler)

View File

@ -2,6 +2,10 @@ package main
import (
"fmt"
"net/http"
"net/http/pprof"
"github.com/TicketsBot-cloud/archiverclient"
app "github.com/TicketsBot/GoPanel/app/http"
"github.com/TicketsBot/GoPanel/app/http/endpoints/api/ticket/livechat"
"github.com/TicketsBot/GoPanel/config"
@ -10,7 +14,6 @@ import (
"github.com/TicketsBot/GoPanel/rpc"
"github.com/TicketsBot/GoPanel/rpc/cache"
"github.com/TicketsBot/GoPanel/utils"
"github.com/TicketsBot/archiverclient"
"github.com/TicketsBot/common/chatrelay"
"github.com/TicketsBot/common/model"
"github.com/TicketsBot/common/observability"
@ -21,8 +24,8 @@ import (
"github.com/rxdn/gdl/rest/request"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"net/http"
"net/http/pprof"
_ "github.com/joho/godotenv/autoload"
)
func main() {

View File

@ -2,6 +2,8 @@ package database
import (
"context"
database2 "github.com/TicketsBot-cloud/database"
"github.com/TicketsBot/GoPanel/config"
"github.com/TicketsBot/database"
"github.com/jackc/pgconn"
@ -13,6 +15,7 @@ import (
)
var Client *database.Database
var Client2 *database2.Database
func ConnectToDatabase() {
config, err := pgxpool.ParseConfig(config.Conf.Database.Uri)
@ -37,4 +40,5 @@ func ConnectToDatabase() {
}
Client = database.NewDatabase(pool)
Client2 = database2.NewDatabase(pool)
}

View File

@ -0,0 +1,150 @@
<div class="modal" transition:fade>
<div class="modal-wrapper">
<Card footer={true} footerRight={true} fill={false}>
<span slot="title">Import Data</span>
<div slot="body" class="body-wrapper">
Please upload your exported data file to import settings and transcripts.
<form>
<div class="row">
<div class="label-wrapper">
<label for="import_data" class="form-label"> Data Export .zip</label>
</div>
<input type="file" id="import_data" style="display: block; width: 100%;" accept=".zip" />
</div>
<div class="row">
<div class="label-wrapper">
<label for="import_data" class="form-label"> Transcripts Export .zip</label>
</div>
<input type="file" id="import_transcripts" style="display: block; width: 100%;" accept=".zip" />
</div>
</form>
</div>
<div slot="footer" class="footer-wrapper">
<Button danger={true} on:click={dispatchClose}>Cancel</Button>
<div style="">
<Button on:click={dispatchConfirm}>Confirm</Button>
</div>
</div>
</Card>
</div>
</div>
<div class="modal-backdrop" transition:fade></div>
<svelte:window on:keydown={handleKeydown}/>
<script>
import {createEventDispatcher} from 'svelte';
import {fade} from 'svelte/transition'
import Card from "../Card.svelte";
import Button from "../Button.svelte";
import Textarea from '../form/Textarea.svelte';
import {setDefaultHeaders} from '../../includes/Auth.svelte'
import {notifyError, notifySuccess} from "../../js/util";
import axios from "axios";
import {API_URL} from "../../js/constants";
setDefaultHeaders();
export let guildId;
let publicKey = "";
const dispatch = createEventDispatcher();
function dispatchClose() {
dispatch('close', {});
}
async function dispatchConfirm() {
let dataFileInput = document.getElementById('import_data');
let transcriptFileInput = document.getElementById('import_transcripts');
if(dataFileInput.files.length === 0 && transcriptFileInput.files.length === 0) {
notifyError('Please select a file to import, at least one of data or transcripts must be provided');
return;
}
const frmData = new FormData();
if (dataFileInput.files.length > 0) {
frmData.append('data_file', dataFileInput.files[0]);
}
if (transcriptFileInput.files.length > 0) {
frmData.append('transcripts_file', transcriptFileInput.files[0]);
}
const res = await axios.post(`${API_URL}/api/${guildId}/import`, frmData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
if (res.status !== 200) {
notifyError(`Failed to import settings: ${res.data.error}`);
return;
}
dispatchClose();
notifySuccess('Imported settings successfully');
}
function handleKeydown(e) {
if (e.key === "Escape") {
dispatchClose();
}
}
</script>
<style>
.modal {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 501;
display: flex;
justify-content: center;
align-items: center;
}
.modal-wrapper {
display: flex;
width: 40%;
}
@media only screen and (max-width: 1280px) {
.modal-wrapper {
width: 96%;
}
}
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 500;
background-color: #000;
opacity: .5;
}
.body-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
}
.footer-wrapper {
display: flex;
flex-direction: row;
gap: 12px;
}
</style>

View File

@ -1,3 +1,6 @@
{#if importModal}
<ImportModal guildId={guildId} on:close={() => importModal = false}/>
{/if}
<section class="sidebar">
<header>
<img src="{iconUrl}" class="guild-icon" alt="Guild icon" width="50" height="50" on:error={handleIconLoadError} />
@ -24,6 +27,13 @@
<ManageSidebarLink {currentRoute} title="Tickets" icon="fa-ticket-alt" href="/manage/{guildId}/tickets" />
<ManageSidebarLink {currentRoute} title="Blacklist" icon="fa-ban" href="/manage/{guildId}/blacklist" />
<ManageSidebarLink {currentRoute} title="Tags" icon="fa-tags" href="/manage/{guildId}/tags" />
{#if isAdmin}
<hr />
<div class="row" style="align-items: center; justify-content: center;">
<ManageSidebarButton on:click={() => importModal = true} title="Import Data" icon="fa-file-import" />
</div>
{/if}
</ul>
</nav>
<nav class="bottom">
@ -93,12 +103,17 @@
import {notifyError, withLoadingScreen} from "../js/util";
import {getIconUrl, getDefaultIcon} from "../js/icons";
import ManageSidebarLink from "./ManageSidebarLink.svelte";
import ManageSidebarButton from "./ManageSidebarButton.svelte";
import SubNavigation from "./SubNavigation.svelte";
import SubNavigationLink from "./SubNavigationLink.svelte";
import ImportModal from "../components/manage/ImportModal.svelte";
export let currentRoute;
export let permissionLevel;
let importModal = false;
$: isAdmin = permissionLevel >= 2;
let guildId = currentRoute.namedParams.id;

View File

@ -0,0 +1,46 @@
<li>
<div style="width: 100%">
<button on:click isTrigger="1" class="btn" target="{newWindow === true ? '_blank' : '_self'}" style="width: 100%">
{#if icon}
<i class="fas {icon}"/>
{/if}
<span>{title}</span>
</button>
</div>
</li>
<style>
button {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
color: white;
background: var(--primary-gradient);
border: none;
/*border-color: var(--primary);*/
/*border-width: 2px;*/
border-radius: .25rem;
margin: 0;
cursor: pointer;
transition: background-color 150ms ease-in-out, border-color 150ms ease-in-out;
}
i {
width: 20px;
text-align: center;
}
</style>
<script>
export let title;
export let icon;
export let newWindow;
</script>

View File

@ -1,5 +1,9 @@
export const API_URL = env.API_URL || "https://api.ticketsbot.cloud"
export const API_URL = env.API_URL || "http://localhost:8081"
export const PLACEHOLDER_DOCS_URL = "https://docs.ticketsbot.cloud/setup/placeholders.html"
export const STRIPE = {
publishKey: env.STRIPE_PUBLISH_KEY || "pk_test_51QpuwdBXCkKxYGKwRUAhyc5RO8H9JRaIUOy8Apg85GfbUdtcyfo5P34yFpuYds13qZ8aMUtwhKesMZDA1F884A8u00fDXFAgHf",
checkoutUrl: env.STRIPE_PORTAL_URL || "https://billing.stripe.com/p/login/test_7sI189bPP9QBbDy9AA"
}
export const OAUTH = {
clientId: env.CLIENT_ID || "1325579039888511056",

49
go.mod
View File

@ -1,14 +1,14 @@
module github.com/TicketsBot/GoPanel
go 1.22.0
go 1.22.6
toolchain go1.22.4
replace github.com/TicketsBot-cloud/database => ../database
require (
github.com/BurntSushi/toml v1.2.1
github.com/TicketsBot/archiverclient v0.0.0-20241012221057-16a920bfb454
github.com/TicketsBot-cloud/archiverclient v0.0.0-20250206203822-d4f91573ad70
github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1
github.com/TicketsBot/database v0.0.0-20241116234225-cdf216a9ffca
github.com/TicketsBot/database v0.0.0-20250205194156-c8239ae6eb4e
github.com/TicketsBot/logarchiver v0.0.0-20241012220745-5f3ba17a5138
github.com/TicketsBot/worker v0.0.0-20241117155137-89dec1fd9a11
github.com/apex/log v1.1.2
@ -16,7 +16,7 @@ require (
github.com/getsentry/sentry-go v0.24.0
github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607
github.com/gin-gonic/gin v1.9.1
github.com/go-playground/validator/v10 v10.14.0
github.com/go-playground/validator/v10 v10.22.0
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-redis/redis/v8 v8.11.5
github.com/go-redis/redis_rate/v9 v9.1.1
@ -24,22 +24,28 @@ require (
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.0
github.com/jackc/pgconn v1.14.3
github.com/jackc/pgtype v1.14.0
github.com/jackc/pgtype v1.14.4
github.com/jackc/pgx/v4 v4.18.3
github.com/penglongli/gin-metrics v0.1.10
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/client_golang v1.20.5
github.com/rxdn/gdl v0.0.0-20241201120412-8fd61c53dd96
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
github.com/weppos/publicsuffix-go v0.20.0
go.uber.org/zap v1.24.0
golang.org/x/sync v0.9.0
golang.org/x/sync v0.11.0
)
require github.com/TicketsBot/archiverclient v0.0.0-20241012221057-16a920bfb454 // indirect
require github.com/joho/godotenv v1.5.1
require (
github.com/ClickHouse/ch-go v0.52.1 // indirect
github.com/ClickHouse/clickhouse-go/v2 v2.10.0 // indirect
github.com/TicketsBot-cloud/common v0.0.0-20250208132851-d5083bb04d98 // indirect
github.com/TicketsBot-cloud/database v0.0.0-20250208140247-a3ced2089495 // indirect
github.com/TicketsBot/analytics-client v0.0.0-20240724103359-30f5dac821e6 // indirect
github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
@ -50,13 +56,13 @@ require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/caarlos0/env v3.5.0+incompatible // indirect
github.com/caarlos0/env/v10 v10.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elliotchance/orderedmap v1.2.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-faster/city v1.0.1 // indirect
@ -66,7 +72,6 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-redsync/redsync/v4 v4.12.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
@ -77,7 +82,7 @@ require (
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx v3.6.2+incompatible // indirect
github.com/jackc/pgx/v5 v5.6.0 // indirect
github.com/jackc/puddle v1.3.0 // indirect
@ -87,23 +92,23 @@ require (
github.com/juju/ratelimit v1.0.2 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.73 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c // indirect
github.com/paulmach/orb v0.9.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rs/xid v1.5.0 // indirect
@ -118,12 +123,12 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/alexcesaro/statsd.v2 v2.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

125
go.sum
View File

@ -41,6 +41,12 @@ github.com/ClickHouse/clickhouse-go/v2 v2.10.0 h1:0w/A50D5MfsRUYBaV6rLKwZ4LXWKLZ
github.com/ClickHouse/clickhouse-go/v2 v2.10.0/go.mod h1:teXfZNM90iQ99Jnuht+dxQXCuhDZ8nvvMoTJOFrcmcg=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/ReneKroon/ttlcache v1.6.0/go.mod h1:DG6nbhXKUQhrExfwwLuZUdH7UnRDDRA1IW+nBuCssvs=
github.com/TicketsBot-cloud/archiverclient v0.0.0-20250206203822-d4f91573ad70 h1:bkTKXS4RRLTOC2UbqRnYObI7uGfyW64mo7dYxx8HN+w=
github.com/TicketsBot-cloud/archiverclient v0.0.0-20250206203822-d4f91573ad70/go.mod h1:BtS3JYHIuRStuUm909E7IDAwg7aAuiS5CNfIH5Y+Ayw=
github.com/TicketsBot-cloud/common v0.0.0-20250208132851-d5083bb04d98 h1:HwtXrqSv6y5E8mTnkOtISTaQr6dsK/lfUC8tUA6SzSU=
github.com/TicketsBot-cloud/common v0.0.0-20250208132851-d5083bb04d98/go.mod h1:iiZhl7w5DeTEGzAeq/hzani8+ILQz60g4JFkOy2vkuM=
github.com/TicketsBot-cloud/database v0.0.0-20250208140247-a3ced2089495 h1:1CIMrhgWO4vcK7gou1tZXBgWUB6UIlM/qaPqFh/yJIw=
github.com/TicketsBot-cloud/database v0.0.0-20250208140247-a3ced2089495/go.mod h1:LPDEn9e5wccH7rq/pUlVcL3UhyLnnwdM2dhj0tp/ljo=
github.com/TicketsBot/analytics-client v0.0.0-20240724103359-30f5dac821e6 h1:JigBTrKv/wRkCnz0G9m/EsqmG5DU6pFWBtrXNhcvK3E=
github.com/TicketsBot/analytics-client v0.0.0-20240724103359-30f5dac821e6/go.mod h1:9Z9qP/yovb6DUe1KlzgN2wicc+8ey6MMoxfynFbMyRg=
github.com/TicketsBot/archiverclient v0.0.0-20241012221057-16a920bfb454 h1:u77mqvmLdljjircnvgikSWNOSyc2uSLLybPOQP8moK8=
@ -49,6 +55,8 @@ github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1 h1:FqC1KGOsmB+ik
github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1/go.mod h1:N7zwetwx8B3RK/ZajWwMroJSyv2ZJ+bIOZWv/z8DhaM=
github.com/TicketsBot/database v0.0.0-20241116234225-cdf216a9ffca h1:dWFpbKflrHgkoNOI6e44GMMCwt8YWD614SmsCzwUAZY=
github.com/TicketsBot/database v0.0.0-20241116234225-cdf216a9ffca/go.mod h1:mpVkDO8tnnWn1pMGEphVg6YSeGIhDwLAN43lBTkpGmU=
github.com/TicketsBot/database v0.0.0-20250205194156-c8239ae6eb4e h1:3cYv3r/wSbTSp2Rmtk9jx3D7qpBQx6QMnOjIQbbND3w=
github.com/TicketsBot/database v0.0.0-20250205194156-c8239ae6eb4e/go.mod h1:mpVkDO8tnnWn1pMGEphVg6YSeGIhDwLAN43lBTkpGmU=
github.com/TicketsBot/logarchiver v0.0.0-20241012220745-5f3ba17a5138 h1:wsR5ESeaQKo122qsmzPcblxlJdE0GIQbp2B/7/uX+TA=
github.com/TicketsBot/logarchiver v0.0.0-20241012220745-5f3ba17a5138/go.mod h1:4Rq0CgSCgXVW6uEyEUvWzxOmFp+L57rFfCjPDFPHFiw=
github.com/TicketsBot/ttlcache v1.6.1-0.20200405150101-acc18e37b261 h1:NHD5GB6cjlkpZFjC76Yli2S63/J2nhr8MuE6KlYJpQM=
@ -93,8 +101,8 @@ github.com/caarlos0/env/v11 v11.2.2/go.mod h1:JBfcdeQiBoI3Zh1QRAWfe+tpiNTmDtcCj/
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
@ -126,8 +134,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/getsentry/sentry-go v0.24.0 h1:02b7qEmJ56EHGe9KFgjArjU/vG/aywm7Efgu+iPc01Y=
github.com/getsentry/sentry-go v0.24.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@ -151,11 +159,9 @@ github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
@ -166,8 +172,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
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/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
@ -277,6 +283,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@ -297,6 +304,7 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
@ -309,18 +317,23 @@ github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8=
github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA=
github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o=
github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
@ -335,6 +348,8 @@ github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFr
github.com/jedib0t/go-pretty/v6 v6.5.6 h1:nKXVLqPfAwY7sWcYXdNZZZ2fjqDpAtj9UeWupgfUxSg=
github.com/jedib0t/go-pretty/v6 v6.5.6/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -365,15 +380,17 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.3/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.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
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=
@ -393,8 +410,6 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
@ -410,6 +425,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@ -450,28 +467,27 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk=
github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/redis/rueidis v1.0.19 h1:s65oWtotzlIFN8eMPhyYwxlwLR1lUdhza2KtWprKYSo=
@ -522,7 +538,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@ -530,6 +545,8 @@ github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08Yu
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
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/ticketsbot/database v0.0.0-20250205194156-c8239ae6eb4e h1:sBwa2Yk3B3pPDdNmnuSPZRG5NVxtmeTYlBvDZTO672o=
github.com/ticketsbot/database v0.0.0-20250205194156-c8239ae6eb4e/go.mod h1:mpVkDO8tnnWn1pMGEphVg6YSeGIhDwLAN43lBTkpGmU=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
@ -553,6 +570,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -600,9 +618,14 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -635,6 +658,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -671,17 +696,18 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -693,8 +719,10 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -743,19 +771,29 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -764,8 +802,13 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -818,6 +861,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -1,5 +1,5 @@
package utils
import "github.com/TicketsBot/archiverclient"
import "github.com/TicketsBot-cloud/archiverclient"
var ArchiverClient *archiverclient.ArchiverClient