diff --git a/core/container/container.go b/core/container/container.go index 16fbd3c..09312f9 100644 --- a/core/container/container.go +++ b/core/container/container.go @@ -3,13 +3,14 @@ package container import ( "context" + "github.com/teadove/fun_telegram/core/infrastructure/pg" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/teadove/fun_telegram/core/supplier/ds_supplier" "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/teadove/fun_telegram/core/presentation/telegram" - "github.com/teadove/fun_telegram/core/repository/ch_repository" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" "github.com/teadove/fun_telegram/core/repository/redis_repository" "github.com/teadove/fun_telegram/core/service/analitics" "github.com/teadove/fun_telegram/core/service/job" @@ -43,29 +44,36 @@ func MustNewCombatContainer(ctx context.Context) Container { locator := ip_locator.Supplier{} - dbRepository, err := mongo_repository.New() - shared.Check(ctx, err) - - chRepository, err := ch_repository.New(ctx) - shared.Check(ctx, err) - dsSupplier, err := ds_supplier.New(ctx) shared.Check(ctx, err) resourceService, err := resource.New(ctx) shared.Check(ctx, err) - analiticsService, err := analitics.New(dbRepository, chRepository, dsSupplier, resourceService) + db, err := pg.NewClientFromSettings() + if err != nil { + shared.FancyPanic(ctx, errors.Wrap(err, "failed to init pg client")) + } + + dbRepository, err := db_repository.NewRepository(ctx, db) + if err != nil { + shared.FancyPanic(ctx, errors.Wrap(err, "failed to init pg repository")) + } + + analiticsService, err := analitics.New( + dsSupplier, + resourceService, + dbRepository, + ) shared.Check(ctx, err) protoClient, err := telegram.NewProtoClient(ctx) shared.Check(ctx, err) - jobService, err := job.New(ctx, dbRepository, chRepository, map[string]job.ServiceChecker{ - "MongoDB": {Checker: dbRepository.Ping, ForFrequent: true}, + jobService, err := job.New(ctx, map[string]job.ServiceChecker{ "Telegram": {Checker: protoClient.Ping, ForFrequent: true}, "Redis": {Checker: persistentStorage.Ping, ForFrequent: true}, - "ClickHouse": {Checker: chRepository.Ping, ForFrequent: true}, + "Postgres": {Checker: dbRepository.Ping, ForFrequent: true}, "Kandinsky": {Checker: kandinskySupplier.Ping}, "IpLocator": {Checker: locator.Ping}, "DSSupplier": {Checker: dsSupplier.Ping}, @@ -78,10 +86,10 @@ func MustNewCombatContainer(ctx context.Context) Container { persistentStorage, kandinskySupplier, &locator, - dbRepository, analiticsService, jobService, resourceService, + dbRepository, ) container := Container{telegramPresentation, jobService} diff --git a/core/infrastructure/pg/client.go b/core/infrastructure/pg/client.go new file mode 100644 index 0000000..d87aa92 --- /dev/null +++ b/core/infrastructure/pg/client.go @@ -0,0 +1,46 @@ +package pg + +import ( + "time" + + "gorm.io/gorm/logger" + "gorm.io/gorm/schema" + + "github.com/pkg/errors" + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +func NewClientFromSettings() (*gorm.DB, error) { + pgConfig := postgres.Config{ + DSN: "postgresql://main:main@localhost:5432/main", + PreferSimpleProtocol: true, + } + + db, err := gorm.Open(postgres.New(pgConfig), &gorm.Config{ + SkipDefaultTransaction: true, + TranslateError: true, + NowFunc: func() time.Time { + ti, _ := time.LoadLocation("utc") + return time.Now().In(ti) + }, + NamingStrategy: schema.NamingStrategy{ + SingularTable: true, + }, + + Logger: logger.Default.LogMode(logger.Warn), + }) + if err != nil { + return nil, errors.Wrap(err, "failed to connect to database") + } + + sqlDb, err := db.DB() + if err != nil { + return nil, errors.Wrap(err, "failed to get std db") + } + + sqlDb.SetMaxIdleConns(10) + sqlDb.SetMaxOpenConns(30) + + return db, nil +} diff --git a/core/presentation/run.go b/core/presentation/run.go index 20c6fcb..56e48df 100644 --- a/core/presentation/run.go +++ b/core/presentation/run.go @@ -21,6 +21,7 @@ func captureInterrupt(ctx context.Context) { Info(). Str("signal", sig.String()). Msg("captured exit signal, exiting...") + pprof.StopCPUProfile() os.Exit(0) } @@ -30,7 +31,7 @@ func captureInterrupt(ctx context.Context) { func Run() { ctx := shared.GetCtx() captureInterrupt(ctx) - zerolog.Ctx(ctx).Info().Str("status", "app.starting").Send() + zerolog.Ctx(ctx).Info().Msg("app.starting") combatContainer := container.MustNewCombatContainer(ctx) go healthServer(combatContainer.JobService) @@ -41,7 +42,7 @@ func Run() { } }() - zerolog.Ctx(ctx).Info().Str("status", "app.started").Send() + zerolog.Ctx(ctx).Info().Msg("app.started") err := combatContainer.Presentation.Run() shared.Check(ctx, err) diff --git a/core/presentation/telegram/disable_command.go b/core/presentation/telegram/disable_command.go index e02eb2d..4dde7dc 100644 --- a/core/presentation/telegram/disable_command.go +++ b/core/presentation/telegram/disable_command.go @@ -4,10 +4,11 @@ import ( "context" "strings" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/celestix/gotgproto/ext" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" "github.com/teadove/fun_telegram/core/repository/redis_repository" ) @@ -57,8 +58,8 @@ func (r *Presentation) checkFromAdmin(ctx *ext.Context, update *ext.Update) (ok return false, errors.New("user not found in members") } - return userMember.Status == mongo_repository.Admin || - userMember.Status == mongo_repository.Creator, nil + return userMember.Status == db_repository.Admin || + userMember.Status == db_repository.Creator, nil } func (r *Presentation) checkFromOwner(ctx *ext.Context, update *ext.Update) (ok bool) { diff --git a/core/presentation/telegram/get_members.go b/core/presentation/telegram/get_members.go index b083277..a679cc6 100644 --- a/core/presentation/telegram/get_members.go +++ b/core/presentation/telegram/get_members.go @@ -5,49 +5,52 @@ import ( "strings" "time" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/celestix/gotgproto/types" "github.com/gotd/td/telegram/peers/members" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" "go.mongodb.org/mongo-driver/mongo" ) var ErrNotChatOrChannel = errors.New("is not chat or channel") -func tgStatusToRepositoryStatus(status members.Status) mongo_repository.MemberStatus { +func tgStatusToRepositoryStatus(status members.Status) db_repository.MemberStatus { switch status { case members.Left: - return mongo_repository.Left + return db_repository.Left case members.Plain: - return mongo_repository.Plain + return db_repository.Plain case members.Creator: - return mongo_repository.Creator + return db_repository.Creator case members.Admin: - return mongo_repository.Admin + return db_repository.Admin case members.Banned: - return mongo_repository.Banned + return db_repository.Banned default: - return mongo_repository.Unknown + return db_repository.Unknown } } func (r *Presentation) updateMembers( ctx context.Context, effectiveChat types.EffectiveChat, -) (mongo_repository.UsersInChat, error) { +) (db_repository.UsersInChat, error) { t0 := time.Now() - zerolog.Ctx(ctx).Info().Str("status", "members.uploading").Send() + zerolog.Ctx(ctx). + Info(). + Msg("members.uploading") - usersInChat := make(mongo_repository.UsersInChat, 0, 50) + usersInChat := make(db_repository.UsersInChat, 0, 50) compileSlice := func(chatMember members.Member) error { user := chatMember.User() _, isBot := user.ToBot() username, _ := user.Username() - userInChat := mongo_repository.UserInChat{ + userInChat := db_repository.UserInChat{ TgId: user.ID(), TgUsername: strings.ToLower(username), TgName: GetNameFromPeerUser(&user), @@ -56,7 +59,7 @@ func (r *Presentation) updateMembers( } usersInChat = append(usersInChat, userInChat) - err := r.mongoRepository.UserUpsert(ctx, &mongo_repository.User{ + err := r.dbRepository.UserUpsert(ctx, &db_repository.User{ TgId: userInChat.TgId, TgUsername: userInChat.TgUsername, TgName: userInChat.TgName, @@ -66,7 +69,7 @@ func (r *Presentation) updateMembers( return errors.Wrap(err, "failed to upsert user") } - err = r.mongoRepository.MemberUpsert(ctx, &mongo_repository.Member{ + err = r.dbRepository.MemberUpsert(ctx, &db_repository.Member{ TgUserId: userInChat.TgId, TgChatId: effectiveChat.GetID(), Status: userInChat.Status, @@ -77,9 +80,8 @@ func (r *Presentation) updateMembers( zerolog.Ctx(ctx). Debug(). - Str("status", "member.uploaded"). Interface("user", userInChat). - Send() + Msg("member.uploaded") return nil } @@ -111,25 +113,26 @@ func (r *Presentation) updateMembers( return nil, errors.WithStack(ErrNotChatOrChannel) } - err := r.mongoRepository.ChatUpsert(ctx, &mongo_repository.Chat{ - TgId: effectiveChat.GetID(), - Title: chatTitle, + err := r.dbRepository.ChatUpsert(ctx, &db_repository.Chat{ + WithCreatedAt: db_repository.WithCreatedAt{CreatedAt: time.Now().UTC()}, + WithUpdatedAt: db_repository.WithUpdatedAt{UpdatedAt: time.Now().UTC()}, + TgId: effectiveChat.GetID(), + Title: chatTitle, }) if err != nil { return nil, errors.Wrap(err, "failed to upsert chat in mongo repository") } - err = r.mongoRepository.SetAllMembersAsLeft(ctx, effectiveChat.GetID(), t0.Add(-time.Hour)) + err = r.dbRepository.MemberSetAsLeftBeforeTime(ctx, effectiveChat.GetID(), t0.Add(-time.Hour)) if err != nil { return nil, errors.Wrap(err, "failed to set all members as left") } zerolog.Ctx(ctx). Info(). - Str("status", "members.uploaded"). Str("elapsed", time.Since(t0).String()). Int("count", len(usersInChat)). - Send() + Msg("members.uploaded") return usersInChat, nil } @@ -137,10 +140,10 @@ func (r *Presentation) updateMembers( func (r *Presentation) getOrUpdateMembers( ctx context.Context, effectiveChat types.EffectiveChat, -) (mongo_repository.UsersInChat, error) { +) (db_repository.UsersInChat, error) { needUpload := false - chat, err := r.mongoRepository.GetChat(ctx, effectiveChat.GetID()) + chat, err := r.dbRepository.ChatSelectById(ctx, effectiveChat.GetID()) if err != nil { if !errors.Is(err, mongo.ErrNoDocuments) { return nil, errors.Wrap(err, "failed to get chat from repository") @@ -158,7 +161,7 @@ func (r *Presentation) getOrUpdateMembers( return usersInChat, nil } - usersInChat, err := r.mongoRepository.GetUsersInChat(ctx, effectiveChat.GetID()) + usersInChat, err := r.dbRepository.UsersSelectInChat(ctx, effectiveChat.GetID()) if err != nil { return nil, errors.Wrap(err, "failed to get users by chat id") } diff --git a/core/presentation/telegram/kandinsky_command.go b/core/presentation/telegram/kandinsky_command.go index bfcd4e6..353851e 100644 --- a/core/presentation/telegram/kandinsky_command.go +++ b/core/presentation/telegram/kandinsky_command.go @@ -6,10 +6,11 @@ import ( "strconv" "time" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/gotd/td/telegram/message" "github.com/gotd/td/telegram/message/styling" "github.com/gotd/td/tg" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -60,7 +61,7 @@ func (r *Presentation) uploadKandinskyImage( ) bool { err := r.analiticsService.KandinskyImageInsert( ctx, - &mongo_repository.KandinskyImageDenormalized{ + &db_repository.KandinskyImageDenormalized{ TgInputPhoto: tg.InputPhoto{ ID: tgPhoto.ID, AccessHash: tgPhoto.AccessHash, @@ -68,7 +69,7 @@ func (r *Presentation) uploadKandinskyImage( }, KandinskyInput: *kandinskyInput, ImgContent: img, - Message: mongo_repository.Message{ + Message: db_repository.Message{ TgChatID: tgChatId, TgId: tgMsg.ID, TgUserId: ctx.Self.ID, @@ -301,7 +302,7 @@ func (r *Presentation) kandkinskyPaginateImagesCommandHandler( images, err := r.analiticsService.KandinskyImagePaginate( ctx, - &mongo_repository.KandinskyImagePaginateInput{ + &db_repository.KandinskyImagePaginateInput{ TgChatId: update.EffectiveChat().GetID(), Page: page, PageSize: pageSize, diff --git a/core/presentation/telegram/ping_command.go b/core/presentation/telegram/ping_command.go index 3e4a90f..5fbef17 100644 --- a/core/presentation/telegram/ping_command.go +++ b/core/presentation/telegram/ping_command.go @@ -4,6 +4,8 @@ import ( "context" "time" + "github.com/teadove/fun_telegram/core/repository/db_repository" + tgError "github.com/celestix/gotgproto/errors" "github.com/celestix/gotgproto/types" @@ -11,7 +13,6 @@ import ( "github.com/gotd/td/tg" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" ) // TODO: fix nolint @@ -40,20 +41,13 @@ func (r *Presentation) pingCommandHandler( msgToPing = update.EffectiveMessage.ReplyToMessage } - userId, err := GetSenderId(msgToPing) - if err != nil { - return errors.Wrap(err, "failed to get sender id") - } - - err = r.mongoRepository.PingMessageCreate( + err = r.dbRepository.PingMessageCreate( ctx, - &mongo_repository.Message{ - TgChatID: update.EffectiveChat().GetID(), - TgUserId: userId, - Text: msgToPing.Text, - TgId: msgToPing.ID, + &db_repository.PingMessage{ + MessageTgChatID: update.EffectiveChat().GetID(), + MessageTgId: msgToPing.ID, + DeleteAt: time.Now().UTC().Add(deletePinAfter), }, - time.Now().UTC().Add(deletePinAfter), ) if err != nil { return errors.Wrap(err, "failed to create ping message") @@ -76,7 +70,7 @@ func (r *Presentation) pingCommandHandler( } func (r *Presentation) deleteOldPingMessages(ctx context.Context) error { - messages, err := r.mongoRepository.PingMessageGetAndDeleteForDeletion(ctx) + messages, err := r.dbRepository.PingMessageGet(ctx) if err != nil { return errors.Wrap(err, "failed to get ping messages") } @@ -94,11 +88,11 @@ func (r *Presentation) deleteOldPingMessages(ctx context.Context) error { for _, message := range messages { log := zerolog.Ctx(ctx). With(). - Int("msg_id", message.TgId). - Int64("chat_id", message.TgChatID). + Int("msg_id", message.MessageTgId). + Int64("chat_id", message.MessageTgChatID). Logger() - inputPeer := r.protoClient.PeerStorage.GetInputPeerById(message.TgChatID) + inputPeer := r.protoClient.PeerStorage.GetInputPeerById(message.MessageTgChatID) if inputPeer == nil { log.Warn().Str("status", "failed.to.get.peer").Send() continue @@ -111,7 +105,7 @@ func (r *Presentation) deleteOldPingMessages(ctx context.Context) error { Unpin: true, PmOneside: true, Peer: inputPeer, - ID: message.TgId, + ID: message.MessageTgId, }, ) if err != nil { @@ -122,5 +116,10 @@ func (r *Presentation) deleteOldPingMessages(ctx context.Context) error { log.Info().Str("status", "ping.message.unpinned").Send() } + err = r.dbRepository.PingMessageDelete(ctx, messages) + if err != nil { + return errors.Wrap(err, "failed to delete ping messages") + } + return nil } diff --git a/core/presentation/telegram/restart_command.go b/core/presentation/telegram/restart_command.go index c0cd875..34c7b2a 100644 --- a/core/presentation/telegram/restart_command.go +++ b/core/presentation/telegram/restart_command.go @@ -4,11 +4,12 @@ import ( "context" "os" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/celestix/gotgproto/ext" "github.com/gotd/td/tg" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" "github.com/teadove/fun_telegram/core/service/resource" ) @@ -32,11 +33,9 @@ func (r *Presentation) restartCommandHandler( zerolog.Ctx(ctx).Warn().Str("status", "reload.begin").Send() - err = r.mongoRepository.RestartMessageCreate(ctx, &mongo_repository.Message{ - TgChatID: ctx.Self.ID, - TgUserId: ctx.Self.ID, - Text: reloadMessage.Text, - TgId: reloadMessage.ID, + err = r.dbRepository.RestartMessageInsert(ctx, &db_repository.RestartMessage{ + MessageTgChatID: ctx.Self.ID, + MessageTgId: reloadMessage.ID, }) if err != nil { return errors.WithStack(err) @@ -48,9 +47,9 @@ func (r *Presentation) restartCommandHandler( } func (r *Presentation) updateRestartMessages(ctx context.Context) error { - messages, err := r.mongoRepository.RestartMessageGetAndDelete(ctx) + messages, err := r.dbRepository.RestartMessageGet(ctx) if err != nil { - return errors.WithStack(err) + return errors.Wrap(err, "failed to get messages") } if len(messages) == 0 { @@ -65,9 +64,9 @@ func (r *Presentation) updateRestartMessages(ctx context.Context) error { for _, message := range messages { tgCtx := r.protoClient.CreateContext() - _, err = tgCtx.EditMessage(message.TgChatID, &tg.MessagesEditMessageRequest{ + _, err = tgCtx.EditMessage(message.MessageTgChatID, &tg.MessagesEditMessageRequest{ Peer: r.protoClient.Self.AsInputPeer(), - ID: message.TgId, + ID: message.MessageTgId, Message: r.resourceService.Localize( ctx, resource.CommandRestartSuccess, @@ -79,5 +78,10 @@ func (r *Presentation) updateRestartMessages(ctx context.Context) error { } } + err = r.dbRepository.RestartMessageDelete(ctx, messages) + if err != nil { + return errors.Wrap(err, "failed to delete restart messages") + } + return nil } diff --git a/core/presentation/telegram/router.go b/core/presentation/telegram/router.go index f7a3985..5f33c64 100644 --- a/core/presentation/telegram/router.go +++ b/core/presentation/telegram/router.go @@ -142,10 +142,9 @@ func (r *Presentation) route(ctx *ext.Context, update *ext.Update) error { zerolog.Ctx(ctx.Context). Info(). - Str("status", "executing.command.begin"). // Interface("input", commandInput). Str("command", firstWord). - Send() + Msg("executing.command.begin") err = route.executor(ctx, update, &commandInput) elapsed := time.Now().UTC().Sub(commandInput.StartedAt) @@ -192,9 +191,8 @@ func (r *Presentation) route(ctx *ext.Context, update *ext.Update) error { zerolog.Ctx(ctx.Context). Info(). - Str("status", "executing.command.done"). Str("elapsed", elapsed.String()). - Send() + Msg("executing.command.done") return nil } diff --git a/core/presentation/telegram/stats_command.go b/core/presentation/telegram/stats_command.go index bb85b87..25625db 100644 --- a/core/presentation/telegram/stats_command.go +++ b/core/presentation/telegram/stats_command.go @@ -6,7 +6,9 @@ import ( "strings" "time" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "gorm.io/gorm" + "github.com/teadove/fun_telegram/core/service/analitics" "go.mongodb.org/mongo-driver/mongo" @@ -35,20 +37,20 @@ func (r *Presentation) getUserFromFlag( ctx *ext.Context, update *ext.Update, input *input, -) (mongo_repository.User, bool, error) { +) (db_repository.User, bool, error) { username, usernameFlagOk := input.Ops[FlagStatsUsername.Long] if !usernameFlagOk || len(username) == 0 { - return mongo_repository.User{}, false, nil + return db_repository.User{}, false, nil } targetUserId, err := strconv.ParseInt(username, 10, 64) if err == nil { - targetUser, err := r.mongoRepository.GetUserById(ctx, targetUserId) + targetUser, err := r.dbRepository.UserSelectById(ctx, targetUserId) if err == nil { return targetUser, true, nil } - if errors.Is(err, mongo.ErrNoDocuments) { + if errors.Is(err, gorm.ErrRecordNotFound) { err = r.replyIfNotSilent( ctx, update, @@ -56,16 +58,16 @@ func (r *Presentation) getUserFromFlag( fmt.Sprintf("Err: user not found by id: %d", targetUserId), ) if err != nil { - return mongo_repository.User{}, false, errors.Wrap(err, "failed to reply") + return db_repository.User{}, false, errors.Wrap(err, "failed to reply") } } - return mongo_repository.User{}, false, errors.Wrap(err, "failed to fetch user") + return db_repository.User{}, false, errors.Wrap(err, "failed to fetch user") } username = strings.ToLower(username) - targetUser, err := r.mongoRepository.GetUserByUsername(ctx, username) + targetUser, err := r.dbRepository.UserSelectByUsername(ctx, username) if err == nil { return targetUser, true, nil } @@ -78,11 +80,11 @@ func (r *Presentation) getUserFromFlag( fmt.Sprintf("Err: user not found by username: %s", username), ) if err != nil { - return mongo_repository.User{}, false, errors.Wrap(err, "failed to reply") + return db_repository.User{}, false, errors.Wrap(err, "failed to reply") } } - return mongo_repository.User{}, false, errors.Wrap(err, "failed to fetch user") + return db_repository.User{}, false, errors.Wrap(err, "failed to fetch user") } func (r *Presentation) statsChannelCommandHandler( diff --git a/core/presentation/telegram/stats_command_channel.go b/core/presentation/telegram/stats_command_channel.go index 394c35d..1f16213 100644 --- a/core/presentation/telegram/stats_command_channel.go +++ b/core/presentation/telegram/stats_command_channel.go @@ -8,6 +8,8 @@ import ( "sync" "time" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/gotd/td/telegram/query" "github.com/gotd/td/telegram/query/messages" "github.com/guregu/null/v5" @@ -18,7 +20,6 @@ import ( "github.com/gotd/td/tg" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/teadove/fun_telegram/core/repository/ch_repository" ) const ( @@ -96,7 +97,7 @@ func (r *Presentation) loadChannelMessage( TgChatID: chat.ID, TgUserId: chat.ID, Text: msg.Message, - TgId: int64(msg.ID), + TgId: msg.ID, } if msg.ReplyTo != nil { @@ -189,22 +190,17 @@ func (r *Presentation) uploadChannelsToRepository( ctx context.Context, channels <-chan Channel, ) error { - channelsSlice := make([]ch_repository.Channel, 0, channelsDumpBatch) + channelsSlice := make([]db_repository.Channel, 0, channelsDumpBatch) for channel := range channels { - var tgAbout *string - if channel.TgAbout.Valid { - tgAbout = &channel.TgAbout.String - } - - channelsSlice = append(channelsSlice, ch_repository.Channel{ + channelsSlice = append(channelsSlice, db_repository.Channel{ TgId: channel.TgId, TgTitle: channel.TgTitle, TgUsername: channel.TgUsername, ParticipantCount: channel.ParticipantCount, RecommendationsIds: channel.RecommendationsIds, IsLeaf: channel.IsLeaf, - TgAbout: tgAbout, + TgAbout: channel.TgAbout, }) if len(channelsSlice) >= channelsDumpBatch { @@ -213,7 +209,7 @@ func (r *Presentation) uploadChannelsToRepository( return errors.Wrap(err, "failed to to batch insert") } - channelsSlice = make([]ch_repository.Channel, 0, channelsDumpBatch) + channelsSlice = make([]db_repository.Channel, 0, channelsDumpBatch) } } diff --git a/core/presentation/telegram/stats_command_upload.go b/core/presentation/telegram/stats_command_upload.go index b3362af..ed170e2 100644 --- a/core/presentation/telegram/stats_command_upload.go +++ b/core/presentation/telegram/stats_command_upload.go @@ -108,7 +108,7 @@ func (r *Presentation) uploadMessageToRepository( TgChatID: update.EffectiveChat().GetID(), TgUserId: msgFrom.UserID, Text: msg.Message, - TgId: int64(msg.ID), + TgId: msg.ID, } if msg.ReplyTo != nil { @@ -205,7 +205,9 @@ func (r *Presentation) updateUploadStatsMessage( lastDate time.Time, maxCount int, ) { - zerolog.Ctx(ctx).Info().Str("status", "messages.batch.uploaded").Int("count", count).Send() + zerolog.Ctx(ctx).Info(). + Int("count", count). + Msg("messages.batch.uploaded") elapsed := time.Since(startedAt).Seconds() remainingCount := maxCount - count @@ -336,7 +338,10 @@ func (r *Presentation) uploadStatsUpload( // nolint: cyclop } } - zerolog.Ctx(ctx).Info().Str("status", "stats.upload.begin").Int("offset", offset).Send() + zerolog.Ctx(ctx). + Info(). + Int("offset", offset). + Msg("stats.upload.begin") historyQuery := query.Messages(r.telegramApi).GetHistory(update.EffectiveChat().GetInputPeer()) historyQuery.BatchSize(iterHistoryBatchSize) historyQuery.OffsetID(offset) @@ -423,18 +428,12 @@ func (r *Presentation) uploadStatsUpload( // nolint: cyclop zerolog.Ctx(ctx). Info(). - Str("status", "waiting.for.uploading.to.repository"). Int("count", count). - Send() + Msg("waiting.for.uploading.to.repository") close(elemChan) wg.Wait() zerolog.Ctx(ctx).Info().Str("status", "messages.uploaded").Int("count", count).Send() - err = r.analiticsService.MessageSetReplyToUserId(ctx, update.EffectiveChat().GetID()) - if err != nil { - return errors.Wrap(err, "failed to set reply to user id") - } - _, err = ctx.EditMessage(barChatId, &tg.MessagesEditMessageRequest{ Peer: barPeer, ID: barMessageId, diff --git a/core/presentation/telegram/telegram.go b/core/presentation/telegram/telegram.go index 505dd59..8cc1bce 100644 --- a/core/presentation/telegram/telegram.go +++ b/core/presentation/telegram/telegram.go @@ -4,6 +4,8 @@ import ( "context" "time" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/teadove/fun_telegram/core/service/tex" "github.com/celestix/gotgproto/dispatcher/handlers/filters" @@ -23,7 +25,6 @@ import ( "github.com/gotd/td/tg" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" "github.com/teadove/fun_telegram/core/repository/redis_repository" "github.com/teadove/fun_telegram/core/service/analitics" "github.com/teadove/fun_telegram/core/service/job" @@ -47,7 +48,7 @@ type Presentation struct { kandinskySupplier *kandinsky_supplier.Supplier ipLocator *ip_locator.Supplier redisRepository *redis_repository.Repository - mongoRepository *mongo_repository.Repository + dbRepository *db_repository.Repository resourceService *resource.Service analiticsService *analitics.Service jobService *job.Service @@ -121,10 +122,10 @@ func MustNewTelegramPresentation( redisRepository *redis_repository.Repository, kandinskySupplier *kandinsky_supplier.Supplier, ipLocator *ip_locator.Supplier, - dbRepository *mongo_repository.Repository, analiticsService *analitics.Service, jobService *job.Service, resourceService *resource.Service, + dbRepository *db_repository.Repository, ) *Presentation { api := protoClient.API() @@ -135,10 +136,10 @@ func MustNewTelegramPresentation( telegramManager: peers.Options{}.Build(api), kandinskySupplier: kandinskySupplier, ipLocator: ipLocator, - mongoRepository: dbRepository, analiticsService: analiticsService, jobService: jobService, resourceService: resourceService, + dbRepository: dbRepository, } protoClient.Dispatcher.AddHandler( @@ -345,10 +346,9 @@ func MustNewTelegramPresentation( presentation.setFeatures() zerolog.Ctx(ctx).Info(). - Str("status", "telegram.presentation.created"). Dur("retry.interval", presentation.protoClient.RetryInterval). Int("retry.count", presentation.protoClient.MaxRetries). - Send() + Msg("telegram.presentation.created") return &presentation } diff --git a/core/repository/ch_repository/channel_methods.go b/core/repository/ch_repository/channel_methods.go deleted file mode 100644 index 1bea243..0000000 --- a/core/repository/ch_repository/channel_methods.go +++ /dev/null @@ -1,254 +0,0 @@ -package ch_repository - -import ( - "context" - - mapset "github.com/deckarep/golang-set/v2" - - "github.com/pkg/errors" -) - -func (r *Repository) ChannelInsert(ctx context.Context, channel *Channel) error { - err := r.conn.AsyncInsert(ctx, ` -INSERT INTO channel VALUES ( - ?, ?, ?, ?, ?, ?, ?, ? - )`, - true, - channel.TgId, - channel.TgTitle, - channel.TgUsername, - channel.UploadedAt, - channel.ParticipantCount, - channel.RecommendationsIds, - channel.IsLeaf, - channel.TgAbout, - ) - if err != nil { - return errors.Wrap(err, "failed to async insert") - } - - return nil -} - -func (r *Repository) ChannelEdgeBatchInsert(ctx context.Context, channels ChannelsEdges) error { - batch, err := r.conn.PrepareBatch(ctx, `INSERT INTO channel_edge`) - if err != nil { - return errors.Wrap(err, "failed to prepare batch") - } - - for _, channel := range channels { - err = batch.Append( - channel.TgIdIn, - channel.TgIdOut, - channel.Order, - ) - if err != nil { - return errors.Wrap(err, "failed to append to batch") - } - } - - err = batch.Send() - if err != nil { - return errors.Wrap(err, "failed to batch send") - } - - return nil -} - -func (r *Repository) ChannelBatchInsert(ctx context.Context, channels []Channel) error { - batch, err := r.conn.PrepareBatch(ctx, `INSERT INTO channel`) - if err != nil { - return errors.Wrap(err, "failed to prepare batch") - } - - for _, channel := range channels { - err = batch.Append( - channel.TgId, - channel.TgTitle, - channel.TgUsername, - channel.UploadedAt, - channel.ParticipantCount, - channel.RecommendationsIds, - channel.IsLeaf, - channel.TgAbout, - ) - if err != nil { - return errors.Wrap(err, "failed to append to batch") - } - } - - err = batch.Send() - if err != nil { - return errors.Wrap(err, "failed to batch send") - } - - return nil -} - -func (r *Repository) ChannelSelectByUsername( - ctx context.Context, - username string, -) (Channel, error) { - row := r.conn.QueryRow(ctx, ` -select tg_id, tg_title, tg_username, uploaded_at, participant_count, recommendations_ids, is_leaf, tg_about from channel final - where tg_username = ? -`, username) - if row.Err() != nil { - return Channel{}, errors.Wrap(row.Err(), "failed to select row from clickhouse") - } - - var channel Channel - err := row.ScanStruct(&channel) - if err != nil { - return Channel{}, errors.Wrap(err, "failed to scan row") - } - - return channel, nil -} - -func (r *Repository) ChannelSelectById(ctx context.Context, id int64) (Channel, error) { - row := r.conn.QueryRow(ctx, ` -select tg_id, tg_title, tg_username, uploaded_at, participant_count, recommendations_ids, is_leaf, tg_about from channel final - where tg_id = ? -`, id) - if row.Err() != nil { - return Channel{}, errors.Wrap(row.Err(), "failed to select row from clickhouse") - } - - var channel Channel - err := row.ScanStruct(&channel) - if err != nil { - return Channel{}, errors.Wrap(err, "failed to scan row") - } - - return channel, nil -} - -func (r *Repository) ChannelSelectByIds(ctx context.Context, id []int64) (Channels, error) { - rows, err := r.conn.Query(ctx, ` -select tg_id, tg_title, tg_username, uploaded_at, participant_count, recommendations_ids, is_leaf, tg_about from channel final - where tg_id in ? -`, id) - if err != nil { - return nil, errors.Wrap(err, "failed to select rows from clickhouse") - } - - output := make(Channels, 0, len(id)) - - for rows.Next() { - row := Channel{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) ChannelSelect(ctx context.Context) (Channels, error) { - rows, err := r.conn.Query(ctx, ` -select tg_id, tg_title, tg_username, uploaded_at, participant_count, recommendations_ids, is_leaf, tg_about - from channel final - order by tg_id -`) - if err != nil { - return nil, errors.Wrap(err, "failed to select rows from clickhouse") - } - - output := make(Channels, 0, 100) - - for rows.Next() { - row := Channel{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) ChannelEdgesSelect( - ctx context.Context, - maxOrder int64, -) (ChannelsEdges, error) { - rows, err := r.conn.Query(ctx, ` -select tg_id_in, tg_id_out, order from channel_edge final order by tg_id_in, tg_id_out and order <= ? -`, maxOrder) - if err != nil { - return nil, errors.Wrap(err, "failed to select rows from clickhouse") - } - - output := make([]ChannelEdge, 0, 100) - - for rows.Next() { - row := ChannelEdge{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) ChannelEdgesSelectById( - ctx context.Context, - tgIdIn []int64, - maxOrder int64, -) (ChannelsEdges, error) { - rows, err := r.conn.Query(ctx, ` - select tg_id_in, tg_id_out, order from channel_edge final where tg_id_in in ? and order <= ? -`, tgIdIn, maxOrder) - if err != nil { - return nil, errors.Wrap(err, "failed to select rows from clickhouse") - } - - output := make(ChannelsEdges, 0, 100) - - for rows.Next() { - row := ChannelEdge{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) ChannelEdgesSelectDFS( - ctx context.Context, - tgUsername string, - depth int64, - maxOrder int64, -) (ChannelsEdges, error) { - channel, err := r.ChannelSelectByUsername(ctx, tgUsername) - if err != nil { - return nil, errors.Wrap(err, "failed to select channel by username") - } - - channelEdgesResult := mapset.NewSet[ChannelEdge]() - tgIds := []int64{channel.TgId} - - for range depth { - channelEdges, err := r.ChannelEdgesSelectById(ctx, tgIds, maxOrder) - if err != nil { - return nil, errors.Wrap(err, "failed to select channel edges by id") - } - - channelEdgesResult.Append(channelEdges...) - tgIds = channelEdges.ToOutIds() - } - - return channelEdgesResult.ToSlice(), nil -} diff --git a/core/repository/ch_repository/channel_models.go b/core/repository/ch_repository/channel_models.go deleted file mode 100644 index 52648f9..0000000 --- a/core/repository/ch_repository/channel_models.go +++ /dev/null @@ -1,56 +0,0 @@ -package ch_repository - -import ( - "time" - - mapset "github.com/deckarep/golang-set/v2" -) - -type Channel struct { - TgId int64 `csv:"tg_id" ch:"tg_id" parquet:"name=tg_id, type=INT64"` - TgTitle string `csv:"tg_title" ch:"tg_title" parquet:"name=tg_title, type=BYTE_ARRAY, convertedtype=UTF8, encoding=PLAIN_DICTIONARY"` - TgUsername string `csv:"tg_username" ch:"tg_username" parquet:"name=tg_username, type=BYTE_ARRAY, convertedtype=UTF8, encoding=PLAIN_DICTIONARY"` - UploadedAt time.Time ` ch:"uploaded_at"` - - ParticipantCount int64 `csv:"participant_count" ch:"participant_count" parquet:"name=participant_count, type=INT64"` - RecommendationsIds []int64 ` ch:"recommendations_ids"` - IsLeaf bool `csv:"is_leaf" ch:"is_leaf" parquet:"name=is_leaf, type=BOOLEAN"` - TgAbout *string `csv:"tg_about" ch:"tg_about" parquet:"name=tg_about, type=BYTE_ARRAY, convertedtype=UTF8, encoding=PLAIN_DICTIONARY"` -} - -type Channels []Channel - -func (r Channels) ToMap() map[int64]Channel { - map_ := make(map[int64]Channel, len(r)) - for _, channel := range r { - map_[channel.TgId] = channel - } - - return map_ -} - -type ChannelEdge struct { - TgIdIn int64 `csv:"tg_id_in" ch:"tg_id_in" parquet:"name=tg_id_in, type=INT64"` - TgIdOut int64 `csv:"tg_id_out" ch:"tg_id_out" parquet:"name=tg_id_out, type=INT64"` - Order int64 `csv:"order" ch:"order" parquet:"name=order, type=INT64"` -} - -type ChannelsEdges []ChannelEdge - -func (r ChannelsEdges) ToOutIds() []int64 { - ids := make([]int64, 0, len(r)) - for _, channelsEdge := range r { - ids = append(ids, channelsEdge.TgIdOut) - } - - return ids -} - -func (r ChannelsEdges) ToIds() []int64 { - ids := mapset.NewSet[int64]() - for _, channelsEdge := range r { - ids.Append(channelsEdge.TgIdOut, channelsEdge.TgIdIn) - } - - return ids.ToSlice() -} diff --git a/core/repository/ch_repository/channel_test.go b/core/repository/ch_repository/channel_test.go deleted file mode 100644 index b1d2f5e..0000000 --- a/core/repository/ch_repository/channel_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package ch_repository - -import ( - "database/sql" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/teadove/fun_telegram/core/shared" -) - -func TestIntegration_ChRepository_ChannelSelect_NotFound(t *testing.T) { - t.Parallel() - - r := getRepository(t) - ctx := shared.GetCtx() - - _, err := r.ChannelSelectById(ctx, 999) - - assert.ErrorIs(t, err, sql.ErrNoRows) -} - -func TestIntegration_ChRepository_ChannelSelect_Found(t *testing.T) { - t.Parallel() - - r := getRepository(t) - ctx := shared.GetCtx() - - err := r.ChannelInsert(ctx, &Channel{TgId: 12}) - require.NoError(t, err) - - channel, err := r.ChannelSelectById(ctx, 12) - assert.NoError(t, err) - assert.Equal(t, int64(12), channel.TgId) -} - -func TestIntegration_ChRepository_ChannelBatchInsert_Ok(t *testing.T) { - t.Parallel() - - r := getRepository(t) - ctx := shared.GetCtx() - - err := r.ChannelBatchInsert( - ctx, - []Channel{{TgId: 12, UploadedAt: time.Now()}, {TgId: 13, UploadedAt: time.Now()}}, - ) - assert.NoError(t, err) -} diff --git a/core/repository/ch_repository/init.go b/core/repository/ch_repository/init.go deleted file mode 100644 index cce179a..0000000 --- a/core/repository/ch_repository/init.go +++ /dev/null @@ -1,40 +0,0 @@ -package ch_repository - -var initSQL = []string{ - ` -CREATE TABLE IF NOT EXISTS message -( - created_at timestamp, - - tg_chat_id Int64, - tg_id Int64, - - tg_user_id Int64, - text String -) ENGINE = ReplacingMergeTree() ORDER BY (tg_chat_id, tg_id)`, - `ALTER TABLE message ADD COLUMN IF NOT EXISTS reply_to_msg_id Nullable(Int64)`, - `ALTER TABLE message ADD COLUMN IF NOT EXISTS reply_to_user_id Nullable(Int64)`, - `ALTER TABLE message ADD COLUMN IF NOT EXISTS words_count UInt64`, - `ALTER TABLE message ADD COLUMN IF NOT EXISTS toxic_words_count UInt64`, - `ALTER TABLE message DROP COLUMN IF EXISTS id`, - ` -CREATE TABLE IF NOT EXISTS channel -( - tg_id Int64, - tg_title String, - tg_username String, - uploaded_at timestamp, - - participant_count Int64, - recommendations_ids Array(Int64) -) ENGINE = ReplacingMergeTree() ORDER BY (tg_id);`, ` -create table if not exists channel_edge -( - tg_id_in Int64, - tg_id_out Int64, - order Int64 - -) ENGINE = ReplacingMergeTree() ORDER BY (tg_id_in, tg_id_out);`, - `alter table channel add column if not exists is_leaf bool default false;`, - `alter table channel add column if not exists tg_about Nullable(String);`, -} diff --git a/core/repository/ch_repository/message_methods.go b/core/repository/ch_repository/message_methods.go deleted file mode 100644 index 91c6e09..0000000 --- a/core/repository/ch_repository/message_methods.go +++ /dev/null @@ -1,636 +0,0 @@ -package ch_repository - -import ( - "context" - "time" - - "github.com/pkg/errors" -) - -func (r *Repository) MessageInsert(ctx context.Context, message *Message) error { - err := r.conn.AsyncInsert(ctx, ` -INSERT INTO message VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ? - )`, - false, - message.CreatedAt, - message.TgChatID, - message.TgId, - message.TgUserId, - message.Text, - message.ReplyToMsgID, - message.ReplyToUserID, - message.WordsCount, - message.ToxicWordsCount, - ) - if err != nil { - return errors.Wrap(err, "failed to async insert") - } - - return nil -} - -func (r *Repository) MessageSetReplyToUserId(ctx context.Context, chatId int64) error { - err := r.conn.AsyncInsert(ctx, ` -insert into message -select am.created_at, - am.tg_chat_id, - am.tg_id, - am.tg_user_id, - am.text, - am.reply_to_msg_id, - m.tg_user_id, - am.words_count, - am.toxic_words_count -from message am - join default.message m on m.tg_chat_id = am.tg_chat_id AND m.tg_id = am.reply_to_msg_id -where reply_to_msg_id is not null and reply_to_user_id is null and am.tg_chat_id = ?; -`, false, chatId) - if err != nil { - return errors.Wrap(err, "failed to async insert") - } - - return nil -} - -func (r *Repository) MessageDeleteByChatId(ctx context.Context, chatId int64) error { - err := r.conn.Exec(ctx, `DELETE FROM message WHERE tg_chat_id = ?`, chatId) - if err != nil { - return errors.Wrap(err, "failed to delete messages by chat") - } - - return nil -} - -type MessageFindInterlocutorsOutput struct { - TgUserId int64 `ch:"tg_user_id"` - MessagesCount uint64 `ch:"count"` -} - -func (r *Repository) MessageFindInterlocutors( - ctx context.Context, - chatId int64, - userId int64, - limit int, - interlocutorLimit time.Duration, -) ([]MessageFindInterlocutorsOutput, error) { - rows, err := r.conn.Query(ctx, ` -select m.tg_user_id as tg_user_id, count(1) as count -from message am final - join default.message m - on am.tg_chat_id = m.tg_chat_id - where am.tg_chat_id = ? - and am.tg_user_id = ? - and abs(am.created_at - m.created_at) - ? < 0 - group by 1 - order by 2 desc - limit ? -`, chatId, userId, int(interlocutorLimit.Seconds()), limit) - if err != nil { - return nil, errors.Wrap(err, "failed to find interlocutors") - } - - output := make([]MessageFindInterlocutorsOutput, 0, limit) - - for rows.Next() { - row := MessageFindInterlocutorsOutput{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) MessageFindRepliesTo( - ctx context.Context, - chatId int64, - userId int64, - minReplyCount int, - limit int, -) ([]MessageFindInterlocutorsOutput, error) { - rows, err := r.conn.Query(ctx, ` -select am.reply_to_user_id as tg_user_id, count(1) as count - from message am final -where am.tg_chat_id = ? and am.tg_user_id = ? and am.reply_to_user_id != 0 and am.reply_to_user_id is not null -group by 1 - having count(1) > ? -order by 2 desc limit ? -`, chatId, userId, minReplyCount, limit) - if err != nil { - return nil, errors.Wrap(err, "failed to find interlocutors") - } - - output := make([]MessageFindInterlocutorsOutput, 0, limit) - - for rows.Next() { - row := MessageFindInterlocutorsOutput{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) MessageFindRepliedBy( - ctx context.Context, - chatId int64, - userId int64, - minReplyCount int, - limit int, -) ([]MessageFindInterlocutorsOutput, error) { - rows, err := r.conn.Query(ctx, ` -select am.tg_user_id as tg_user_id, count(1) as count - from message am final - where am.tg_chat_id = ? and am.reply_to_user_id = ? - group by 1 - having count(1) > ? - order by 2 desc - LIMIT ?; -`, chatId, userId, minReplyCount, limit) - if err != nil { - return nil, errors.Wrap(err, "failed to find interlocutors") - } - - output := make([]MessageFindInterlocutorsOutput, 0, limit) - - for rows.Next() { - row := MessageFindInterlocutorsOutput{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -type MessageFindAllRepliedByOutput struct { - TgUserId int64 `ch:"tg_user_id"` - RepliedTgUserId int64 `ch:"replied_tg_user_id"` - Count uint64 `ch:"count"` -} - -func (r *Repository) MessageFindAllRepliedBy( - ctx context.Context, - chatId int64, - limit int, -) ([]MessageFindAllRepliedByOutput, error) { - rows, err := r.conn.Query(ctx, ` -select am.tg_user_id as tg_user_id, am.reply_to_user_id as replied_tg_user_id, count(1) as count - from message am final - where am.reply_to_user_id != am.tg_user_id and tg_chat_id = ? and m.reply_to_user_id != 0 and m.reply_to_user_id is not null - group by 1, 2 - order by 3 desc - limit ?; -`, chatId, limit) - if err != nil { - return nil, errors.Wrap(err, "failed to find interlocutors") - } - - output := make([]MessageFindAllRepliedByOutput, 0, limit) - - for rows.Next() { - row := MessageFindAllRepliedByOutput{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) MessageGetByChatIdAndUserId( - ctx context.Context, - chatId int64, - userId int64, -) ([]Message, error) { - rows, err := r.conn.Query(ctx, ` -select created_at, tg_chat_id, tg_id, tg_user_id, text, reply_to_msg_id, reply_to_user_id from message final - where tg_chat_id = ? - and tg_user_id = ? -`, chatId, userId) - if err != nil { - return nil, errors.Wrap(err, "failed to find interlocutors") - } - - output := make([]Message, 0, 100) - - for rows.Next() { - row := Message{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -type GroupedCountGetByChatIdByUserIdOutput struct { - TgUserId int64 `ch:"tg_user_id"` - WordsCount uint64 `ch:"words_count"` - ToxicWordsCount uint64 `ch:"toxic_words_count"` -} - -func (r *Repository) GroupedCountGetByChatIdByUserId( - ctx context.Context, - chatId int64, - limit int64, - userIds []int64, -) ([]GroupedCountGetByChatIdByUserIdOutput, error) { - rows, err := r.conn.Query(ctx, ` -with "user" as (select arrayJoin(cast(?, 'Array(Int64)')) as "id") -select tg_user_id, sum(words_count) as "words_count", sum(toxic_words_count) as "toxic_words_count" - from "user" u - left join message m on m.tg_user_id = u.id - where m.tg_chat_id = ? - group by 1 - order by 2 desc - limit ?; -`, userIds, chatId, limit) - if err != nil { - return nil, errors.Wrap(err, "failed to find interlocutors") - } - - output := make([]GroupedCountGetByChatIdByUserIdOutput, 0, 100) - - for rows.Next() { - row := GroupedCountGetByChatIdByUserIdOutput{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -const ( - GroupedCountGetByChatIdByUserIdQueryAsc = ` -with "user" as (select arrayJoin(cast(?, 'Array(Int64)')) as "id") -select u.id as "tg_user_id", sum(words_count) as "words_count", sum(toxic_words_count) as "toxic_words_count" - from "user" u - left join message m on m.tg_user_id = u.id -where m.tg_chat_id = ? - group by 1 - order by 2 asc - limit ?; -` - GroupedCountGetByChatIdByUserIdQueryDesc = ` -with "user" as (select arrayJoin(cast(?, 'Array(Int64)')) as "id") -select u.id as "tg_user_id", sum(words_count) as "words_count", sum(toxic_words_count) as "toxic_words_count" - from "user" u - left join message m on m.tg_user_id = u.id -where m.tg_chat_id = ? - group by 1 - order by 2 desc - limit ?; -` -) - -func (r *Repository) GroupedCountGetByChatIdByUserIdAsc( - ctx context.Context, - chatId int64, - limit int64, - userIds []int64, - asc bool, -) ([]GroupedCountGetByChatIdByUserIdOutput, error) { - var query string - if asc { - query = GroupedCountGetByChatIdByUserIdQueryAsc - } else { - query = GroupedCountGetByChatIdByUserIdQueryDesc - } - rows, err := r.conn.Query(ctx, query, userIds, chatId, limit) - if err != nil { - return nil, errors.Wrap(err, "failed to select messages with cte") - } - - output := make([]GroupedCountGetByChatIdByUserIdOutput, 0, 100) - - for rows.Next() { - row := GroupedCountGetByChatIdByUserIdOutput{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) CountGetByChatId( - ctx context.Context, - chatId int64, -) (uint64, error) { - row := r.conn.QueryRow(ctx, ` -select count() as count from message final - where tg_chat_id = ? -`, chatId) - if row.Err() != nil { - return 0, errors.Wrap(row.Err(), "failed to query row") - } - - var count uint64 - err := row.Scan(&count) - if err != nil { - return 0, errors.Wrap(err, "failed to scan row") - } - - return count, nil -} - -func (r *Repository) CountGetByChatIdByUserId( - ctx context.Context, - chatId int64, - userId int64, -) (uint64, error) { - row := r.conn.QueryRow(ctx, ` -select count() as count from message final - where tg_chat_id = ? - and tg_user_id = ? -`, chatId, userId) - if row.Err() != nil { - return 0, errors.Wrap(row.Err(), "failed to query row") - } - - var count uint64 - err := row.Scan(&count) - if err != nil { - return 0, errors.Wrap(err, "failed to scan row") - } - - return count, nil -} - -func (r *Repository) MessageGetByChatId( - ctx context.Context, - chatId int64, -) ([]Message, error) { - rows, err := r.conn.Query(ctx, ` -select created_at, tg_chat_id, tg_id, tg_user_id, text, reply_to_msg_id, reply_to_user_id from message final - where tg_chat_id = ? -`, chatId) - if err != nil { - return nil, errors.Wrap(err, "failed to find interlocutors") - } - - output := make([]Message, 0, 100) - - for rows.Next() { - row := Message{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) GetLastMessageByChatId(ctx context.Context, chatId int64) (Message, error) { - row := r.conn.QueryRow(ctx, ` -select created_at, tg_chat_id, tg_id, tg_user_id, text, reply_to_msg_id, reply_to_user_id from message final - where tg_chat_id = ? - order by created_at limit 1 -`, chatId) - if row.Err() != nil { - return Message{}, errors.Wrap(row.Err(), "failed to select row from clickhouse") - } - - var message Message - err := row.ScanStruct(&message) - if err != nil { - return Message{}, errors.Wrap(err, "failed to scan row") - } - - return message, nil -} - -func (r *Repository) GetLastMessageByChatIdByUserId( - ctx context.Context, - chatId int64, - userId int64, -) (Message, error) { - row := r.conn.QueryRow(ctx, ` -select created_at, tg_chat_id, tg_id, tg_user_id, text, reply_to_msg_id, reply_to_user_id from message final - where tg_chat_id = ? and tg_user_id = ? - order by created_at limit 1 -`, chatId, userId) - if row.Err() != nil { - return Message{}, errors.Wrap(row.Err(), "failed to select row from clickhouse") - } - - var message Message - err := row.ScanStruct(&message) - if err != nil { - return Message{}, errors.Wrap(row.Err(), "failed to scan row") - } - - return message, err -} - -type MessagesGroupedByTime struct { - CreatedAt time.Time `ch:"created_at"` - WordsCount uint64 `ch:"words_count"` -} - -type MessagesGroupedByTimeByWeekday struct { - IsWeekend bool `ch:"is_weekend"` - CreatedAt time.Time `ch:"created_at"` - WordsCount uint64 `ch:"words_count"` -} - -// GetMessagesGroupedByDateByChatId -// precision = 60 means per minute, 86400 - per day -func (r *Repository) GetMessagesGroupedByDateByChatId( - ctx context.Context, - chatId int64, - precision int, -) ([]MessagesGroupedByTime, error) { - rows, err := r.conn.Query(ctx, ` - select fromUnixTimestamp(intDiv(toUnixTimestamp(created_at), ?) * ?) as "created_at", - sum(words_count) as "words_count" - from message final - where tg_chat_id = ? - group by 1 - order by 1 desc; -`, precision, precision, chatId) - if err != nil { - return nil, errors.Wrap(err, "failed to query rows") - } - - output := make([]MessagesGroupedByTime, 0, 100) - - for rows.Next() { - row := MessagesGroupedByTime{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) GetMessagesGroupedByDateByChatIdByUserId( - ctx context.Context, - chatId int64, - userId int64, - precision int, -) ([]MessagesGroupedByTime, error) { - rows, err := r.conn.Query(ctx, ` - select fromUnixTimestamp(intDiv(toUnixTimestamp(created_at), ?) * ?) as "created_at", sum(m.words_count) as "words_count" - from message m final - where tg_chat_id = ? and tg_user_id = ? - group by 1 - order by 1 desc; -`, precision, precision, chatId, userId) - if err != nil { - return nil, errors.Wrap(err, "failed to query rows") - } - - output := make([]MessagesGroupedByTime, 0, 100) - - for rows.Next() { - row := MessagesGroupedByTime{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -// GetMessagesGroupedByTimeByChatId -// precision = 60 means per minute, 86400 - per day -func (r *Repository) GetMessagesGroupedByTimeByChatId( - ctx context.Context, - chatId int64, - precision int, - tz int8, -) ([]MessagesGroupedByTimeByWeekday, error) { - rows, err := r.conn.Query(ctx, ` - select case when toDayOfWeek(m.created_at + interval ? hour) >= 6 then true else false end as is_weekend, - toTime(fromUnixTimestamp(intDiv(toUnixTimestamp(created_at + interval ? hour), ?) * ?)) as created_at, - sum(m.words_count) as words_count - from message m final - where tg_chat_id = ? - group by 1, 2 - order by 1 desc; -`, tz, tz, precision, precision, chatId) - if err != nil { - return nil, errors.Wrap(err, "failed to query rows") - } - - output := make([]MessagesGroupedByTimeByWeekday, 0, 100) - - for rows.Next() { - row := MessagesGroupedByTimeByWeekday{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) GetMessagesGroupedByTimeByChatIdByUserId( - ctx context.Context, - chatId int64, - userId int64, - precision int, - tz int8, -) ([]MessagesGroupedByTimeByWeekday, error) { - rows, err := r.conn.Query(ctx, ` - select case when toDayOfWeek(m.created_at + interval ? hour) >= 6 then true else false end as is_weekend, - toTime(fromUnixTimestamp(intDiv(toUnixTimestamp(created_at + interval ? hour), ?) * ?)) as "created_at", - sum(m.words_count) as "words_count" - from message m final - where tg_chat_id = ? and tg_user_id = ? - group by 1, 2 - order by 1 desc; -`, tz, tz, precision, precision, chatId, userId) - if err != nil { - return nil, errors.Wrap(err, "failed to query rows") - } - - output := make([]MessagesGroupedByTimeByWeekday, 0, 100) - - for rows.Next() { - row := MessagesGroupedByTimeByWeekday{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} - -func (r *Repository) MessagesGetByChatIds( - ctx context.Context, - tgChatIds []int64, -) ([]Message, error) { - rows, err := r.conn.Query(ctx, ` -select m.created_at, - m.tg_chat_id, - m.tg_id, - m.tg_user_id, - m.text, - m.reply_to_msg_id, - m.reply_to_user_id, - m.words_count, - m.toxic_words_count -from message m final - where m.tg_chat_id in ? -`, tgChatIds) - if err != nil { - return nil, errors.Wrap(err, "failed to select messages") - } - - output := make([]Message, 0, 100) - - for rows.Next() { - row := Message{} - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - output = append(output, row) - } - - return output, nil -} diff --git a/core/repository/ch_repository/message_models.go b/core/repository/ch_repository/message_models.go deleted file mode 100644 index 04ae6d6..0000000 --- a/core/repository/ch_repository/message_models.go +++ /dev/null @@ -1,57 +0,0 @@ -package ch_repository - -import ( - "time" - - "github.com/guregu/null/v5" -) - -type Message struct { - CreatedAt time.Time `csv:"created_at" ch:"created_at"` - - TgChatID int64 `csv:"tg_chat_id" ch:"tg_chat_id"` - TgId int64 `csv:"tg_id" ch:"tg_id"` - TgUserId int64 `csv:"tg_user_id" ch:"tg_user_id"` - Text string `csv:"text" ch:"text"` - WordsCount uint64 `csv:"words_count" ch:"words_count"` - ToxicWordsCount uint64 `csv:"toxic_words_count" ch:"toxic_words_count"` - - ReplyToMsgID null.Int64 `csv:"reply_to_msg_id" ch:"reply_to_msg_id"` - ReplyToUserID null.Int64 `csv:"reply_to_user_id" ch:"reply_to_user_id"` -} - -func (r *Message) ToParquet() MessageParquet { - message := MessageParquet{ - CreatedAt: r.CreatedAt.UnixNano() / int64(time.Millisecond), - TgChatID: r.TgChatID, - TgId: r.TgId, - TgUserId: r.TgUserId, - Text: r.Text, - WordsCount: int64(r.WordsCount), - ToxicWordsCount: int64(r.ToxicWordsCount), - } - - if r.ReplyToMsgID.Valid { - message.ReplyToMsgID = &r.ReplyToMsgID.Int64 - } - - if r.ReplyToUserID.Valid { - message.ReplyToUserID = &r.ReplyToUserID.Int64 - } - - return message -} - -type MessageParquet struct { - CreatedAt int64 `parquet:"name=created_at, type=INT64, convertedtype=TIMESTAMP_MILLIS"` - - TgChatID int64 `parquet:"name=tg_chat_id, type=INT64"` - TgId int64 `parquet:"name=tg_id, type=INT64"` - TgUserId int64 `parquet:"name=tg_user_id, type=INT64"` - Text string `parquet:"name=text, type=BYTE_ARRAY, convertedtype=UTF8, encoding=PLAIN_DICTIONARY"` - WordsCount int64 `parquet:"name=words_count, type=INT64"` - ToxicWordsCount int64 `parquet:"name=toxic_words_count, type=INT64"` - - ReplyToMsgID *int64 `parquet:"name=reply_to_msg_id, type=INT64"` - ReplyToUserID *int64 `parquet:"name=reply_to_user_id, type=INT64"` -} diff --git a/core/repository/ch_repository/repository.go b/core/repository/ch_repository/repository.go deleted file mode 100644 index 994bd9b..0000000 --- a/core/repository/ch_repository/repository.go +++ /dev/null @@ -1,94 +0,0 @@ -package ch_repository - -import ( - "context" - "fmt" - "time" - - "github.com/ClickHouse/clickhouse-go/v2" - "github.com/ClickHouse/clickhouse-go/v2/lib/driver" - "github.com/pkg/errors" - "github.com/rs/zerolog" - "github.com/teadove/fun_telegram/core/shared" -) - -type Repository struct { - conn driver.Conn - databaseName string -} - -func New(ctx context.Context) (*Repository, error) { - r := Repository{databaseName: "default"} - conn, err := clickhouse.Open(&clickhouse.Options{ - Addr: []string{shared.AppSettings.Storage.ClickhouseUtl}, - Auth: clickhouse.Auth{ - Database: r.databaseName, - Username: "default", - }, - Settings: clickhouse.Settings{ - "max_execution_time": 60 * 3, - "max_query_size": 3000000000000, - }, - Protocol: clickhouse.Native, - // Debug: true, - Debugf: func(format string, v ...any) { - zerolog.Ctx(ctx). - Debug(). - Str("status", "ch.log"). - Str("log", fmt.Sprintf(format, v...)). - Send() - }, - Compression: &clickhouse.Compression{ - Method: clickhouse.CompressionLZ4, - }, - DialTimeout: time.Second * 30, - MaxOpenConns: 5, - MaxIdleConns: 5, - ConnMaxLifetime: time.Duration(20) * time.Minute, - ConnOpenStrategy: clickhouse.ConnOpenInOrder, - BlockBufferSize: 10, - MaxCompressionBuffer: 10240, - ClientInfo: clickhouse.ClientInfo{ - Products: []struct { - Name string - Version string - }{ - {Name: "FunTelegram", Version: shared.Undefined}, - }, - }, - }) - if err != nil { - return nil, errors.WithStack(err) - } - - r.conn = conn - go r.init(ctx) - - return &r, nil -} - -func (r *Repository) Ping(ctx context.Context) error { - err := r.conn.Ping(ctx) - if err != nil { - return errors.WithStack(err) - } - - return nil -} - -func (r *Repository) init(ctx context.Context) { - t0 := time.Now() - - for _, sql := range initSQL { - err := r.conn.Exec(ctx, sql) - if err != nil { - zerolog.Ctx(ctx).Error().Stack().Err(err).Str("status", "failed.to.run.init.sql").Send() - } - } - - zerolog.Ctx(ctx). - Info(). - Str("status", "ch.migration.applied"). - Dur("elapsed", time.Since(t0)). - Send() -} diff --git a/core/repository/ch_repository/repository_test.go b/core/repository/ch_repository/repository_test.go deleted file mode 100644 index 158d15f..0000000 --- a/core/repository/ch_repository/repository_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package ch_repository - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/teadove/fun_telegram/core/shared" -) - -func getRepository(t *testing.T) *Repository { - r, err := New(shared.GetCtx()) - require.NoError(t, err) - - return r -} - -func TestIntegration_ChRepository_Ping_Ok(t *testing.T) { - t.Parallel() - - r := getRepository(t) - - err := r.Ping(shared.GetCtx()) - assert.NoError(t, err) -} - -func TestIntegration_ChRepository_MessageFindInterlocutors_Ok(t *testing.T) { - t.Parallel() - - r := getRepository(t) - - output, err := r.MessageFindInterlocutors( - shared.GetCtx(), - 1701683862, - 418878871, - 10, - time.Minute*5, - ) - assert.NoError(t, err) - shared.SendInterface(output) -} diff --git a/core/repository/ch_repository/stats.go b/core/repository/ch_repository/stats.go deleted file mode 100644 index 6715c6a..0000000 --- a/core/repository/ch_repository/stats.go +++ /dev/null @@ -1,49 +0,0 @@ -package ch_repository - -import ( - "context" - - "github.com/pkg/errors" - "github.com/teadove/fun_telegram/core/schemas" -) - -func (r *Repository) StatsForDatabase( - ctx context.Context, -) (map[string]schemas.StorageStats, error) { - rows, err := r.conn.Query(ctx, ` -SELECT table as table, - sum(bytes) AS bytes, - sum(rows) AS count -FROM system.parts -WHERE active and database = ? -GROUP BY 1 -ORDER BY bytes DESC -`, r.databaseName) - if err != nil { - return nil, errors.Wrap(err, "failed to get stats from database") - } - - type record struct { - Table string `ch:"table"` - TotalSizeBytes uint64 `ch:"bytes"` - Count uint64 `ch:"count"` - } - - map_ := make(map[string]schemas.StorageStats, 3) - - for rows.Next() { - var row record - err = rows.ScanStruct(&row) - if err != nil { - return nil, errors.Wrap(err, "failed to scan row") - } - - map_[row.Table] = schemas.StorageStats{ - TotalSizeBytes: int(row.TotalSizeBytes), - Count: int(row.Count), - AvgObjWithIndexSizeBytes: int(row.TotalSizeBytes / row.Count), - } - } - - return map_, nil -} diff --git a/core/repository/db_repository/base.go b/core/repository/db_repository/base.go new file mode 100644 index 0000000..f249615 --- /dev/null +++ b/core/repository/db_repository/base.go @@ -0,0 +1,25 @@ +package db_repository + +import ( + "time" +) + +type WithId struct { + ID uint `gorm:"primarykey"` +} + +type WithCreatedAt struct { + CreatedAt time.Time `gorm:"index"` +} + +type WithCreatedInDBAt struct { + CreatedInDBAt time.Time `gorm:"index"` +} + +type WithUpdatedAt struct { + UpdatedAt time.Time `gorm:"index"` +} + +type WithUpdatedInDBAt struct { + UpdatedInDBAt time.Time `gorm:"index"` +} diff --git a/core/repository/db_repository/channel_methods.go b/core/repository/db_repository/channel_methods.go new file mode 100644 index 0000000..14f23c7 --- /dev/null +++ b/core/repository/db_repository/channel_methods.go @@ -0,0 +1,126 @@ +package db_repository + +import ( + "context" + + mapset "github.com/deckarep/golang-set/v2" + + "github.com/pkg/errors" +) + +func (r *Repository) ChannelInsert(ctx context.Context, channel *Channel) error { + err := r.db.WithContext(ctx).Create(&channel).Error + if err != nil { + return errors.Wrap(err, "failed to insert channel") + } + + return nil +} + +func (r *Repository) ChannelEdgeBatchInsert(ctx context.Context, channels ChannelsEdges) error { + err := r.db.WithContext(ctx).Create(&channels).Error + if err != nil { + return errors.Wrap(err, "failed to insert channels edges") + } + + return nil +} + +func (r *Repository) ChannelBatchInsert(ctx context.Context, channels []Channel) error { + err := r.db.WithContext(ctx).Create(&channels).Error + if err != nil { + return errors.Wrap(err, "failed to insert channels") + } + + return nil +} + +func (r *Repository) ChannelSelectByUsername( + ctx context.Context, + tgUsername string, +) (Channel, error) { + var channel Channel + + err := r.db.WithContext(ctx).First(&channel, "tg_username = ?", tgUsername).Error + if err != nil { + return Channel{}, errors.Wrap(err, "failed to query channel by username") + } + + return channel, nil +} + +func (r *Repository) ChannelSelectById(ctx context.Context, tgId int64) (Channel, error) { + var channel Channel + + err := r.db.WithContext(ctx).First(&channel, "tg_id = ?", tgId).Error + if err != nil { + return Channel{}, errors.Wrap(err, "failed to query channel by id") + } + + return channel, nil +} + +func (r *Repository) ChannelSelectByIds(ctx context.Context, tgIds []int64) (Channels, error) { + var channels []Channel + + err := r.db.WithContext(ctx).Find(&channels, "tg_id in (?)", tgIds).Error + if err != nil { + return nil, errors.Wrap(err, "failed to query channel by ids") + } + + return channels, nil +} + +func (r *Repository) ChannelEdgesSelect( + ctx context.Context, + maxOrder int64, +) (ChannelsEdges, error) { + var out ChannelsEdges + err := r.db.WithContext(ctx).Find(out, "order <= ?", maxOrder).Error + if err != nil { + return nil, errors.Wrap(err, "failed to query channel edges") + } + + return out, nil +} + +func (r *Repository) ChannelEdgesSelectById( + ctx context.Context, + tgIdIn []int64, + maxOrder int64, +) (ChannelsEdges, error) { + var out ChannelsEdges + err := r.db.WithContext(ctx).Find(out, "order <= ? AND tg_id_in = ?", maxOrder, tgIdIn).Error + if err != nil { + return nil, errors.Wrap(err, "failed to query channel edges") + } + + return out, nil +} + +func (r *Repository) ChannelEdgesSelectDFS( + ctx context.Context, + tgUsername string, + depth int64, + maxOrder int64, +) (ChannelsEdges, error) { + channel, err := r.ChannelSelectByUsername(ctx, tgUsername) + if err != nil { + return nil, errors.Wrap(err, "failed to select channel by username") + } + + channelEdgesResult := mapset.NewSet[ChannelEdge]() + tgIds := []int64{channel.TgId} + + for range depth { + channelEdges, err := r.ChannelEdgesSelectById(ctx, tgIds, maxOrder) + if err != nil { + return nil, errors.Wrap(err, "failed to select channel edges by id") + } + + channelEdgesResult.Append(channelEdges...) + tgIds = channelEdges.ToOutIds() + } + + return channelEdgesResult.ToSlice(), nil +} diff --git a/core/repository/db_repository/channel_model.go b/core/repository/db_repository/channel_model.go new file mode 100644 index 0000000..a45a306 --- /dev/null +++ b/core/repository/db_repository/channel_model.go @@ -0,0 +1,61 @@ +package db_repository + +import ( + mapset "github.com/deckarep/golang-set/v2" + "github.com/guregu/null/v5" + "github.com/lib/pq" +) + +type Channel struct { + WithId + WithCreatedInDBAt + + TgId int64 `sql:"tg_id" parquet:"name=tg_id, type=INT64"` + TgTitle string `sql:"tg_title" parquet:"name=tg_title, type=BYTE_ARRAY, convertedtype=UTF8, encoding=PLAIN_DICTIONARY"` + TgUsername string `sql:"tg_username" parquet:"name=tg_username, type=BYTE_ARRAY, convertedtype=UTF8, encoding=PLAIN_DICTIONARY"` + + ParticipantCount int64 `sql:"participant_count" parquet:"name=participant_count, type=INT64"` + RecommendationsIds pq.Int64Array `sql:"recommendations_ids" gorm:"type:integer[]"` + IsLeaf bool `sql:"is_leaf" parquet:"name=is_leaf, type=BOOLEAN"` + TgAbout null.String `sql:"tg_about" parquet:"name=tg_about, type=BYTE_ARRAY, convertedtype=UTF8, encoding=PLAIN_DICTIONARY"` +} + +type Channels []Channel + +func (r Channels) ToMap() map[int64]Channel { + map_ := make(map[int64]Channel, len(r)) + for _, channel := range r { + map_[channel.TgId] = channel + } + + return map_ +} + +type ChannelEdge struct { + WithId + WithCreatedAt + + TgIdIn int64 `sql:"tg_id_in" parquet:"name=tg_id_in, type=INT64"` + TgIdOut int64 `sql:"tg_id_out" parquet:"name=tg_id_out, type=INT64"` + Order int64 `sql:"order" parquet:"name=order, type=INT64"` +} + +type ChannelsEdges []ChannelEdge + +func (r ChannelsEdges) ToOutIds() []int64 { + ids := make([]int64, 0, len(r)) + for _, channelsEdge := range r { + ids = append(ids, channelsEdge.TgIdOut) + } + + return ids +} + +func (r ChannelsEdges) ToIds() []int64 { + ids := mapset.NewSet[int64]() + for _, channelsEdge := range r { + ids.Append(channelsEdge.TgIdOut, channelsEdge.TgIdIn) + } + + return ids.ToSlice() +} diff --git a/core/repository/db_repository/chat_methods.go b/core/repository/db_repository/chat_methods.go new file mode 100644 index 0000000..7f539a4 --- /dev/null +++ b/core/repository/db_repository/chat_methods.go @@ -0,0 +1,31 @@ +package db_repository + +import ( + "context" + + "github.com/pkg/errors" + "gorm.io/gorm/clause" +) + +func (r *Repository) ChatUpsert(ctx context.Context, row *Chat) error { + err := r.db.WithContext(ctx).Clauses( + clause.OnConflict{ + Columns: []clause.Column{{Name: "tg_id"}}, + DoUpdates: clause.AssignmentColumns([]string{"tg_id", "title", "updated_at"}), + }). + Create(&row).Error + if err != nil { + return errors.Wrap(err, "failed to upsert chat") + } + + return nil +} + +func (r *Repository) ChatSelectById(ctx context.Context, tgId int64) (chat Chat, err error) { + err = r.db.WithContext(ctx).Where("tg_id = ?", tgId).Find(&chat).Limit(1).Error + if err != nil { + return Chat{}, errors.Wrap(err, "failed to get chat") + } + + return chat, nil +} diff --git a/core/repository/db_repository/chat_model.go b/core/repository/db_repository/chat_model.go new file mode 100644 index 0000000..f9e88b7 --- /dev/null +++ b/core/repository/db_repository/chat_model.go @@ -0,0 +1,38 @@ +package db_repository + +type Chat struct { + WithId + WithCreatedAt + WithUpdatedAt + + TgId int64 `sql:"tg_id" gorm:"index:,unique"` + Title string ` gorm:"index"` +} + +type UserInChat struct { + TgId int64 + TgUsername string + TgName string + IsBot bool + Status MemberStatus +} + +type UsersInChat []UserInChat + +func (r UsersInChat) ToMap() map[int64]UserInChat { + map_ := make(map[int64]UserInChat, len(r)) + for _, user := range r { + map_[user.TgId] = user + } + + return map_ +} + +func (r UsersInChat) ToIds() []int64 { + slice := make([]int64, len(r)) + for _, user := range r { + slice = append(slice, user.TgId) + } + + return slice +} diff --git a/core/repository/db_repository/image_methods.go b/core/repository/db_repository/image_methods.go new file mode 100644 index 0000000..572668c --- /dev/null +++ b/core/repository/db_repository/image_methods.go @@ -0,0 +1,125 @@ +package db_repository + +import ( + "context" + + "github.com/pkg/errors" +) + +func (r *Repository) KandinskyImageInsert( + ctx context.Context, + input *KandinskyImageDenormalized, +) error { + image := Image{Content: input.ImgContent} + err := r.db.WithContext(ctx).Create(&image).Error + if err != nil { + return errors.Wrap(err, "could not insert image") + } + + err = r.db.WithContext(ctx).Create(&input.Message).Error + if err != nil { + return errors.Wrap(err, "could not insert message") + } + + tgImage := TgImage{ + TgInputPhoto: input.TgInputPhoto, + MessageId: input.Message.ID, + ImageId: image.ID, + } + err = r.db.WithContext(ctx).Create(&tgImage).Error + if err != nil { + return errors.Wrap(err, "could not insert image") + } + + err = r.db.WithContext(ctx).Create(&KandinskyImage{ + Input: input.KandinskyInput, + TgImageId: tgImage.ID, + ImageId: image.ID, + }).Error + if err != nil { + return errors.Wrap(err, "could not insert image") + } + + return nil +} + +func (r *Repository) KandinskyImagePaginate( + ctx context.Context, + input *KandinskyImagePaginateInput, +) ([]KandinskyImageDenormalized, error) { + var kandinskyImages []KandinskyImage + err := r.db.WithContext(ctx). + Find(&kandinskyImages). + Where("tg_chat_id = ?", input.TgChatId). + Offset(input.Page * input.PageSize). + Limit(input.PageSize). + Error + if err != nil { + return nil, errors.Wrap(err, "could not get kandinsky images") + } + + tgImageIds := make([]uint, 0, len(kandinskyImages)) + for _, img := range kandinskyImages { + tgImageIds = append(tgImageIds, img.TgImageId) + } + + var tgImages []TgImage + err = r.db.WithContext(ctx). + Find(&tgImages). + Where("id in (?)", tgImageIds). + Error + if err != nil { + return nil, errors.Wrap(err, "could not get tg images") + } + + imageIds := make([]uint, 0, len(kandinskyImages)) + for _, img := range kandinskyImages { + imageIds = append(imageIds, img.ImageId) + } + + var images []Image + err = r.db.WithContext(ctx). + Find(&images). + Where("id in (?)", imageIds). + Error + if err != nil { + return nil, errors.Wrap(err, "could not get tg images") + } + + messageIds := make([]uint, 0, len(kandinskyImages)) + for _, img := range tgImages { + messageIds = append(messageIds, img.MessageId) + } + + var messages []Message + err = r.db.WithContext(ctx). + Find(&messages). + Where("id in (?)", messageIds). + Error + if err != nil { + return nil, errors.Wrap(err, "could not get messages") + } + + if len(images) != len(tgImages) || + len(images) != len(kandinskyImages) || + len(images) != len(messages) { + return nil, errors.Errorf("image count mismatch %d != %d != %d != %d", + len(images), + len(tgImages), + len(kandinskyImages), + len(messages), + ) + } + + imagesDenormalized := make([]KandinskyImageDenormalized, 0, len(kandinskyImages)) + for idx, kandindkyImage := range kandinskyImages { + imagesDenormalized = append(imagesDenormalized, KandinskyImageDenormalized{ + TgInputPhoto: tgImages[idx].TgInputPhoto, + KandinskyInput: kandindkyImage.Input, + ImgContent: images[idx].Content, + Message: messages[idx], + }) + } + + return imagesDenormalized, nil +} diff --git a/core/repository/db_repository/image_model.go b/core/repository/db_repository/image_model.go new file mode 100644 index 0000000..c674a51 --- /dev/null +++ b/core/repository/db_repository/image_model.go @@ -0,0 +1,46 @@ +package db_repository + +import ( + "github.com/gotd/td/tg" + "github.com/teadove/fun_telegram/core/supplier/kandinsky_supplier" +) + +type Image struct { + WithId + WithCreatedAt + + Content []byte `bson:"content"` +} + +type TgImage struct { + WithId + WithCreatedAt + + TgInputPhoto tg.InputPhoto `sql:"tg_input_photo" gorm:"embedded"` + + MessageId uint `sql:"message_id"` + ImageId uint `sql:"image_id"` +} + +type KandinskyImage struct { + WithId + WithCreatedAt + + Input kandinsky_supplier.RequestGenerationInput `sql:"input" gorm:"embedded"` + + TgImageId uint `sql:"tg_image_id"` + ImageId uint `sql:"image_id"` +} + +type KandinskyImageDenormalized struct { + TgInputPhoto tg.InputPhoto + KandinskyInput kandinsky_supplier.RequestGenerationInput + ImgContent []byte + Message Message +} + +type KandinskyImagePaginateInput struct { + TgChatId int64 + Page int + PageSize int +} diff --git a/core/repository/db_repository/message_methods.go b/core/repository/db_repository/message_methods.go new file mode 100644 index 0000000..ca992c7 --- /dev/null +++ b/core/repository/db_repository/message_methods.go @@ -0,0 +1,402 @@ +package db_repository + +import ( + "context" + "time" + + "gorm.io/gorm/clause" + + "gorm.io/gorm" + + "github.com/pkg/errors" +) + +func (r *Repository) messageSetReplyToUserId( + ctx context.Context, + tx *gorm.DB, + input *Message, +) error { + if input.ReplyToTgMsgID.Valid { + err := tx.WithContext(ctx). + Model(&Message{}). + Where("reply_to_tg_msg_id = ? AND tg_chat_id = ?", input.ReplyToTgMsgID.Int64, input.TgChatID). + Update("reply_to_tg_user_id", input.TgUserId). + Error + if err != nil { + return errors.Wrap(err, "failed to update reply_to_user_id") + } + } + + // TODO set replied message + return nil +} + +func (r *Repository) MessageInsert(ctx context.Context, input *Message) error { + err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + err := tx.WithContext(ctx).Clauses( + clause.OnConflict{ + Columns: []clause.Column{{Name: "tg_chat_id"}, {Name: "tg_id"}}, + DoUpdates: clause.AssignmentColumns( + []string{"text", "words_count", "toxic_words_count"}, + ), + }).Create(input).Error + if err != nil { + return errors.Wrap(err, "failed to insert message") + } + + err = r.messageSetReplyToUserId(ctx, tx, input) + if err != nil { + return errors.Wrap(err, "failed to set reply to user id") + } + + return nil + }) + if err != nil { + return errors.Wrap(err, "failed to insert message") + } + + return nil +} + +func (r *Repository) MessagesDeleteByChat(ctx context.Context, tgChatId int64) (uint64, error) { + resp := r.db.Delete(&Message{}, "tg_chat_id = ?", tgChatId).WithContext(ctx) + if resp.Error != nil { + return 0, errors.Wrap(resp.Error, "failed to delete message") + } + + return uint64(resp.RowsAffected), nil +} + +func (r *Repository) MessageCountByChatId( + ctx context.Context, + tgChatId int64, +) (uint64, error) { + var count int64 + err := r.db. + WithContext(ctx). + Model(&Message{}). + Where("tg_chat_id = ?", tgChatId). + Count(&count). + Error + if err != nil { + return 0, errors.Wrap(err, "failed to count message") + } + + return uint64(count), nil +} + +func (r *Repository) MessageCountByChatIdAndUserId( + ctx context.Context, + tgChatId int64, + tgUserId int64, +) (uint64, error) { + var count int64 + err := r.db. + WithContext(ctx). + Model(&Message{}). + Where("tg_chat_id = ? AND tg_user_id = ?", tgChatId, tgUserId). + Count(&count). + Error + if err != nil { + return 0, errors.Wrap(err, "failed to count message") + } + + return uint64(count), nil +} + +func (r *Repository) MessageGroupByChatIdAndUserId( + ctx context.Context, + tgChatId int64, + tgUserIds []int64, + limit int64, + desc bool, +) ([]MessageGroupByChatIdAndUserIdOutput, error) { + var output []MessageGroupByChatIdAndUserIdOutput + + const queryByAsc = ` +select + tg_user_id, + sum(words_count) as "words_count", + sum(toxic_words_count) as "toxic_words_count" +from message where tg_chat_id = ? and tg_user_id in (?) +group by 1 +order by 2 asc +limit ? +` + const queryByDesc = ` +select + tg_user_id, + sum(words_count) as "words_count", + sum(toxic_words_count) as "toxic_words_count" +from message where tg_chat_id = ? and tg_user_id in (?) +group by 1 +order by 2 desc +limit ? +` + var query string + if desc { + query = queryByDesc + } else { + query = queryByAsc + } + + err := r.db.WithContext(ctx).Raw(query, tgChatId, tgUserIds, limit).Scan(&output).Error + if err != nil { + return nil, errors.Wrap(err, "failed to group by messages") + } + + return output, nil +} + +func (r *Repository) MessageGetLastByChatId(ctx context.Context, tgChatId int64) (Message, error) { + var message Message + + err := r.db. + WithContext(ctx). + Find(&message). + Where("tg_chat_id = ?", tgChatId). + Order("created_at desc"). + Limit(1). + Error + if err != nil { + return Message{}, errors.Wrap(err, "failed to get message") + } + + return message, nil +} + +func (r *Repository) MessageGetLastByChatIdAndUserId( + ctx context.Context, + tgChatId int64, + tgUserId int64, +) (Message, error) { + var message Message + + err := r.db. + WithContext(ctx). + Where("tg_chat_id = ? and tg_user_id = ?", tgChatId, tgUserId). + Order("created_at desc"). + Limit(1). + Find(&message). + Error + if err != nil { + return Message{}, errors.Wrap(err, "failed to get message") + } + + return message, nil +} + +func (r *Repository) MessageGroupByDateAndChatId( + ctx context.Context, + tgChatId int64, + precision time.Duration, +) ([]MessageGroupByTimeOutput, error) { + var output []MessageGroupByTimeOutput + + precisionSeconds := int(precision.Seconds()) + + err := r.db.WithContext(ctx).Raw(` +select + to_timestamp((EXTRACT(EPOCH from m.created_at) ::int / ?) * ?) as "created_at", + sum(m.words_count) as "words_count" +from message m +where tg_chat_id = ? +group by 1 +order by 1 desc +`, precisionSeconds, precisionSeconds, tgChatId). + Scan(&output). + Error + if err != nil { + return nil, errors.Wrap(err, "failed to group by messages") + } + + return output, nil +} + +func (r *Repository) MessageGroupByTimeAndChatId( + ctx context.Context, + tgChatId int64, + precision time.Duration, + tz int8, +) ([]MessagesGroupByTimeByWeekdayOutput, error) { + var output []MessagesGroupByTimeByWeekdayOutput + precisionSeconds := int(precision.Seconds()) + + err := r.db.WithContext(ctx).Raw(` +select case when extract(isodow from m.created_at + interval ? hour) >= 6 then true else false end as is_weekend, + date '1970-01-01' + to_timestamp((EXTRACT(EPOCH from m.created_at) ::int / ?) * ?) :: time as "created_at", + sum(m.words_count) as words_count + from message m + where tg_chat_id = ? + group by 1, 2 + order by 1 desc; +`, tz, precisionSeconds, precisionSeconds, tgChatId). + Scan(&output). + Error + if err != nil { + return nil, errors.Wrap(err, "failed to group by messages") + } + + return output, nil +} + +func (r *Repository) MessageGroupByTimeAndChatIdAndUserId( + ctx context.Context, + tgChatId int64, + tgUserId int64, + precision time.Duration, + tz int8, +) ([]MessagesGroupByTimeByWeekdayOutput, error) { + var output []MessagesGroupByTimeByWeekdayOutput + precisionSeconds := int(precision.Seconds()) + + err := r.db.WithContext(ctx).Raw(` +select case when extract(isodow from m.created_at + interval ? hour) >= 6 then true else false end as is_weekend, + date '1970-01-01' + to_timestamp((EXTRACT(EPOCH from m.created_at) ::int / ?) * ?) :: time as "created_at", + sum(m.words_count) as words_count + from message m + where tg_chat_id = ? and tg_user_id = ? + group by 1, 2 + order by 1 desc; +`, tz, precisionSeconds, precisionSeconds, tgChatId, tgUserId). + Scan(&output). + Error + if err != nil { + return nil, errors.Wrap(err, "failed to group by messages") + } + + return output, nil +} + +func (r *Repository) MessageGroupByDateAndChatIdAndUserId( + ctx context.Context, + tgChatId int64, + tgUserId int64, + precision time.Duration, +) ([]MessageGroupByTimeOutput, error) { + var output []MessageGroupByTimeOutput + + precisionSeconds := int(precision.Seconds()) + + err := r.db.WithContext(ctx).Raw(` +select + to_timestamp((EXTRACT(EPOCH from m.created_at) ::int / ?) * ?) as "created_at", + sum(m.words_count) as "words_count" +from message m +where tg_chat_id = ? and tg_user_id = ? +group by 1 +order by 1 desc +`, precisionSeconds, precisionSeconds, tgChatId, tgUserId). + Scan(&output). + Error + if err != nil { + return nil, errors.Wrap(err, "failed to group by messages") + } + + return output, nil +} + +func (r *Repository) MessageFindRepliesTo( + ctx context.Context, + tgChatId int64, + tgUserId int64, + minReplyCount int, + limit int, +) ([]MessageGroupByInterlocutorsOutput, error) { + var output []MessageGroupByInterlocutorsOutput + + err := r.db.WithContext(ctx).Raw(` +select + am.reply_to_tg_user_id as tg_user_id, + count(1) as count + from message am +where am.tg_chat_id = ? and am.tg_user_id = ? and am.reply_to_tg_user_id is not null +group by 1 + having count(1) > ? +order by 2 desc limit ? +`, tgChatId, tgUserId, minReplyCount, limit).Scan(&output).Error + if err != nil { + return nil, errors.Wrap(err, "failed to group by messages") + } + + return output, nil +} + +func (r *Repository) MessageFindRepliedBy( + ctx context.Context, + tgChatId int64, + tgUserId int64, + minReplyCount int, + limit int, +) ([]MessageGroupByInterlocutorsOutput, error) { + var output []MessageGroupByInterlocutorsOutput + + err := r.db.WithContext(ctx).Raw(` +select am.tg_user_id as tg_user_id, count(1) as count + from message am + where am.tg_chat_id = ? and am.reply_to_tg_user_id = ? + group by 1 + having count(1) > ? + order by 2 desc + LIMIT ?; +`, tgChatId, tgUserId, minReplyCount, limit).Scan(&output).Error + if err != nil { + return nil, errors.Wrap(err, "failed to group by messages") + } + + return output, nil +} + +func (r *Repository) MessageSelectByChatIds( + ctx context.Context, + tgChatIds []int64, +) ([]Message, error) { + var output []Message + err := r.db.WithContext(ctx).Find(&output, "tg_chat_id in (?)", tgChatIds).Error + if err != nil { + return nil, errors.Wrap(err, "failed to find messages") + } + + return output, nil +} + +func (r *Repository) MessageSelectByChatIdAndUserIdWithWordsCount( + ctx context.Context, + tgChatId int64, + tgUserId int64, + atLeastWordCount int, + limit int, +) ([]Message, error) { + var output []Message + err := r.db. + WithContext(ctx). + Where("tg_chat_id = ? and tg_user_id = ? and words_count >= ?", tgChatId, tgUserId, atLeastWordCount). + Limit(limit). + Find(&output). + Error + if err != nil { + return nil, errors.Wrap(err, "failed to find messages") + } + + return output, nil +} + +func (r *Repository) MessageSelectByChatIdWithWordsCount( + ctx context.Context, + tgChatId int64, + atLeastWordCount int, + limit int, +) ([]Message, error) { + var output []Message + err := r.db. + WithContext(ctx). + Where("tg_chat_id = ? and words_count >= ?", tgChatId, atLeastWordCount). + Limit(limit). + Find(&output). + Error + if err != nil { + return nil, errors.Wrap(err, "failed to find messages") + } + + return output, nil +} diff --git a/core/repository/db_repository/message_model.go b/core/repository/db_repository/message_model.go new file mode 100644 index 0000000..d3208e7 --- /dev/null +++ b/core/repository/db_repository/message_model.go @@ -0,0 +1,98 @@ +package db_repository + +import ( + "time" + + "github.com/guregu/null/v5" +) + +type Message struct { + WithId + WithCreatedAt + + TgChatID int64 `sql:"tg_chat_id" gorm:"index:tg_chat_id_tg_id_idx,unique"` + TgId int `sql:"tg_id" gorm:"index:tg_chat_id_tg_id_idx,unique"` + + TgUserId int64 `sql:"tg_user_id" gorm:"index"` + Text string `sql:"text"` + WordsCount uint64 `sql:"words_count"` + ToxicWordsCount uint64 `sql:"toxic_words_count"` + + ReplyToTgMsgID null.Int64 `sql:"reply_to_tg_msg_id" gorm:"index"` + ReplyToTgUserID null.Int64 `sql:"reply_to_tg_user_id" gorm:"index"` +} + +func (r *Message) ToParquet() MessageParquet { + message := MessageParquet{ + CreatedAt: r.CreatedAt.UnixNano() / int64(time.Millisecond), + TgChatID: r.TgChatID, + TgId: int64(r.TgId), + TgUserId: r.TgUserId, + Text: r.Text, + WordsCount: int64(r.WordsCount), + ToxicWordsCount: int64(r.ToxicWordsCount), + } + + if r.ReplyToTgMsgID.Valid { + message.ReplyToTgMsgID = &r.ReplyToTgMsgID.Int64 + } + + if r.ReplyToTgUserID.Valid { + message.ReplyToTgUserID = &r.ReplyToTgUserID.Int64 + } + + return message +} + +type MessageParquet struct { + CreatedAt int64 `parquet:"name=created_at, type=INT64, convertedtype=TIMESTAMP_MILLIS"` + + TgChatID int64 `parquet:"name=tg_chat_id, type=INT64"` + TgId int64 `parquet:"name=tg_id, type=INT64"` + TgUserId int64 `parquet:"name=tg_user_id, type=INT64"` + Text string `parquet:"name=text, type=BYTE_ARRAY, convertedtype=UTF8, encoding=PLAIN_DICTIONARY"` + WordsCount int64 `parquet:"name=words_count, type=INT64"` + ToxicWordsCount int64 `parquet:"name=toxic_words_count, type=INT64"` + + ReplyToTgMsgID *int64 `parquet:"name=reply_to_tg_msg_id, type=INT64"` + ReplyToTgUserID *int64 `parquet:"name=reply_to_tg_user_id, type=INT64"` +} + +type RestartMessage struct { + WithId + WithCreatedAt + + MessageTgChatID int64 `sql:"message_tg_chat_id" gorm:"index:tg_chat_id_tg_id_idx,unique"` + MessageTgId int `sql:"message_tg_id" gorm:"index:tg_chat_id_tg_id_idx,unique"` +} + +type PingMessage struct { + WithId + WithCreatedAt + + MessageTgChatID int64 `sql:"message_tg_chat_id" gorm:"index:tg_chat_id_tg_id_idx,unique"` + MessageTgId int `sql:"message_tg_id" gorm:"index:tg_chat_id_tg_id_idx,unique"` + DeleteAt time.Time `sql:"delete_at" gorm:"index"` +} + +type MessageGroupByChatIdAndUserIdOutput struct { + TgUserId int64 `sql:"tg_user_id"` + WordsCount uint64 `sql:"words_count"` + ToxicWordsCount uint64 `sql:"toxic_words_count"` +} + +type MessageGroupByTimeOutput struct { + CreatedAt time.Time `sql:"created_at"` + WordsCount uint64 `sql:"words_count"` +} + +type MessagesGroupByTimeByWeekdayOutput struct { + IsWeekend bool `sql:"is_weekend"` + CreatedAt time.Time `sql:"created_at"` + WordsCount uint64 `sql:"words_count"` +} + +type MessageGroupByInterlocutorsOutput struct { + TgUserId int64 `sql:"tg_user_id"` + MessagesCount uint64 `sql:"count"` +} diff --git a/core/repository/db_repository/message_test.go b/core/repository/db_repository/message_test.go new file mode 100644 index 0000000..9d930e2 --- /dev/null +++ b/core/repository/db_repository/message_test.go @@ -0,0 +1,115 @@ +package db_repository + +import ( + "math/rand/v2" + "testing" + + "github.com/guregu/null/v5" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/teadove/fun_telegram/core/infrastructure/pg" + "github.com/teadove/fun_telegram/core/shared" +) + +func generateMessage() Message { + return Message{ + TgChatID: rand.Int64N(100000), + TgId: rand.IntN(100000), + TgUserId: rand.Int64N(100000), + Text: shared.RandomString(), + } +} + +func TestIntegration_DbRepository_MessageCountByChatIdAndUserId_Ok(t *testing.T) { + ctx := shared.GetCtx() + db, err := pg.NewClientFromSettings() + require.NoError(t, err) + + dbRepository, err := NewRepository(ctx, db) + require.NoError(t, err) + + chatId := rand.Int64N(100_000) + userId := rand.Int64N(100_000) + + message := generateMessage() + message.TgChatID = chatId + message.TgUserId = userId + err = dbRepository.MessageInsert(ctx, &message) + require.NoError(t, err) + + message = generateMessage() + message.TgChatID = chatId + message.TgUserId = userId + err = dbRepository.MessageInsert(ctx, &message) + require.NoError(t, err) + + count, err := dbRepository.MessageCountByChatIdAndUserId(ctx, chatId, userId) + require.NoError(t, err) + assert.Equal(t, uint64(2), count) +} + +func TestIntegration_DbRepository_MessageGroupByChatIdAndUserId_Ok(t *testing.T) { + ctx := shared.GetCtx() + db, err := pg.NewClientFromSettings() + require.NoError(t, err) + + dbRepository, err := NewRepository(ctx, db) + require.NoError(t, err) + + chatId := rand.Int64N(100_000) + userId := rand.Int64N(100_000) + + message := generateMessage() + message.TgChatID = chatId + message.TgUserId = userId + message.ToxicWordsCount = 3 + message.WordsCount = 5 + err = dbRepository.MessageInsert(ctx, &message) + require.NoError(t, err) + + message = generateMessage() + message.TgChatID = chatId + message.TgUserId = userId + message.ToxicWordsCount = 2 + message.WordsCount = 4 + err = dbRepository.MessageInsert(ctx, &message) + require.NoError(t, err) + + group, err := dbRepository.MessageGroupByChatIdAndUserId(ctx, chatId, []int64{userId}, 10, true) + require.NoError(t, err) + + assert.Equal(t, uint64(9), group[0].WordsCount) + assert.Equal(t, uint64(5), group[0].ToxicWordsCount) + assert.Equal(t, userId, group[0].TgUserId) +} + +func TestIntegration_DbRepository_MessageInsert_Ok(t *testing.T) { + ctx := shared.GetCtx() + db, err := pg.NewClientFromSettings() + require.NoError(t, err) + + dbRepository, err := NewRepository(ctx, db) + require.NoError(t, err) + + message := generateMessage() + chatId := rand.Int64N(100_000) + userId := rand.Int64N(100_000) + msgId := message.TgId + + message.TgChatID = chatId + message.TgUserId = userId + message.ToxicWordsCount = 3 + message.WordsCount = 5 + err = dbRepository.MessageInsert(ctx, &message) + require.NoError(t, err) + + message = generateMessage() + message.TgChatID = chatId + message.TgUserId = userId + message.ReplyToTgMsgID = null.IntFrom(int64(msgId)) + message.ToxicWordsCount = 3 + message.WordsCount = 5 + err = dbRepository.MessageInsert(ctx, &message) + require.NoError(t, err) +} diff --git a/core/repository/db_repository/ping_message_methods.go b/core/repository/db_repository/ping_message_methods.go new file mode 100644 index 0000000..9e06f9a --- /dev/null +++ b/core/repository/db_repository/ping_message_methods.go @@ -0,0 +1,37 @@ +package db_repository + +import ( + "context" + "time" + + "github.com/pkg/errors" +) + +func (r *Repository) PingMessageCreate(ctx context.Context, message *PingMessage) error { + err := r.db.WithContext(ctx).Create(message).Error + if err != nil { + return errors.Wrap(err, "unable to create ping message") + } + + return nil +} + +func (r *Repository) PingMessageGet(ctx context.Context) ([]PingMessage, error) { + var messages []PingMessage + + err := r.db.WithContext(ctx).Where("delete_at <= ?", time.Now().UTC()).Find(&messages).Error + if err != nil { + return nil, errors.Wrap(err, "failed to ping message get") + } + + return messages, nil +} + +func (r *Repository) PingMessageDelete(ctx context.Context, messages []PingMessage) error { + err := r.db.WithContext(ctx).Delete(messages).Error + if err != nil { + return errors.Wrap(err, "failed to ping message delete") + } + + return nil +} diff --git a/core/repository/db_repository/repository.go b/core/repository/db_repository/repository.go new file mode 100644 index 0000000..fe55065 --- /dev/null +++ b/core/repository/db_repository/repository.go @@ -0,0 +1,68 @@ +package db_repository + +import ( + "context" + + "github.com/pkg/errors" + "github.com/rs/zerolog" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +type Repository struct { + db *gorm.DB +} + +func NewRepository(ctx context.Context, db *gorm.DB) (*Repository, error) { + r := Repository{db: db} + + err := r.Migrate(ctx) + if err != nil { + return nil, errors.Wrap(err, "failed to migrate repository") + } + + return &r, nil +} + +func (r *Repository) Migrate(ctx context.Context) error { + oldLogger := r.db.Logger + r.db.Logger = oldLogger.LogMode(logger.Silent) + defer func() { r.db.Logger = oldLogger }() + + err := r.db. + WithContext(ctx). + AutoMigrate( + &Message{}, + &Member{}, + &Channel{}, + &ChannelEdge{}, + &Chat{}, + &User{}, + &RestartMessage{}, + &PingMessage{}, + &Image{}, + &KandinskyImage{}, + &TgImage{}, + ) + if err != nil { + return errors.Wrap(err, "failed to migrate database") + } + + zerolog.Ctx(ctx).Info().Msg("pg.migrated.successfully") + return nil +} + +func (r *Repository) Ping(ctx context.Context) error { + var v int64 + + err := r.db.WithContext(ctx).Raw("select 1").Scan(&v).Error + if err != nil { + return errors.Wrap(err, "failed to ping database") + } + + if v == 0 { + return errors.New("non one response") + } + + return nil +} diff --git a/core/repository/db_repository/restart_message_methods.go b/core/repository/db_repository/restart_message_methods.go new file mode 100644 index 0000000..b881376 --- /dev/null +++ b/core/repository/db_repository/restart_message_methods.go @@ -0,0 +1,39 @@ +package db_repository + +import ( + "context" + + "github.com/pkg/errors" +) + +func (r *Repository) RestartMessageInsert( + ctx context.Context, + restartMessage *RestartMessage, +) error { + err := r.db.WithContext(ctx).Create(&restartMessage).Error + if err != nil { + return errors.Wrap(err, "failed to restart message insert") + } + + return nil +} + +func (r *Repository) RestartMessageGet(ctx context.Context) ([]RestartMessage, error) { + var messages []RestartMessage + + err := r.db.WithContext(ctx).Find(&messages).Error + if err != nil { + return nil, errors.Wrap(err, "failed to restart message get") + } + + return messages, nil +} + +func (r *Repository) RestartMessageDelete(ctx context.Context, messages []RestartMessage) error { + err := r.db.WithContext(ctx).Delete(messages).Error + if err != nil { + return errors.Wrap(err, "failed to restart message delete") + } + + return nil +} diff --git a/core/repository/db_repository/user_methods.go b/core/repository/db_repository/user_methods.go new file mode 100644 index 0000000..e358480 --- /dev/null +++ b/core/repository/db_repository/user_methods.go @@ -0,0 +1,130 @@ +package db_repository + +import ( + "context" + "time" + + "github.com/pkg/errors" + "gorm.io/gorm/clause" +) + +func (r *Repository) UserSelectById(ctx context.Context, tgId int64) (User, error) { + var user User + err := r.db.WithContext(ctx).Where("tg_id = ?", tgId).Limit(1).Find(&user).Error + if err != nil { + return User{}, errors.Wrap(err, "failed to get user by tg_id") + } + + return user, nil +} + +func (r *Repository) UserSelectByUsername(ctx context.Context, tgUsername string) (User, error) { + var user User + + err := r.db.WithContext(ctx).Where("tg_username = ?", tgUsername).First(&user).Error + if err != nil { + return User{}, errors.Wrap(err, "failed to get user by tg username") + } + + return user, nil +} + +func (r *Repository) UsersSelectByStatusInChat( + ctx context.Context, + tgChatId int64, + memberStatuses []MemberStatus, +) (UsersInChat, error) { + var usersInChat UsersInChat + + err := r.db.WithContext(ctx). + Raw(` +select u.tg_id, u.tg_username, u.tg_name, u.is_bot, m.status + from "user" u +join member m on u.tg_id = m.tg_user_id + where m.tg_chat_id = ? and m.status in (?) +`, tgChatId, memberStatuses). + Scan(&usersInChat). + Error + if err != nil { + return nil, errors.Wrap(err, "failed to get users") + } + + return usersInChat, nil +} + +func (r *Repository) UsersSelectInChat( + ctx context.Context, + tgChatId int64, +) (UsersInChat, error) { + var usersInChat UsersInChat + + err := r.db.WithContext(ctx). + Raw(` +select u.tg_id, u.tg_username, u.tg_name, u.is_bot, m.status + from "user" u +join member m on u.tg_id = m.tg_user_id + where m.tg_chat_id = ? +`, tgChatId). + Scan(&usersInChat). + Error + if err != nil { + return nil, errors.Wrap(err, "failed to get users") + } + + return usersInChat, nil +} + +func (r *Repository) UserUpsert(ctx context.Context, user *User) error { + user.UpdatedInDBAt = time.Now().UTC() + + err := r.db.WithContext(ctx).Clauses( + clause.OnConflict{ + Columns: []clause.Column{{Name: "tg_username"}, {Name: "tg_id"}}, + DoUpdates: clause.AssignmentColumns( + []string{"tg_id", "tg_username", "tg_name", "is_bot", "updated_in_db_at"}, + ), + }). + Create(&user).Error + if err != nil { + return errors.Wrap(err, "failed to upsert chat") + } + + return nil +} + +func (r *Repository) MemberUpsert(ctx context.Context, member *Member) error { + member.CreatedInDBAt = time.Now().UTC() + member.UpdatedInDBAt = time.Now().UTC() + + err := r.db.WithContext(ctx).Clauses( + clause.OnConflict{ + Columns: []clause.Column{{Name: "tg_user_id"}, {Name: "tg_chat_id"}}, + DoUpdates: clause.AssignmentColumns( + []string{"status", "updated_in_db_at"}, + ), + }). + Create(&member).Error + if err != nil { + return errors.Wrap(err, "failed to upsert member") + } + + return nil +} + +func (r *Repository) MemberSetAsLeftBeforeTime( + ctx context.Context, + tgChatId int64, + notUpdatedBefore time.Time, +) error { + err := r.db. + WithContext(ctx). + Model(&Member{}). + Where("tg_chat_id = ? AND updated_in_db_at < ?", tgChatId, notUpdatedBefore). + Update("status", Left). + Error + if err != nil { + return errors.Wrap(err, "failed to set member as left before time") + } + + return nil +} diff --git a/core/repository/db_repository/user_model.go b/core/repository/db_repository/user_model.go new file mode 100644 index 0000000..bd26b10 --- /dev/null +++ b/core/repository/db_repository/user_model.go @@ -0,0 +1,40 @@ +package db_repository + +type User struct { + WithId + WithCreatedAt + WithUpdatedInDBAt + + TgId int64 `sql:"tg_id" gorm:"index:_,unique"` + TgUsername string `sql:"tg_username" gorm:"index:_,unique"` + TgName string `sql:"tg_name" gorm:"index"` + IsBot bool `sql:"is_bot"` +} + +type MemberStatus string + +const ( + Plain MemberStatus = "PLAIN" + // Creator is status for chat/channel creator. + Creator MemberStatus = "CREATOR" + // Admin is status for chat/channel admin. + Admin MemberStatus = "ADMIN" + // Banned is status for banned user. + Banned MemberStatus = "BANNED" + // Left is status for user that left chat/channel. + Left MemberStatus = "LEFT" + + Unknown MemberStatus = "UNKNOWN" +) + +var MemberStatusesActive = []MemberStatus{Plain, Creator, Admin} + +type Member struct { + WithId + WithCreatedInDBAt + WithUpdatedInDBAt + + TgUserId int64 `sql:"tg_user_id" gorm:"index:tg_user_id_tg_chat_id_idx,unique"` + TgChatId int64 `sql:"tg_chat_id" gorm:"index:tg_user_id_tg_chat_id_idx,unique"` + Status MemberStatus +} diff --git a/core/repository/mongo_repository/chat_methods.go b/core/repository/mongo_repository/chat_methods.go deleted file mode 100644 index d3b953c..0000000 --- a/core/repository/mongo_repository/chat_methods.go +++ /dev/null @@ -1,39 +0,0 @@ -package mongo_repository - -import ( - "context" - "time" - - "github.com/pkg/errors" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo/options" -) - -func (r *Repository) ChatUpsert(ctx context.Context, row *Chat) error { - row.UpdatedAt = time.Now().UTC() - - filter := bson.M{"tg_id": row.TgId} - update := bson.M{"$set": bson.M{ - "tg_id": row.TgId, - "title": row.Title, - "updated_at": row.UpdatedAt, - "created_at": row.CreatedAt, - }} - opts := options.Update().SetUpsert(true) - - _, err := r.chatCollection.UpdateOne(ctx, filter, update, opts) - if err != nil { - return errors.WithStack(err) - } - - return nil -} - -func (r *Repository) GetChat(ctx context.Context, chatId int64) (chat Chat, err error) { - err = r.chatCollection.FirstWithCtx(ctx, bson.M{"tg_id": chatId}, &chat) - if err != nil { - return Chat{}, errors.Wrap(err, "failed to get chat") - } - - return chat, nil -} diff --git a/core/repository/mongo_repository/image_methods.go b/core/repository/mongo_repository/image_methods.go deleted file mode 100644 index d454382..0000000 --- a/core/repository/mongo_repository/image_methods.go +++ /dev/null @@ -1,99 +0,0 @@ -package mongo_repository - -import ( - "context" - - "github.com/gotd/td/tg" - "github.com/kamva/mgm/v3/builder" - "github.com/kamva/mgm/v3/operator" - "github.com/pkg/errors" - "github.com/teadove/fun_telegram/core/supplier/kandinsky_supplier" - "go.mongodb.org/mongo-driver/bson" -) - -type KandinskyImageDenormalized struct { - TgInputPhoto tg.InputPhoto `bson:"tg_input_photo"` - KandinskyInput kandinsky_supplier.RequestGenerationInput `bson:"kandinsky_input"` - ImgContent []byte `bson:"img_content"` - Message Message `bson:"message"` -} - -func (r *Repository) KandinskyImageInsert( - ctx context.Context, - input *KandinskyImageDenormalized, -) error { - img := &Image{Content: input.ImgContent} - - err := r.imageCollection.CreateWithCtx(ctx, img) - if err != nil { - return errors.Wrap(err, "failed to insert image") - } - - err = r.messageCollection.CreateWithCtx(ctx, &input.Message) - if err != nil { - return errors.Wrap(err, "failed to insert msg") - } - - tgImage := TgImage{ - TgInputPhoto: input.TgInputPhoto, - MessageId: input.Message.ID, - ImageId: img.ID, - } - - err = r.tgImageCollection.CreateWithCtx(ctx, &tgImage) - if err != nil { - return errors.Wrap(err, "failed to insert tg image") - } - - err = r.kandinskyImageCollection.CreateWithCtx(ctx, &KandinskyImage{ - Input: input.KandinskyInput, - TgImageId: tgImage.ID, - ImageId: img.ID, - }) - if err != nil { - return errors.Wrap(err, "failed to insert kandisnky image") - } - - return nil -} - -type KandinskyImagePaginateInput struct { - TgChatId int64 - Page int - PageSize int -} - -func (r *Repository) KandinskyImagePaginate( - ctx context.Context, - input *KandinskyImagePaginateInput, -) ([]KandinskyImageDenormalized, error) { - images := make([]KandinskyImageDenormalized, 0, input.PageSize) - - err := r.kandinskyImageCollection.SimpleAggregateWithCtx( - ctx, - &images, - bson.M{operator.Sort: bson.M{"created_at": -1}}, - builder.Lookup(r.tgImageCollection.Name(), "tg_image_id", "_id", "tg_image"), - bson.M{operator.Unwind: "$tg_image"}, - builder.Lookup(r.imageCollection.Name(), "image_id", "_id", "image"), - bson.M{operator.Unwind: "$image"}, - builder.Lookup(r.messageCollection.Name(), "tg_image.message_id", "_id", "message"), - bson.M{operator.Unwind: "$message"}, - bson.M{ - operator.Project: bson.M{ - "tg_input_photo": "$tg_image.tg_input_photo", - "kandinsky_input": "$input", - "message": "$message", - "img_content": "$image.content", - }, - }, - bson.M{operator.Match: bson.M{"message.tg_chat_id": input.TgChatId}}, - bson.M{operator.Skip: input.Page * input.PageSize}, - bson.M{operator.Limit: input.PageSize}, - ) - if err != nil { - return nil, errors.WithStack(err) - } - - return images, nil -} diff --git a/core/repository/mongo_repository/member_methods.go b/core/repository/mongo_repository/member_methods.go deleted file mode 100644 index d1b8312..0000000 --- a/core/repository/mongo_repository/member_methods.go +++ /dev/null @@ -1,114 +0,0 @@ -package mongo_repository - -import ( - "context" - "time" - - "github.com/kamva/mgm/v3/builder" - "github.com/kamva/mgm/v3/operator" - "github.com/pkg/errors" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo/options" -) - -func (r *Repository) MemberUpsert(ctx context.Context, member *Member) error { - member.CreatedAt = time.Now().UTC() - member.UpdatedAt = time.Now().UTC() - - filter := bson.M{"tg_chat_id": member.TgChatId, "tg_user_id": member.TgUserId} - update := bson.M{"$set": bson.M{ - "tg_user_id": member.TgUserId, - "tg_chat_id": member.TgChatId, - "status": member.Status, - "updated_at": member.UpdatedAt, - "created_at": member.CreatedAt, - }} - opts := options.Update().SetUpsert(true) - - _, err := r.memberCollection.UpdateOne(ctx, filter, update, opts) - if err != nil { - return errors.WithStack(err) - } - - return nil -} - -func (r *Repository) SetAllMembersAsLeft( - ctx context.Context, - tgChatId int64, - notUpdatedBefore time.Time, -) error { - _, err := r.memberCollection.UpdateMany( - ctx, - bson.M{"tg_chat_id": tgChatId, "updated_at": bson.M{operator.Lte: notUpdatedBefore}}, - bson.M{"$set": bson.M{ - "status": Left, - "updated_at": time.Now().UTC(), - }}, - ) - if err != nil { - return errors.Wrap(err, "failed to set members as left") - } - - return nil -} - -func (r *Repository) GetUsersInChat(ctx context.Context, chatId int64) (UsersInChat, error) { - usersInChat := make(UsersInChat, 0, 100) - - err := r.memberCollection.SimpleAggregateWithCtx( - ctx, - &usersInChat, - builder.Lookup(r.userCollection.Name(), "tg_user_id", "tg_id", "user"), - bson.M{operator.Match: bson.M{"tg_chat_id": chatId}}, - bson.M{operator.Unwind: "$user"}, - bson.M{ - operator.Project: bson.M{ - "status": 1, - "tg_id": "$user.tg_id", - "tg_username": "$user.tg_username", - "tg_name": "$user.tg_name", - "is_bot": "$user.is_bot", - }, - }, - ) - if err != nil { - return nil, errors.WithStack(err) - } - - return usersInChat, nil -} - -func (r *Repository) GetUsersInChatOnlyActive( - ctx context.Context, - chatId int64, -) (UsersInChat, error) { - usersInChat := make(UsersInChat, 0, 100) - - err := r.memberCollection.SimpleAggregateWithCtx( - ctx, - &usersInChat, - builder.Lookup(r.userCollection.Name(), "tg_user_id", "tg_id", "user"), - bson.M{operator.Match: bson.M{"tg_chat_id": chatId}}, - bson.M{operator.Unwind: "$user"}, - bson.M{ - operator.Project: bson.M{ - "status": 1, - "tg_id": "$user.tg_id", - "tg_username": "$user.tg_username", - "tg_name": "$user.tg_name", - "is_bot": "$user.is_bot", - }, - }, - bson.M{ - operator.Match: bson.M{ - "status": bson.M{operator.In: []MemberStatus{Plain, Creator, Admin}}, - }, - }, - ) - if err != nil { - return nil, errors.WithStack(err) - } - - return usersInChat, nil -} diff --git a/core/repository/mongo_repository/member_methods_test.go b/core/repository/mongo_repository/member_methods_test.go deleted file mode 100644 index 85e393b..0000000 --- a/core/repository/mongo_repository/member_methods_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package mongo_repository - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/teadove/fun_telegram/core/shared" -) - -func TestIntegration_MongoRepository_GetUsersInChat_Ok(t *testing.T) { - r := getRepository(t) - ctx := shared.GetCtx() - - users, err := r.GetUsersInChat(ctx, 1178533048) - assert.NoError(t, err) - shared.SendInterface(users) -} diff --git a/core/repository/mongo_repository/methods.go b/core/repository/mongo_repository/methods.go deleted file mode 100644 index 1c74702..0000000 --- a/core/repository/mongo_repository/methods.go +++ /dev/null @@ -1,332 +0,0 @@ -package mongo_repository - -import ( - "context" - "time" - - "github.com/kamva/mgm/v3/builder" - "github.com/kamva/mgm/v3/operator" - errors "github.com/pkg/errors" - "github.com/rs/zerolog" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" -) - -const duplicationError = 11000 - -func (r *Repository) MessageCreate(ctx context.Context, message *Message) error { - err := r.messageCollection.CreateWithCtx(ctx, message) - if err != nil { - return errors.WithStack(err) - } - - return nil -} - -func (r *Repository) MessageCreateOrNothingAndSetTime(ctx context.Context, message *Message) error { - message.UpdatedAt = time.Now().UTC() - - _, err := r.messageCollection.InsertOne(ctx, &message) - if err != nil { - var mgerr mongo.WriteException - if errors.As(err, &mgerr) { - if mgerr.HasErrorCode(duplicationError) { - return nil - } - } - - return errors.WithStack(err) - } - - return nil -} - -func (r *Repository) MessageGetSortedLimited(ctx context.Context, limit int64) ([]Message, error) { - messages := make([]Message, 0, 100) - - opts := options.Find().SetSort(bson.M{"created_at": 1}).SetLimit(limit) - - err := r.messageCollection.SimpleFindWithCtx(ctx, &messages, bson.M{}, opts) - if err != nil { - return nil, errors.WithStack(err) - } - - return messages, nil -} - -func (r *Repository) DeleteMessages(ctx context.Context, messages []Message) (int64, error) { - messageIds := make([]primitive.ObjectID, len(messages)) - for idx, message := range messages { - messageIds[idx] = message.ID - } - - result, err := r.messageCollection.DeleteMany(ctx, bson.M{"_id": bson.M{"$in": messageIds}}) - if err != nil { - return 0, errors.WithStack(err) - } - - return result.DeletedCount, nil -} - -func (r *Repository) DeleteMessagesOldWithCount(ctx context.Context, limit int64) (int64, error) { - batchSize := int64(10_000) - count := int64(0) - - for { - shouldBreak := false - - if batchSize+count > limit { - batchSize = limit - count - shouldBreak = true - } - - messages, err := r.MessageGetSortedLimited(ctx, batchSize) - if err != nil { - return 0, errors.WithStack(err) - } - - batchCount, err := r.DeleteMessages(ctx, messages) - if err != nil { - return 0, errors.WithStack(err) - } - - zerolog.Ctx(ctx).Info().Str("status", "messages.deleted").Int64("count", batchCount).Send() - - count += batchCount - - if shouldBreak { - break - } - } - - return count, nil -} - -func (r *Repository) MessageDeleteOld(ctx context.Context) (int64, error) { - result, err := r.messageCollection.DeleteMany(ctx, - bson.M{"created_at": bson.M{"$lt": time.Now().UTC().Add(-time.Hour * 24 * 365)}}) - if err != nil { - return 0, errors.WithStack(err) - } - - return result.DeletedCount, nil -} - -// GetMessagesByChat -// Deprecated -func (r *Repository) GetMessagesByChat(ctx context.Context, chatId int64) ([]Message, error) { - messages := make([]Message, 0, 100) - - opts := options.Find().SetSort(bson.M{"created_at": -1}) - - err := r.messageCollection.SimpleFindWithCtx(ctx, &messages, bson.M{"tg_chat_id": chatId}, opts) - if err != nil { - return nil, errors.WithStack(err) - } - - return messages, nil -} - -// GetMessagesByChatAndUsername -// Deprecated -func (r *Repository) GetMessagesByChatAndUsername( - ctx context.Context, - chatId int64, - username string, -) ([]Message, error) { - messages := make([]Message, 0, 100) - - err := r.messageCollection.SimpleAggregateWithCtx( - ctx, - &messages, - builder.Lookup(r.userCollection.Name(), "tg_user_id", "tg_id", "user"), - bson.M{operator.Unwind: "$user"}, - bson.M{ - operator.Project: bson.M{ - "username": "$user.tg_username", - "text": 1, - "tg_chat_id": 1, - "tg_id": 1, - "created_at": 1, - "updated_at": 1, - }, - }, - bson.M{operator.Match: bson.M{"username": username, "tg_chat_id": chatId}}, - ) - if err != nil { - return nil, errors.WithStack(err) - } - - return messages, nil -} - -// GetLastMessage -// Deprecated -func (r *Repository) GetLastMessage(ctx context.Context, chatId int64) (Message, error) { - var message Message - - err := r.messageCollection.FirstWithCtx( - ctx, - bson.M{"tg_chat_id": chatId}, - &message, - options.FindOne().SetSort(bson.M{"created_at": 1}), - ) - if err != nil { - return Message{}, errors.WithStack(err) - } - - return message, nil -} - -// CheckUserExists -// Deprecated -func (r *Repository) CheckUserExists(ctx context.Context, userId int64) (bool, error) { - count, err := r.userCollection.CountDocuments(ctx, bson.M{"tg_user_id": userId}) - if err != nil { - return false, errors.WithStack(err) - } - - return count == 1, nil -} - -func (r *Repository) DeleteMessagesByChat(ctx context.Context, chatId int64) (int64, error) { - result, err := r.messageCollection.DeleteMany(ctx, bson.M{"tg_chat_id": chatId}) - if err != nil { - return 0, errors.WithStack(err) - } - - return result.DeletedCount, nil -} - -func (r *Repository) DeleteAllMessages(ctx context.Context) (int64, error) { - result, err := r.messageCollection.DeleteMany(ctx, bson.M{}) - if err != nil { - return 0, errors.WithStack(err) - } - - return result.DeletedCount, nil -} - -func (r *Repository) RestartMessageCreate(ctx context.Context, message *Message) error { - err := r.messageCollection.CreateWithCtx(ctx, message) - if err != nil { - var mgerr mongo.WriteException - if !(errors.As(err, &mgerr) && mgerr.HasErrorCode(duplicationError)) { - return errors.WithStack(err) - } - - err = r.messageCollection.First( - bson.M{"tg_chat_id": message.TgChatID, "tg_id": message.TgId}, - message, - ) - if err != nil { - return errors.WithStack(err) - } - } - - err = r.restartMessageCollection.CreateWithCtx(ctx, &RestartMessage{ - MessageId: message.ID, - }) - if err != nil { - return errors.WithStack(err) - } - - return nil -} - -func (r *Repository) RestartMessageGetAndDelete(ctx context.Context) ([]Message, error) { - messages := make([]Message, 0, 5) - - err := r.messageCollection.SimpleAggregateWithCtx( - ctx, - &messages, - builder.Lookup(r.restartMessageCollection.Name(), "_id", "message_id", "restart_messages"), - bson.M{ - operator.Project: bson.M{ - "text": 1, - "tg_chat_id": 1, - "tg_id": 1, - "created_at": 1, - "updated_at": 1, - "restart_messages": "$restart_messages.message_id", - }, - }, - bson.M{operator.Unwind: "$restart_messages"}, - ) - if err != nil { - return nil, errors.WithStack(err) - } - - _, err = r.restartMessageCollection.DeleteMany(ctx, bson.M{}) - if err != nil { - return nil, errors.WithStack(err) - } - - return messages, nil -} - -func (r *Repository) PingMessageCreate( - ctx context.Context, - message *Message, - deleteAt time.Time, -) error { - err := r.messageCollection.CreateWithCtx(ctx, message) - if err != nil { - var mgerr mongo.WriteException - if !(errors.As(err, &mgerr) && mgerr.HasErrorCode(duplicationError)) { - return errors.WithStack(err) - } - - err = r.messageCollection.First( - bson.M{"tg_chat_id": message.TgChatID, "tg_id": message.TgId}, - message, - ) - if err != nil { - return errors.WithStack(err) - } - } - - err = r.pingMessageCollection.CreateWithCtx(ctx, &PingMessage{ - MessageId: message.ID, - DeleteAt: deleteAt, - }) - if err != nil { - return errors.WithStack(err) - } - - return nil -} - -func (r *Repository) PingMessageGetAndDeleteForDeletion(ctx context.Context) ([]Message, error) { - messages := make([]Message, 0, 5) - - now := time.Now().UTC() - - err := r.messageCollection.SimpleAggregateWithCtx( - ctx, - &messages, - builder.Lookup(r.pingMessageCollection.Name(), "_id", "message_id", "ping_messages"), - bson.M{operator.Match: bson.M{"ping_messages.delete_at": bson.M{operator.Lt: now}}}, - bson.M{ - operator.Project: bson.M{ - "text": 1, - "tg_chat_id": 1, - "tg_id": 1, - "created_at": 1, - "updated_at": 1, - }, - }, - ) - if err != nil { - return nil, errors.WithStack(err) - } - - _, err = r.pingMessageCollection.DeleteMany(ctx, bson.M{"delete_at": bson.M{operator.Lt: now}}) - if err != nil { - return nil, errors.WithStack(err) - } - - return messages, nil -} diff --git a/core/repository/mongo_repository/methods_test.go b/core/repository/mongo_repository/methods_test.go deleted file mode 100644 index e6d215e..0000000 --- a/core/repository/mongo_repository/methods_test.go +++ /dev/null @@ -1,423 +0,0 @@ -package mongo_repository - -import ( - "context" - "math/rand" - "strconv" - "sync" - "testing" - "time" - - "github.com/kamva/mgm/v3" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/teadove/fun_telegram/core/shared" - "go.mongodb.org/mongo-driver/bson" -) - -func getRepository(t *testing.T) *Repository { - r, err := New() - require.NoError(t, err) - - return r -} - -func generateMessage(r *Repository, t *testing.T) []Message { - ctx := shared.GetCtx() - _, err := r.DeleteAllMessages(ctx) - require.NoError(t, err) - - var wg sync.WaitGroup - var mu sync.Mutex - messages := make([]Message, 0, 1000) - - for i := 0; i < 1_000; i++ { - for j := 0; j < rand.Intn(500); j++ { - wg.Add(1) - j := j - i := i - go func() { - defer wg.Done() - - text := strconv.Itoa(rand.Intn(1_000_000)) - message := &Message{ - Text: text, - TgChatID: int64(i), - TgUserId: rand.Int63n(10), - TgId: j, - } - err := r.MessageCreate(ctx, message) - require.NoError(t, err) - - mu.Lock() - defer mu.Unlock() - messages = append(messages, *message) - }() - } - } - wg.Wait() - - return messages -} - -func TestIntegration_DbRepository_MessageCreate_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - id := rand.Int63n(1_000_000) - err := r.MessageCreate( - context.Background(), - &Message{Text: "Привет", TgChatID: id, TgUserId: id, TgId: 1}, - ) - assert.NoError(t, err) - - message := Message{} - - err = r.messageCollection.First(bson.M{"tg_chat_id": id, "tg_user_id": id}, &message) - assert.NoError(t, err) - - assert.Equal(t, "Привет", message.Text) -} - -func TestIntegration_DbRepository_UserUpsert_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - ctx := shared.GetCtx() - - id := rand.Int63n(1_000_000) - err := r.UserUpsert(ctx, &User{ - TgId: id, - TgUsername: "teadove", - TgName: "teadove", - }) - assert.NoError(t, err) - - user, err := r.GetUserById(ctx, id) - assert.Equal(t, user.TgName, "teadove") - assert.Equal(t, user.TgUsername, "teadove") - - err = r.UserUpsert(ctx, &User{ - TgId: id, - TgUsername: "tainella", - TgName: "tainella", - }) - assert.NoError(t, err) - - user, err = r.GetUserById(ctx, id) - assert.Equal(t, user.TgName, "tainella") - assert.Equal(t, user.TgUsername, "tainella") -} - -func TestIntegration_DbRepository_DeleteOldMessages_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - _, err := r.MessageDeleteOld(shared.GetModuleCtx("repository")) - assert.NoError(t, err) -} - -func TestIntegration_DbRepository_GetMessagesByChat_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - id := rand.Int63n(1_000_000) - ctx := context.Background() - err := r.MessageCreate(ctx, &Message{Text: "Привет", TgChatID: id, TgUserId: id, TgId: 1}) - assert.NoError(t, err) - err = r.MessageCreate(ctx, &Message{Text: "Привет", TgChatID: id, TgUserId: id, TgId: 2}) - assert.NoError(t, err) - - messages, err := r.GetMessagesByChat(ctx, id) - assert.NoError(t, err) - - assert.Len(t, messages, 2) - assert.Equal(t, "Привет", messages[0].Text) -} - -func TestIntegration_DbRepository_GetUsersByUserId_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - err := r.UserUpsert(context.Background(), &User{ - TgId: id, - TgUsername: "teadove", - TgName: "teadove", - }) - assert.NoError(t, err) - - users, err := r.GetUsersById(ctx, []int64{id}) - assert.NoError(t, err) - - assert.Len(t, users, 1) - assert.Equal(t, "teadove", users[0].TgUsername) -} - -func TestIntegration_DbRepository_GetLastMessage_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - err := r.MessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 1}) - require.NoError(t, err) - err = r.MessageCreate(ctx, &Message{Text: "2", TgChatID: id, TgUserId: id, TgId: 2}) - require.NoError(t, err) - - msg, err := r.GetLastMessage(context.Background(), id) - assert.NoError(t, err) - assert.Equal(t, "1", msg.Text) -} - -func TestIntegration_DbRepository_MessageCreateOrNothingAndSetTime_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - err := r.MessageCreateOrNothingAndSetTime( - ctx, - &Message{Text: "2", TgChatID: id, TgUserId: id, TgId: 100}, - ) - require.NoError(t, err) - err = r.MessageCreateOrNothingAndSetTime( - ctx, - &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 100}, - ) - require.NoError(t, err) - - msg, err := r.GetLastMessage(context.Background(), id) - assert.NoError(t, err) - assert.Equal(t, "2", msg.Text) -} - -func TestIntegration_DbRepository_CheckUserExists_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - err := r.UserUpsert(ctx, &User{ - TgId: id, - TgUsername: "teadove", - TgName: "teadove", - }) - require.NoError(t, err) - - ok, err := r.CheckUserExists(ctx, id) - assert.NoError(t, err) - assert.True(t, ok) -} - -func TestIntegration_DbRepository_CheckUserExists_False(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - - ok, err := r.CheckUserExists(ctx, id) - assert.NoError(t, err) - assert.False(t, ok) -} - -func TestIntegration_DbRepository_GetUserById_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - err := r.UserUpsert(ctx, &User{ - TgId: id, - TgUsername: "teadove", - TgName: "teadove", - }) - require.NoError(t, err) - - user, err := r.GetUserById(ctx, id) - assert.NoError(t, err) - assert.Equal(t, "teadove", user.TgName) -} - -func TestIntegration_DbRepository_GetUsersByChatId_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - _, err := r.GetUsersByChatId(ctx, 1825059942) - assert.NoError(t, err) -} - -func TestIntegration_DbRepository_GetMessagesByChatAndUsername_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - _, err := r.GetMessagesByChatAndUsername(ctx, 1825059942, "TeaDove") - assert.NoError(t, err) -} - -func TestIntegration_DbRepository_DeleteMessagesByChat_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - err := r.MessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 1}) - require.NoError(t, err) - err = r.MessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 2}) - require.NoError(t, err) - err = r.MessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 3}) - require.NoError(t, err) - err = r.MessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 4}) - require.NoError(t, err) - err = r.MessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 5}) - require.NoError(t, err) - - count, err := r.DeleteMessagesByChat(ctx, id) - assert.NoError(t, err) - assert.Equal(t, int64(5), count) -} - -func TestIntegration_DbRepository_Ping_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - - err := r.Ping(ctx) - assert.NoError(t, err) -} - -func TestIntegration_DbRepository_StatsForTable_Ok(t *testing.T) { - r := getRepository(t) - generateMessage(r, t) - ctx := context.Background() - - _, err := r.StatsForTable(ctx, mgm.CollName(&Message{})) - assert.NoError(t, err) -} - -func TestIntegration_DbRepository_StatsForDatabase_Ok(t *testing.T) { - r := getRepository(t) - generateMessage(r, t) - ctx := context.Background() - - stats, err := r.StatsForDatabase(ctx) - assert.NoError(t, err) - shared.SendInterface(stats) -} - -func TestIntegration_DbRepository_MessageGetSortedLimited_Ok(t *testing.T) { - r := getRepository(t) - generateMessage(r, t) - ctx := context.Background() - - messages, err := r.MessageGetSortedLimited(ctx, 1000) - assert.NoError(t, err) - assert.GreaterOrEqual(t, len(messages), 0) - assert.LessOrEqual(t, len(messages), 1000) -} - -func TestIntegration_DbRepository_DeleteMessages_Ok(t *testing.T) { - r := getRepository(t) - messages := generateMessage(r, t) - - ctx := context.Background() - - count, err := r.DeleteMessages(ctx, messages) - assert.NoError(t, err) - assert.Len(t, messages, int(count)) -} - -func TestIntegration_DbRepository_GenerateMessages_Ok(t *testing.T) { - r := getRepository(t) - _ = generateMessage(r, t) -} - -func TestIntegration_DbRepository_ReleaseMemory_Ok(t *testing.T) { - r := getRepository(t) - ctx := context.Background() - - messages := generateMessage(r, t) - _, err := r.DeleteMessages(ctx, messages) - require.NoError(t, err) - - _, err = r.ReleaseMemory(ctx) - assert.NoError(t, err) -} - -func TestIntegration_DbRepository_SetReloadMessage_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - err := r.MessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 1}) - require.NoError(t, err) - - err = r.RestartMessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 1}) - require.NoError(t, err) - - id = rand.Int63n(1_000_000) - err = r.RestartMessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 1}) - require.NoError(t, err) -} - -func TestIntegration_DbRepository_ReloadMessageCreateGetAndDelete_Ok(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - err := r.RestartMessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 1}) - require.NoError(t, err) - - id = rand.Int63n(1_000_000) - err = r.RestartMessageCreate(ctx, &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 1}) - require.NoError(t, err) - - _, err = r.RestartMessageGetAndDelete(ctx) - assert.NoError(t, err) - - messages, err := r.RestartMessageGetAndDelete(ctx) - assert.NoError(t, err) - assert.Len(t, messages, 0) -} - -func TestIntegration_DbRepository_PingMessageGetAndDeleteForDeletion_NoMessagesOk(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - err := r.PingMessageCreate( - ctx, - &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 1}, - time.Now().UTC().Add(1*time.Hour), - ) - require.NoError(t, err) - - messages, err := r.PingMessageGetAndDeleteForDeletion(ctx) - assert.NoError(t, err) - assert.Len(t, messages, 0) -} - -func TestIntegration_DbRepository_PingMessageGetAndDeleteForDeletion_OneMessageOk(t *testing.T) { - t.Parallel() - r := getRepository(t) - - ctx := context.Background() - id := rand.Int63n(1_000_000) - err := r.PingMessageCreate( - ctx, - &Message{Text: "1", TgChatID: id, TgUserId: id, TgId: 1}, - time.Now().UTC().Add(-1*time.Hour), - ) - require.NoError(t, err) - - messages, err := r.PingMessageGetAndDeleteForDeletion(ctx) - assert.NoError(t, err) - assert.Len(t, messages, 1) -} diff --git a/core/repository/mongo_repository/models.go b/core/repository/mongo_repository/models.go deleted file mode 100644 index ebb90e1..0000000 --- a/core/repository/mongo_repository/models.go +++ /dev/null @@ -1,125 +0,0 @@ -package mongo_repository - -import ( - "time" - - "github.com/gotd/td/tg" - "github.com/teadove/fun_telegram/core/supplier/kandinsky_supplier" - - "github.com/kamva/mgm/v3" - "go.mongodb.org/mongo-driver/bson/primitive" -) - -type Message struct { - mgm.DefaultModel `bson:",inline"` - - TgChatID int64 `bson:"tg_chat_id"` - TgId int `bson:"tg_id"` - TgUserId int64 `bson:"tg_user_id"` - Text string -} - -type RestartMessage struct { - mgm.DefaultModel `bson:",inline"` - - MessageId primitive.ObjectID `bson:"message_id"` -} - -type PingMessage struct { - mgm.DefaultModel `bson:",inline"` - - MessageId primitive.ObjectID `bson:"message_id"` - DeleteAt time.Time `bson:"delete_at"` -} - -type User struct { - mgm.DefaultModel `bson:",inline"` - - TgId int64 `bson:"tg_id"` - TgUsername string `bson:"tg_username"` - TgName string `bson:"tg_name"` - IsBot bool `bson:"is_bot"` -} - -type MemberStatus string - -const ( - Plain MemberStatus = "PLAIN" - // Creator is status for chat/channel creator. - Creator MemberStatus = "CREATOR" - // Admin is status for chat/channel admin. - Admin MemberStatus = "ADMIN" - // Banned is status for banned user. - Banned MemberStatus = "BANNED" - // Left is status for user that left chat/channel. - Left MemberStatus = "LEFT" - - Unknown MemberStatus = "UNKNOWN" -) - -type Member struct { - mgm.DefaultModel `bson:",inline"` - - TgUserId int64 `bson:"tg_user_id"` - TgChatId int64 `bson:"tg_chat_id"` - Status MemberStatus -} - -type Chat struct { - mgm.DefaultModel `bson:",inline"` - - TgId int64 `bson:"tg_chat_id"` - Title string -} - -type UserInChat struct { - TgId int64 `bson:"tg_id"` - TgUsername string `bson:"tg_username"` - TgName string `bson:"tg_name"` - IsBot bool `bson:"is_bot"` - Status MemberStatus -} - -type UsersInChat []UserInChat - -func (r UsersInChat) ToMap() map[int64]UserInChat { - map_ := make(map[int64]UserInChat, len(r)) - for _, user := range r { - map_[user.TgId] = user - } - - return map_ -} - -func (r UsersInChat) ToIds() []int64 { - slice := make([]int64, len(r)) - for _, user := range r { - slice = append(slice, user.TgId) - } - - return slice -} - -type Image struct { - mgm.DefaultModel `bson:",inline"` - - Content []byte `bson:"content"` -} - -type TgImage struct { - mgm.DefaultModel `bson:",inline"` - - TgInputPhoto tg.InputPhoto `bson:"tg_input_photo"` - - MessageId primitive.ObjectID `bson:"message_id"` - ImageId primitive.ObjectID `bson:"image_id"` -} - -type KandinskyImage struct { - mgm.DefaultModel `bson:",inline"` - - Input kandinsky_supplier.RequestGenerationInput `bson:"input"` - - TgImageId primitive.ObjectID `bson:"tg_image_id"` - ImageId primitive.ObjectID `bson:"image_id"` -} diff --git a/core/repository/mongo_repository/repository.go b/core/repository/mongo_repository/repository.go deleted file mode 100644 index 6a0ec40..0000000 --- a/core/repository/mongo_repository/repository.go +++ /dev/null @@ -1,59 +0,0 @@ -package mongo_repository - -import ( - "time" - - "github.com/kamva/mgm/v3" - "github.com/pkg/errors" - "github.com/teadove/fun_telegram/core/shared" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" -) - -type Repository struct { - messageCollection *mgm.Collection - userCollection *mgm.Collection - memberCollection *mgm.Collection - chatCollection *mgm.Collection - restartMessageCollection *mgm.Collection - pingMessageCollection *mgm.Collection - - imageCollection *mgm.Collection - tgImageCollection *mgm.Collection - kandinskyImageCollection *mgm.Collection - - client *mongo.Client -} - -const databaseName = "db_main" - -func New() (*Repository, error) { - r := Repository{} - - err := mgm.SetDefaultConfig( - &mgm.Config{CtxTimeout: 12 * time.Second}, - databaseName, - options.Client().ApplyURI(shared.AppSettings.Storage.MongoDbUrl), - ) - if err != nil { - return nil, errors.WithStack(err) - } - - r.messageCollection = mgm.Coll(&Message{}) - r.userCollection = mgm.Coll(&User{}) - r.restartMessageCollection = mgm.Coll(&RestartMessage{}) - r.pingMessageCollection = mgm.Coll(&PingMessage{}) - r.memberCollection = mgm.Coll(&Member{}) - r.chatCollection = mgm.Coll(&Chat{}) - - r.imageCollection = mgm.Coll(&Image{}) - r.kandinskyImageCollection = mgm.Coll(&KandinskyImage{}) - r.tgImageCollection = mgm.Coll(&TgImage{}) - - r.client, err = mgm.NewClient(options.Client().ApplyURI(shared.AppSettings.Storage.MongoDbUrl)) - if err != nil { - return nil, errors.WithStack(err) - } - - return &r, nil -} diff --git a/core/repository/mongo_repository/stats.go b/core/repository/mongo_repository/stats.go deleted file mode 100644 index 847ed86..0000000 --- a/core/repository/mongo_repository/stats.go +++ /dev/null @@ -1,107 +0,0 @@ -package mongo_repository - -import ( - "context" - - "github.com/kamva/mgm/v3" - "github.com/pkg/errors" - "github.com/teadove/fun_telegram/core/schemas" - "go.mongodb.org/mongo-driver/bson" -) - -func (r *Repository) Ping(ctx context.Context) error { - err := r.client.Ping(ctx, nil) - if err != nil { - return errors.WithStack(err) - } - - return nil -} - -func (r *Repository) StatsForMessages(ctx context.Context) (schemas.StorageStats, error) { - return r.StatsForTable(ctx, mgm.CollName(&Message{})) -} - -func (r *Repository) StatsForTable( - ctx context.Context, - collName string, -) (schemas.StorageStats, error) { - result := r.client.Database(databaseName).RunCommand(ctx, bson.M{"collStats": collName}) - - var document bson.M - - err := result.Decode(&document) - if err != nil { - return schemas.StorageStats{}, errors.WithStack(err) - } - - stats := schemas.StorageStats{} - - count, ok := document["count"].(int32) - if !ok { - return schemas.StorageStats{}, errors.New("failed to get count from stats") - } - - stats.Count = int(count) - - totalSize, ok := document["totalSize"].(int32) - if !ok { - return schemas.StorageStats{}, errors.New("failed to get totalSize from stats") - } - - stats.TotalSizeBytes = int(totalSize) - - if stats.Count != 0 { - stats.AvgObjWithIndexSizeBytes = stats.TotalSizeBytes / stats.Count - } - - return stats, nil -} - -func (r *Repository) StatsForDatabase( - ctx context.Context, -) (map[string]schemas.StorageStats, error) { - colls, err := r.client.Database(databaseName).ListCollectionNames(ctx, bson.M{}) - if err != nil { - return nil, errors.WithStack(err) - } - - map_ := make(map[string]schemas.StorageStats, len(colls)) - for _, coll := range colls { - map_[coll], err = r.StatsForTable(ctx, coll) - if err != nil { - return nil, errors.WithStack(err) - } - } - - return map_, nil -} - -func (r *Repository) ReleaseMemory(ctx context.Context) (int, error) { - bytesFreed := 0 - - colls, err := r.client.Database(databaseName).ListCollectionNames(ctx, bson.M{}) - if err != nil { - return 0, errors.WithStack(err) - } - - for _, coll := range colls { - result := r.client.Database(databaseName).RunCommand(ctx, bson.M{"compact": coll}) - - var document bson.M - - err = result.Decode(&document) - if err != nil { - return 0, errors.WithStack(err) - } - - bytesFreedColl, ok := document["bytesFreed"].(int32) - if !ok { - return 0, errors.New("failed to get bytesFreed") - } - - bytesFreed += int(bytesFreedColl) - } - - return bytesFreed, nil -} diff --git a/core/repository/mongo_repository/user_methods.go b/core/repository/mongo_repository/user_methods.go deleted file mode 100644 index e57a694..0000000 --- a/core/repository/mongo_repository/user_methods.go +++ /dev/null @@ -1,90 +0,0 @@ -package mongo_repository - -import ( - "context" - "strings" - "time" - - "github.com/pkg/errors" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo/options" -) - -func (r *Repository) UserUpsert(ctx context.Context, user *User) error { - user.UpdatedAt = time.Now().UTC() - - filter := bson.M{"tg_id": user.TgId} - update := bson.M{"$set": bson.M{ - "tg_id": user.TgId, - "tg_username": user.TgUsername, - "tg_name": user.TgName, - "updated_at": user.UpdatedAt, - "created_at": user.CreatedAt, - "is_bot": user.IsBot, - }} - opts := options.Update().SetUpsert(true) - - _, err := r.userCollection.UpdateOne(ctx, filter, update, opts) - if err != nil { - return errors.WithStack(err) - } - - return nil -} - -func (r *Repository) GetUsersById(ctx context.Context, usersId []int64) ([]User, error) { - users := make([]User, 0, len(usersId)) - - err := r.userCollection.SimpleFindWithCtx(ctx, &users, bson.M{"tg_id": bson.M{"$in": usersId}}) - if err != nil { - return nil, errors.WithStack(err) - } - - return users, nil -} - -func (r *Repository) GetUsersByChatId(ctx context.Context, chatId int64) ([]User, error) { - userIds, err := r.messageCollection.Distinct(ctx, "tg_id", bson.M{"tg_chat_id": chatId}) - if err != nil { - return nil, errors.WithStack(err) - } - - usersIdsConcrete := make([]int64, 0, len(userIds)) - - for _, userId := range userIds { - userIdConcrete, ok := userId.(int64) - if !ok { - return nil, errors.New("non int64 type") - } - - usersIdsConcrete = append(usersIdsConcrete, userIdConcrete) - } - - return r.GetUsersById(ctx, usersIdsConcrete) -} - -func (r *Repository) GetUserById(ctx context.Context, userId int64) (User, error) { - var user User - - err := r.userCollection.FirstWithCtx(ctx, bson.M{"tg_id": userId}, &user) - if err != nil { - return User{}, errors.WithStack(err) - } - - return user, nil -} - -func (r *Repository) GetUserByUsername(ctx context.Context, username string) (User, error) { - var user User - - err := r.userCollection.FirstWithCtx( - ctx, - bson.M{"tg_username": strings.ToLower(username)}, - &user, - ) - if err != nil { - return User{}, errors.WithStack(err) - } - - return user, nil -} diff --git a/core/service/analitics/analise.go b/core/service/analitics/analise.go index 36f40be..f71a119 100644 --- a/core/service/analitics/analise.go +++ b/core/service/analitics/analise.go @@ -4,11 +4,12 @@ import ( "context" "sync" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/teadove/fun_telegram/core/service/resource" "github.com/teadove/fun_telegram/core/supplier/ds_supplier" "github.com/pkg/errors" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" ) func (r *Service) getChatterBoxes( @@ -18,7 +19,7 @@ func (r *Service) getChatterBoxes( input *AnaliseChatInput, getter nameGetter, asc bool, - usersInChat mongo_repository.UsersInChat, + usersInChat db_repository.UsersInChat, ) { defer wg.Done() output := statsReport{ @@ -46,11 +47,11 @@ func (r *Service) getChatterBoxes( output.repostImage.Name = "ChatterBoxes" } - userToCountArray, err := r.chRepository.GroupedCountGetByChatIdByUserIdAsc( + userToCountArray, err := r.dbRepository.MessageGroupByChatIdAndUserId( ctx, input.TgChatId, - limit, usersInChat.ToIds(), + limit, asc, ) if err != nil { @@ -106,7 +107,7 @@ func (r *Service) getMessageFindRepliedBy( }, } - interlocutors, err := r.chRepository.MessageFindRepliedBy( + interlocutors, err := r.dbRepository.MessageFindRepliedBy( ctx, input.TgChatId, input.TgUserId, @@ -178,7 +179,7 @@ func (r *Service) getMessageFindRepliesTo( }, } - interlocutors, err := r.chRepository.MessageFindRepliesTo( + interlocutors, err := r.dbRepository.MessageFindRepliesTo( ctx, input.TgChatId, input.TgUserId, @@ -240,7 +241,7 @@ func (r *Service) getMessageFindAllRepliedByGraph( wg *sync.WaitGroup, statsReportChan chan<- statsReport, input *AnaliseChatInput, - usersInChat mongo_repository.UsersInChat, + usersInChat db_repository.UsersInChat, getter nameGetter, ) { defer wg.Done() @@ -253,7 +254,7 @@ func (r *Service) getMessageFindAllRepliedByGraph( edges := make([]ds_supplier.GraphEdge, 0, len(usersInChat)*interlocutorsLimit) for _, user := range usersInChat { - replies, err := r.chRepository.MessageFindRepliesTo( + replies, err := r.dbRepository.MessageFindRepliesTo( ctx, input.TgChatId, user.TgId, @@ -310,7 +311,7 @@ func (r *Service) getMessageFindAllRepliedByHeatmap( wg *sync.WaitGroup, statsReportChan chan<- statsReport, input *AnaliseChatInput, - usersInChat mongo_repository.UsersInChat, + usersInChat db_repository.UsersInChat, getter nameGetter, ) { defer wg.Done() @@ -323,7 +324,7 @@ func (r *Service) getMessageFindAllRepliedByHeatmap( edges := make([]ds_supplier.GraphEdge, 0, len(usersInChat)*interlocutorsLimit) for _, user := range usersInChat { - replies, err := r.chRepository.MessageFindRepliesTo( + replies, err := r.dbRepository.MessageFindRepliesTo( ctx, input.TgChatId, user.TgId, diff --git a/core/service/analitics/channel.go b/core/service/analitics/channel.go index 9dc1d5e..7f1c7c9 100644 --- a/core/service/analitics/channel.go +++ b/core/service/analitics/channel.go @@ -5,19 +5,20 @@ import ( "runtime" "time" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/teadove/fun_telegram/core/service/resource" "github.com/teadove/fun_telegram/core/shared" "github.com/teadove/fun_telegram/core/supplier/ds_supplier" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/teadove/fun_telegram/core/repository/ch_repository" ) -func (r *Service) ChannelInsert(ctx context.Context, channel *ch_repository.Channel) error { - channel.UploadedAt = time.Now().UTC() +func (r *Service) ChannelInsert(ctx context.Context, channel *db_repository.Channel) error { + channel.CreatedInDBAt = time.Now().UTC() - err := r.chRepository.ChannelInsert(ctx, channel) + err := r.dbRepository.ChannelInsert(ctx, channel) if err != nil { return errors.Wrap(err, "failed to insert channel") } @@ -31,10 +32,10 @@ func (r *Service) ChannelInsert(ctx context.Context, channel *ch_repository.Chan return nil } -func (r *Service) ChannelSelect(ctx context.Context, id int64) (ch_repository.Channel, error) { - channel, err := r.chRepository.ChannelSelectById(ctx, id) +func (r *Service) ChannelSelect(ctx context.Context, id int64) (db_repository.Channel, error) { + channel, err := r.dbRepository.ChannelSelectById(ctx, id) if err != nil { - return ch_repository.Channel{}, errors.Wrap(err, "failed to select channel") + return db_repository.Channel{}, errors.Wrap(err, "failed to select channel") } return channel, nil @@ -44,13 +45,13 @@ var channelDataTtl = time.Hour * 24 * 60 func (r *Service) channelEdgeBatchInsert( ctx context.Context, - channels ch_repository.Channels, + channels db_repository.Channels, ) error { - edges := make([]ch_repository.ChannelEdge, 0, len(channels)*2) + edges := make([]db_repository.ChannelEdge, 0, len(channels)*2) for _, channelIn := range channels { for idx, channelOut := range channelIn.RecommendationsIds { - edges = append(edges, ch_repository.ChannelEdge{ + edges = append(edges, db_repository.ChannelEdge{ TgIdIn: channelIn.TgId, TgIdOut: channelOut, Order: int64(idx), @@ -58,7 +59,7 @@ func (r *Service) channelEdgeBatchInsert( } } - err := r.chRepository.ChannelEdgeBatchInsert(ctx, edges) + err := r.dbRepository.ChannelEdgeBatchInsert(ctx, edges) if err != nil { return errors.Wrap(err, "failed to batch insert channel edges") } @@ -69,13 +70,13 @@ func (r *Service) channelEdgeBatchInsert( // ChannelBatchInsert // nolint: cyclop // TODO fix cyclop -func (r *Service) ChannelBatchInsert(ctx context.Context, channels []ch_repository.Channel) error { +func (r *Service) ChannelBatchInsert(ctx context.Context, channels []db_repository.Channel) error { channelIds := make([]int64, len(channels)) for idx, channel := range channels { channelIds[idx] = channel.TgId } - oldChannels, err := r.chRepository.ChannelSelectByIds(ctx, channelIds) + oldChannels, err := r.dbRepository.ChannelSelectByIds(ctx, channelIds) if err != nil { return errors.Wrap(err, "failed to select channels") } @@ -84,17 +85,17 @@ func (r *Service) ChannelBatchInsert(ctx context.Context, channels []ch_reposito leafsCount := 0 alreadyProcessed := 0 - newChannels := make([]ch_repository.Channel, 0, len(channels)) + newChannels := make([]db_repository.Channel, 0, len(channels)) for _, channel := range channels { oldChannel, ok := oldChannelsMap[channel.TgId] - if ok && time.Since(oldChannel.UploadedAt) < channelDataTtl && !oldChannel.IsLeaf && + if ok && time.Since(oldChannel.CreatedInDBAt) < channelDataTtl && !oldChannel.IsLeaf && channel.IsLeaf { alreadyProcessed++ continue } - channel.UploadedAt = time.Now().UTC() + channel.CreatedInDBAt = time.Now().UTC() if channel.RecommendationsIds == nil { leafsCount++ } @@ -102,7 +103,7 @@ func (r *Service) ChannelBatchInsert(ctx context.Context, channels []ch_reposito newChannels = append(newChannels, channel) } - err = r.chRepository.ChannelBatchInsert(ctx, newChannels) + err = r.dbRepository.ChannelBatchInsert(ctx, newChannels) if err != nil { return errors.Wrap(err, "failed to insert channels") } @@ -136,16 +137,16 @@ func (r *Service) DumpChannels( var ( err error - channelEdges ch_repository.ChannelsEdges + channelEdges db_repository.ChannelsEdges ) if username != "" { - channelEdges, err = r.chRepository.ChannelEdgesSelectDFS(ctx, username, depth, maxOrder) + channelEdges, err = r.dbRepository.ChannelEdgesSelectDFS(ctx, username, depth, maxOrder) if err != nil { return nil, errors.Wrap(err, "failed to dsf channel edges") } } else { - channelEdges, err = r.chRepository.ChannelEdgesSelect(ctx, maxOrder) + channelEdges, err = r.dbRepository.ChannelEdgesSelect(ctx, maxOrder) if err != nil { return nil, errors.Wrap(err, "failed to dsf channel edges") } @@ -214,12 +215,12 @@ type AnaliseChannelInput struct { } func (r *Service) AnaliseChannel(ctx context.Context, input *AnaliseChannelInput) (File, error) { - rootChannel, err := r.chRepository.ChannelSelectByUsername(ctx, input.TgUsername) + rootChannel, err := r.dbRepository.ChannelSelectByUsername(ctx, input.TgUsername) if err != nil { return File{}, errors.Wrap(err, "failed to select channel by username") } - channelEdges, err := r.chRepository.ChannelEdgesSelectDFS( + channelEdges, err := r.dbRepository.ChannelEdgesSelectDFS( ctx, input.TgUsername, input.Depth, @@ -233,7 +234,7 @@ func (r *Service) AnaliseChannel(ctx context.Context, input *AnaliseChannelInput return File{}, errors.New("no channel edges found") } - channels, err := r.chRepository.ChannelSelectByIds(ctx, channelEdges.ToIds()) + channels, err := r.dbRepository.ChannelSelectByIds(ctx, channelEdges.ToIds()) if err != nil { return File{}, errors.Wrap(err, "failed to select channel") } diff --git a/core/service/analitics/image.go b/core/service/analitics/image.go index 877b1e3..f588c3b 100644 --- a/core/service/analitics/image.go +++ b/core/service/analitics/image.go @@ -3,15 +3,16 @@ package analitics import ( "context" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/pkg/errors" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" ) func (r *Service) KandinskyImageInsert( ctx context.Context, - input *mongo_repository.KandinskyImageDenormalized, + input *db_repository.KandinskyImageDenormalized, ) error { - err := r.mongoRepository.KandinskyImageInsert(ctx, input) + err := r.dbRepository.KandinskyImageInsert(ctx, input) if err != nil { return errors.Wrap(err, "failed to insert image") } @@ -21,9 +22,9 @@ func (r *Service) KandinskyImageInsert( func (r *Service) KandinskyImagePaginate( ctx context.Context, - input *mongo_repository.KandinskyImagePaginateInput, -) ([]mongo_repository.KandinskyImageDenormalized, error) { - res, err := r.mongoRepository.KandinskyImagePaginate(ctx, input) + input *db_repository.KandinskyImagePaginateInput, +) ([]db_repository.KandinskyImageDenormalized, error) { + res, err := r.dbRepository.KandinskyImagePaginate(ctx, input) if err != nil { return nil, errors.Wrap(err, "failed to paginate in mongodb") } diff --git a/core/service/analitics/image_test.go b/core/service/analitics/image_test.go index c488527..9417592 100644 --- a/core/service/analitics/image_test.go +++ b/core/service/analitics/image_test.go @@ -3,8 +3,9 @@ package analitics import ( "testing" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/stretchr/testify/assert" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" "github.com/teadove/fun_telegram/core/shared" ) @@ -13,7 +14,7 @@ func TestIntegration_AnaliticsService_KandinskyImagePaginate_Ok(t *testing.T) { r := getService(t) ctx := shared.GetModuleCtx("tests") - images, err := r.KandinskyImagePaginate(ctx, &mongo_repository.KandinskyImagePaginateInput{ + images, err := r.KandinskyImagePaginate(ctx, &db_repository.KandinskyImagePaginateInput{ TgChatId: 1178533048, Page: 0, PageSize: 10, diff --git a/core/service/analitics/markov.go b/core/service/analitics/markov.go new file mode 100644 index 0000000..0518930 --- /dev/null +++ b/core/service/analitics/markov.go @@ -0,0 +1,87 @@ +package analitics + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/rs/zerolog" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/teadove/fun_telegram/core/shared" +) + +func (r *Service) buildChain(ctx context.Context, messages []db_repository.Message) error { + const ( + minFreakToUse = 3 + ) + + t0 := time.Now().UTC() + + sentences := make([]string, 0, len(messages)) + for _, message := range messages { + text := strings.ToLower(removeNonAlphanumeric(message.Text)) + sentencesParsed := strings.FieldsFunc(text, func(r rune) bool { + if r == '\n' { + return true + } + if r == '.' { + return true + } + + return false + }) + + for _, sentence := range sentencesParsed { + if len(sentence) <= 6 { + continue + } + + sentences = append(sentences, sentence) + } + + } + + chain := make(map[string]map[string]int, 10) + + for _, sentence := range sentences { + fields := strings.Fields(sentence) + for idx := range len(fields) - 2 { + word1 := strings.TrimSpace(fields[idx]) + word2 := strings.TrimSpace(fields[idx+1]) + word3 := strings.TrimSpace(fields[idx+2]) + + key := fmt.Sprintf("%s %s", word1, word2) + v, ok := chain[key] + if ok { + v[word3] += 1 + } else { + chain[key] = map[string]int{word3: 1} + } + } + } + + chainFixed := make(map[string]map[string]int, 10) + for k, v := range chain { + sentToFreq := make(map[string]int) + for ki, vi := range v { + if vi >= minFreakToUse { + sentToFreq[ki] = vi + } + } + + if len(sentToFreq) != 0 { + chainFixed[k] = sentToFreq + } + } + + shared.SendInterface(chainFixed) + + zerolog.Ctx(ctx). + Debug(). + Int("messages_processed", len(messages)). + Int("chain_size", len(chainFixed)). + Str("elapsed", time.Since(t0).String()). + Msg("messages.for.markov.chain.fetched") + return nil +} diff --git a/core/service/analitics/markov_test.go b/core/service/analitics/markov_test.go new file mode 100644 index 0000000..6f65e07 --- /dev/null +++ b/core/service/analitics/markov_test.go @@ -0,0 +1,19 @@ +package analitics + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/teadove/fun_telegram/core/shared" +) + +func TestIntegration_AnaliticsService_BuildMarkov_Ok(t *testing.T) { + r := getService(t) + ctx := shared.GetModuleCtx("tests") + + messages, err := r.dbRepository.MessageSelectByChatIdWithWordsCount(ctx, 1798223288, 2, 100_000) + assert.NoError(t, err) + + err = r.buildChain(ctx, messages) + assert.NoError(t, err) +} diff --git a/core/service/analitics/message.go b/core/service/analitics/message.go index ded170f..dcbdf77 100644 --- a/core/service/analitics/message.go +++ b/core/service/analitics/message.go @@ -5,29 +5,35 @@ import ( "strings" "github.com/pkg/errors" - "github.com/teadove/fun_telegram/core/repository/ch_repository" + "github.com/teadove/fun_telegram/core/repository/db_repository" ) func (r *Service) MessageInsert(ctx context.Context, message *Message) error { - chMessage := &ch_repository.Message{ - CreatedAt: message.CreatedAt, - TgChatID: message.TgChatID, - TgUserId: message.TgUserId, - Text: message.Text, - TgId: message.TgId, - ReplyToMsgID: message.ReplyToMsgID, - ReplyToUserID: message.ReplyToUserID, + chMessage := &db_repository.Message{ + WithCreatedAt: db_repository.WithCreatedAt{CreatedAt: message.CreatedAt}, + TgChatID: message.TgChatID, + TgUserId: message.TgUserId, + Text: message.Text, + TgId: message.TgId, + ReplyToTgMsgID: message.ReplyToMsgID, + ReplyToTgUserID: message.ReplyToUserID, } words := strings.Fields(message.Text) - for _, word := range words { - word, ok := r.filterAndLemma(word) + var ( + ok bool + word string + err error + ) + + for _, word = range words { + word, ok = r.filterAndLemma(word) if !ok { continue } chMessage.WordsCount++ - ok, err := r.IsToxic(word) + ok, err = r.IsToxic(word) if err != nil { return errors.Wrap(err, "failed to check if word is toxic") } @@ -37,7 +43,7 @@ func (r *Service) MessageInsert(ctx context.Context, message *Message) error { } } - err := r.chRepository.MessageInsert(ctx, chMessage) + err = r.dbRepository.MessageInsert(ctx, chMessage) if err != nil { return errors.Wrap(err, "failed to insert message in ch repository") } @@ -45,40 +51,17 @@ func (r *Service) MessageInsert(ctx context.Context, message *Message) error { return nil } -func (r *Service) MessageSetReplyToUserId(ctx context.Context, chatId int64) error { - err := r.chRepository.MessageSetReplyToUserId(ctx, chatId) - if err != nil { - return errors.Wrap(err, "failed to set reply to user id in ch repository") - } - - return nil -} - -func (r *Service) DeleteMessagesByChatId(ctx context.Context, chatId int64) (int64, error) { - count, err := r.mongoRepository.DeleteMessagesByChat(ctx, chatId) +func (r *Service) DeleteMessagesByChatId(ctx context.Context, chatId int64) (uint64, error) { + count, err := r.dbRepository.MessagesDeleteByChat(ctx, chatId) if err != nil { return 0, errors.Wrap(err, "failed to delete messages from mongo repository") } - err = r.chRepository.MessageDeleteByChatId(ctx, chatId) - if err != nil { - return 0, errors.Wrap(err, "failed to delete messages from ch repository") - } - - return count, nil -} - -func (r *Service) DeleteAllMessages(ctx context.Context) (int64, error) { - count, err := r.mongoRepository.DeleteAllMessages(ctx) - if err != nil { - return 0, errors.Wrap(err, "failed to delete messages") - } - return count, nil } func (r *Service) GetLastMessage(ctx context.Context, chatId int64) (Message, error) { - message, err := r.chRepository.GetLastMessageByChatId(ctx, chatId) + message, err := r.dbRepository.MessageGetLastByChatId(ctx, chatId) if err != nil { return Message{}, errors.Wrap(err, "failed to get last message") } diff --git a/core/service/analitics/message_test.go b/core/service/analitics/message_test.go index d8c7233..2aa3df2 100644 --- a/core/service/analitics/message_test.go +++ b/core/service/analitics/message_test.go @@ -14,7 +14,7 @@ func generateMessage() Message { return Message{ CreatedAt: time.Now().UTC(), TgChatID: rand.Int64N(100000), - TgId: rand.Int64N(100000), + TgId: rand.IntN(100000), TgUserId: rand.Int64N(100000), Text: shared.RandomString(), } diff --git a/core/service/analitics/models.go b/core/service/analitics/models.go index a510b14..2e65f67 100644 --- a/core/service/analitics/models.go +++ b/core/service/analitics/models.go @@ -10,7 +10,7 @@ type Message struct { CreatedAt time.Time `json:"created_at"` TgChatID int64 `json:"tg_chat_id"` - TgId int64 `json:"tg_id"` + TgId int `json:"tg_id"` TgUserId int64 `json:"tg_user_id"` Text string `json:"text"` ReplyToMsgID null.Int64 `json:"reply_to_msg_id"` diff --git a/core/service/analitics/parquet.go b/core/service/analitics/parquet.go index c6177ce..4335f72 100644 --- a/core/service/analitics/parquet.go +++ b/core/service/analitics/parquet.go @@ -4,8 +4,9 @@ import ( "context" "io" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/pkg/errors" - "github.com/teadove/fun_telegram/core/repository/ch_repository" "github.com/xitongsys/parquet-go-source/mem" "github.com/xitongsys/parquet-go/parquet" "github.com/xitongsys/parquet-go/source" @@ -51,7 +52,7 @@ func pwWriteSlice(pw *writer.ParquetWriter, slice []any) error { } func (r *Service) dumpChannelsParquet(ctx context.Context, tgChatIds []int64) (File, error) { - channels, err := r.chRepository.ChannelSelectByIds(ctx, tgChatIds) + channels, err := r.dbRepository.ChannelSelectByIds(ctx, tgChatIds) if err != nil { return File{}, errors.Wrap(err, "failed to select channel") } @@ -65,7 +66,7 @@ func (r *Service) dumpChannelsParquet(ctx context.Context, tgChatIds []int64) (F fw, err := getMemFileWrite(&file) - pw, err := writer.NewParquetWriter(fw, new(ch_repository.Channel), 4) + pw, err := writer.NewParquetWriter(fw, new(db_repository.Channel), 4) if err != nil { return File{}, errors.Wrap(err, "failed to create parquet writer") } @@ -83,7 +84,7 @@ func (r *Service) dumpChannelsParquet(ctx context.Context, tgChatIds []int64) (F return file, nil } -func (r *Service) dumpChannelsEdgeParquet(channels ch_repository.ChannelsEdges) (File, error) { +func (r *Service) dumpChannelsEdgeParquet(channels db_repository.ChannelsEdges) (File, error) { slice := make([]any, 0, len(channels)) for _, v := range channels { slice = append(slice, v) @@ -93,7 +94,7 @@ func (r *Service) dumpChannelsEdgeParquet(channels ch_repository.ChannelsEdges) fw, err := getMemFileWrite(&file) - pw, err := writer.NewParquetWriter(fw, new(ch_repository.ChannelEdge), 4) + pw, err := writer.NewParquetWriter(fw, new(db_repository.ChannelEdge), 4) if err != nil { return File{}, errors.Wrap(err, "failed to create parquet writer") } @@ -112,7 +113,7 @@ func (r *Service) dumpChannelsEdgeParquet(channels ch_repository.ChannelsEdges) } func (r *Service) dumpMessagesParquet(ctx context.Context, tgChatIds []int64) (File, error) { - channels, err := r.chRepository.MessagesGetByChatIds(ctx, tgChatIds) + channels, err := r.dbRepository.MessageSelectByChatIds(ctx, tgChatIds) if err != nil { return File{}, errors.Wrap(err, "failed to select items") } @@ -126,7 +127,7 @@ func (r *Service) dumpMessagesParquet(ctx context.Context, tgChatIds []int64) (F fw, err := getMemFileWrite(&file) - pw, err := writer.NewParquetWriter(fw, new(ch_repository.MessageParquet), 4) + pw, err := writer.NewParquetWriter(fw, new(db_repository.MessageParquet), 4) if err != nil { return File{}, errors.Wrap(err, "failed to create parquet writer") } diff --git a/core/service/analitics/service.go b/core/service/analitics/service.go index e41a08a..e044cc3 100644 --- a/core/service/analitics/service.go +++ b/core/service/analitics/service.go @@ -9,6 +9,8 @@ import ( "sync" "time" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/teadove/fun_telegram/core/service/resource" "github.com/rs/zerolog" @@ -18,13 +20,10 @@ import ( "github.com/aaaton/golem/v4/dicts/ru" "github.com/dlclark/regexp2" "github.com/pkg/errors" - "github.com/teadove/fun_telegram/core/repository/ch_repository" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" ) type Service struct { - mongoRepository *mongo_repository.Repository - chRepository *ch_repository.Repository + dbRepository *db_repository.Repository dsSupplier *ds_supplier.Supplier resourceService *resource.Service @@ -33,16 +32,14 @@ type Service struct { } func New( - mongoRepository *mongo_repository.Repository, - chRepository *ch_repository.Repository, dsSupplier *ds_supplier.Supplier, resourceService *resource.Service, + dbRepository *db_repository.Repository, ) (*Service, error) { r := Service{ - mongoRepository: mongoRepository, - chRepository: chRepository, dsSupplier: dsSupplier, resourceService: resourceService, + dbRepository: dbRepository, } exp, err := regexp2.Compile( @@ -129,10 +126,11 @@ func (r *AnaliseReport) appendFromChan( if statsReportValue.err != nil { zerolog.Ctx(ctx). - Error().Stack().Err(statsReportValue.err). - Str("status", "failed.to.compile.statistics"). + Error(). + Stack(). + Err(statsReportValue.err). Dict("report", report). - Send() + Msg("failed.to.compile.statistics") continue } @@ -151,9 +149,8 @@ func (r *AnaliseReport) appendFromChan( zerolog.Ctx(ctx). Info(). - Str("status", "analitics.image.compiled"). Dict("report", report). - Send() + Msg("analitics.image.compiled") } } @@ -161,7 +158,7 @@ func (r *Service) analiseUserChat( ctx context.Context, input *AnaliseChatInput, ) (AnaliseReport, error) { - count, err := r.chRepository.CountGetByChatIdByUserId(ctx, input.TgChatId, input.TgUserId) + count, err := r.dbRepository.MessageCountByChatIdAndUserId(ctx, input.TgChatId, input.TgUserId) if err != nil { return AnaliseReport{}, errors.Wrap(err, "failed to get count from ch repository") } @@ -170,7 +167,7 @@ func (r *Service) analiseUserChat( return AnaliseReport{}, errors.WithStack(ErrNoMessagesFound) } - lastMessage, err := r.chRepository.GetLastMessageByChatIdByUserId( + lastMessage, err := r.dbRepository.MessageGetLastByChatIdAndUserId( ctx, input.TgChatId, input.TgUserId, @@ -179,11 +176,11 @@ func (r *Service) analiseUserChat( return AnaliseReport{}, errors.Wrap(err, "failed to get last message from ch repositry") } - if count == 0 { - return AnaliseReport{}, nil - } - - usersInChat, err := r.mongoRepository.GetUsersInChatOnlyActive(ctx, input.TgChatId) + usersInChat, err := r.dbRepository.UsersSelectByStatusInChat( + ctx, + input.TgChatId, + db_repository.MemberStatusesActive, + ) if err != nil { return AnaliseReport{}, errors.Wrap( err, @@ -236,7 +233,11 @@ func (r *Service) analiseWholeChat( ctx context.Context, input *AnaliseChatInput, ) (AnaliseReport, error) { - usersInChat, err := r.mongoRepository.GetUsersInChatOnlyActive(ctx, input.TgChatId) + usersInChat, err := r.dbRepository.UsersSelectByStatusInChat( + ctx, + input.TgChatId, + db_repository.MemberStatusesActive, + ) if err != nil { return AnaliseReport{}, errors.Wrap( err, @@ -244,12 +245,12 @@ func (r *Service) analiseWholeChat( ) } - count, err := r.chRepository.CountGetByChatId(ctx, input.TgChatId) + count, err := r.dbRepository.MessageCountByChatId(ctx, input.TgChatId) if err != nil { return AnaliseReport{}, errors.Wrap(err, "failed to get count from ch repository") } - lastMessage, err := r.chRepository.GetLastMessageByChatId(ctx, input.TgChatId) + lastMessage, err := r.dbRepository.MessageGetLastByChatId(ctx, input.TgChatId) if err != nil { return AnaliseReport{}, errors.Wrap(err, "failed to get last message from ch repositry") } @@ -321,7 +322,7 @@ func (r *Service) AnaliseChat( ctx context.Context, input *AnaliseChatInput, ) (report AnaliseReport, err error) { - zerolog.Ctx(ctx).Info().Str("status", "compiling.stats.begin").Interface("input", input).Send() + zerolog.Ctx(ctx).Info().Interface("input", input).Msg("compiling.stats.begin") if input.TgUserId != 0 { report, err = r.analiseUserChat(ctx, input) diff --git a/core/service/analitics/service_test.go b/core/service/analitics/service_test.go index d3dc68f..9a4e1eb 100644 --- a/core/service/analitics/service_test.go +++ b/core/service/analitics/service_test.go @@ -9,13 +9,14 @@ import ( "path/filepath" "testing" + "github.com/teadove/fun_telegram/core/infrastructure/pg" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/teadove/fun_telegram/core/service/resource" "github.com/teadove/fun_telegram/core/supplier/ds_supplier" "github.com/stretchr/testify/require" - "github.com/teadove/fun_telegram/core/repository/ch_repository" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" "github.com/teadove/fun_telegram/core/shared" ) @@ -46,19 +47,20 @@ func draw(t *testing.T, reportImages []File) { func getService(t *testing.T) *Service { ctx := shared.GetCtx() - dbRepository, err := mongo_repository.New() + + dsSupplier, err := ds_supplier.New(ctx) require.NoError(t, err) - chRepository, err := ch_repository.New(ctx) + resourceService, err := resource.New(ctx) require.NoError(t, err) - dsSupplier, err := ds_supplier.New(ctx) + db, err := pg.NewClientFromSettings() require.NoError(t, err) - resourceService, err := resource.New(ctx) + dbRepository, err := db_repository.NewRepository(ctx, db) require.NoError(t, err) - r, err := New(dbRepository, chRepository, dsSupplier, resourceService) + r, err := New(dsSupplier, resourceService, dbRepository) require.NoError(t, err) return r @@ -86,7 +88,7 @@ func TestIntegration_AnaliticsService_AnaliseChatForUser_Ok(t *testing.T) { ctx := shared.GetModuleCtx("tests") report, err := r.AnaliseChat(ctx, &AnaliseChatInput{ - TgChatId: 1701683862, + TgChatId: 1178533048, Tz: 3, TgUserId: 418878871, Locale: resource.En, diff --git a/core/service/analitics/time.go b/core/service/analitics/time.go index 3b92799..3cd8715 100644 --- a/core/service/analitics/time.go +++ b/core/service/analitics/time.go @@ -29,10 +29,10 @@ func (r *Service) getMessagesGroupedByDateByChatId( }, } - messagesGrouped, err := r.chRepository.GetMessagesGroupedByDateByChatId( + messagesGrouped, err := r.dbRepository.MessageGroupByDateAndChatId( ctx, input.TgChatId, - 86400*7, + time.Hour*24*7, ) if err != nil { statsReportResult.err = errors.Wrap(err, "failed to get messages from ch repository") @@ -83,11 +83,11 @@ func (r *Service) getMessagesGroupedByDateByChatIdByUserId( }, } - messagesGrouped, err := r.chRepository.GetMessagesGroupedByDateByChatIdByUserId( + messagesGrouped, err := r.dbRepository.MessageGroupByDateAndChatIdAndUserId( ctx, input.TgChatId, input.TgUserId, - 86400*7, + time.Hour*24*7, ) if err != nil { statsReportResult.err = errors.Wrap(err, "failed to get messages from ch repository") @@ -138,10 +138,10 @@ func (r *Service) getMessagesGroupedByTimeByChatId( }, } - messagesGrouped, err := r.chRepository.GetMessagesGroupedByTimeByChatId( + messagesGrouped, err := r.dbRepository.MessageGroupByTimeAndChatId( ctx, input.TgChatId, - 60*30, + time.Minute*10, input.Tz, ) if err != nil { @@ -213,11 +213,11 @@ func (r *Service) getMessagesGroupedByTimeByChatIdByUserId( }, } - messagesGrouped, err := r.chRepository.GetMessagesGroupedByTimeByChatIdByUserId( + messagesGrouped, err := r.dbRepository.MessageGroupByTimeAndChatIdAndUserId( ctx, input.TgChatId, input.TgUserId, - 60*30, + time.Minute*10, input.Tz, ) if err != nil { diff --git a/core/service/analitics/toxicity.go b/core/service/analitics/toxicity.go index 1c97660..3bed5a5 100644 --- a/core/service/analitics/toxicity.go +++ b/core/service/analitics/toxicity.go @@ -5,7 +5,7 @@ import ( "strings" "sync" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" + "github.com/teadove/fun_telegram/core/repository/db_repository" "github.com/teadove/fun_telegram/core/service/resource" @@ -20,7 +20,7 @@ func (r *Service) getMostToxicUsers( statsReportChan chan<- statsReport, input *AnaliseChatInput, getter nameGetter, - usersInChat mongo_repository.UsersInChat, + usersInChat db_repository.UsersInChat, ) { defer wg.Done() @@ -33,11 +33,12 @@ func (r *Service) getMostToxicUsers( }, } - userToCountArray, err := r.chRepository.GroupedCountGetByChatIdByUserId( + userToCountArray, err := r.dbRepository.MessageGroupByChatIdAndUserId( ctx, input.TgChatId, - maxUsers, usersInChat.ToIds(), + maxUsers, + true, ) if err != nil { output.err = errors.Wrap(err, "failed to get GroupedCountGetByChatIdByUserId") diff --git a/core/service/analitics/utils.go b/core/service/analitics/utils.go index 02eda40..2240816 100644 --- a/core/service/analitics/utils.go +++ b/core/service/analitics/utils.go @@ -4,13 +4,13 @@ import ( "fmt" "strings" - "github.com/teadove/fun_telegram/core/shared" + "github.com/teadove/fun_telegram/core/repository/db_repository" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" + "github.com/teadove/fun_telegram/core/shared" ) type nameGetter struct { - idToUser map[int64]mongo_repository.UserInChat + idToUser map[int64]db_repository.UserInChat anonymize bool idToAnonName map[int64]string } @@ -47,10 +47,10 @@ func (r *nameGetter) getNameAndUsername(userId int64) string { } func (r *Service) getNameGetter( - usersInChat mongo_repository.UsersInChat, + usersInChat db_repository.UsersInChat, anonymize bool, ) nameGetter { - idToUser := make(map[int64]mongo_repository.UserInChat, len(usersInChat)) + idToUser := make(map[int64]db_repository.UserInChat, len(usersInChat)) for _, user := range usersInChat { idToUser[user.TgId] = user diff --git a/core/service/analitics/words.go b/core/service/analitics/words.go index 0eac819..dae9d10 100644 --- a/core/service/analitics/words.go +++ b/core/service/analitics/words.go @@ -1,6 +1,7 @@ package analitics import ( + "regexp" "strings" mapset "github.com/deckarep/golang-set/v2" @@ -192,3 +193,9 @@ func (r *Service) filterAndLemma(word string) (string, bool) { return word, true } + +var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Zа-яА-Я0-9 ]+`) + +func removeNonAlphanumeric(str string) string { + return nonAlphanumericRegex.ReplaceAllString(str, "") +} diff --git a/core/service/job/health.go b/core/service/job/health.go index 314b5e6..d748da6 100644 --- a/core/service/job/health.go +++ b/core/service/job/health.go @@ -92,14 +92,14 @@ func (r *Service) Check(ctx context.Context, frequent bool) CheckResults { zerolog.Ctx(ctx). Error().Stack(). Err(result.Err). - Str("status", "health.check.failed"). Str("service", result.Name). - Dur("elapsed", elapsed).Send() + Dur("elapsed", elapsed).Msg("health.check.failed") } else { zerolog.Ctx(ctx). - Debug().Str("status", "health.check.ok"). + Debug(). Str("service", result.Name). - Dur("elapsed", elapsed).Send() + Dur("elapsed", elapsed). + Msg("health.check.ok") } result.Elapsed = elapsed diff --git a/core/service/job/health_test.go b/core/service/job/health_test.go index a3124b6..281ffb8 100644 --- a/core/service/job/health_test.go +++ b/core/service/job/health_test.go @@ -5,7 +5,6 @@ import ( "testing" "time" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" "github.com/teadove/fun_telegram/core/shared" "github.com/pkg/errors" @@ -15,10 +14,8 @@ import ( func getService(t *testing.T) *Service { ctx := shared.GetModuleCtx("test") - repository, err := mongo_repository.New() - require.NoError(t, err) - r, err := New(ctx, repository, nil, nil) + r, err := New(ctx, nil) require.NoError(t, err) return r diff --git a/core/service/job/mem.go b/core/service/job/mem.go index 7d80b3e..69f2d4a 100644 --- a/core/service/job/mem.go +++ b/core/service/job/mem.go @@ -15,7 +15,6 @@ func (r *Service) logMemUsage(ctx context.Context) { zerolog.Ctx(ctx). Info(). - Str("status", "perfstats"). Float64("stop.the.world.ms", shared.ToFixed(float64(m.PauseTotalNs)/1024/1024, 2)). Float64("heap.alloc.mb", shared.ToMega(m.HeapAlloc)). Float64("heap.alloc.count.k", shared.ToKilo(m.HeapObjects)). @@ -24,5 +23,5 @@ func (r *Service) logMemUsage(ctx context.Context) { Float64("gc.cpu.percent", shared.ToFixed(m.GCCPUFraction*100, 4)). Uint32("gc.cycles", m.NumGC). Int("gorutine.count", runtime.NumGoroutine()). - Send() + Msg("perfstats") } diff --git a/core/service/job/service.go b/core/service/job/service.go index 4c07a1f..7572683 100644 --- a/core/service/job/service.go +++ b/core/service/job/service.go @@ -4,30 +4,27 @@ import ( "context" "time" + "github.com/teadove/fun_telegram/core/repository/db_repository" + "github.com/go-co-op/gocron" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/teadove/fun_telegram/core/repository/ch_repository" - "github.com/teadove/fun_telegram/core/repository/mongo_repository" "github.com/teadove/fun_telegram/core/schemas" "github.com/teadove/fun_telegram/core/shared" ) type Service struct { - mongoRepository *mongo_repository.Repository - chRepository *ch_repository.Repository + dbRepository *db_repository.Repository checkers map[string]ServiceChecker } func New( ctx context.Context, - dbRepository *mongo_repository.Repository, - chRepository *ch_repository.Repository, checkers map[string]ServiceChecker, ) (*Service, error) { ctx = shared.AddModuleCtx(ctx, "job") - r := Service{mongoRepository: dbRepository, checkers: checkers, chRepository: chRepository} + r := Service{checkers: checkers} scheduler := gocron.NewScheduler(time.UTC) @@ -73,19 +70,20 @@ type DeleteOldMessagesOutput struct { func (r *Service) Stats(ctx context.Context) (map[string]map[string]schemas.StorageStats, error) { statsByDatabase := make(map[string]map[string]schemas.StorageStats, 2) - stats, err := r.mongoRepository.StatsForDatabase(ctx) - if err != nil { - return nil, errors.WithStack(err) - } + // TODO compile stats + //stats, err := r.mongoRepository.StatsForDatabase(ctx) + //if err != nil { + // return nil, errors.WithStack(err) + //} - statsByDatabase["MongoDB"] = stats + // statsByDatabase["MongoDB"] = stats - stats, err = r.chRepository.StatsForDatabase(ctx) - if err != nil { - return nil, errors.WithStack(err) - } + //stats, err = r.chRepository.StatsForDatabase(ctx) + //if err != nil { + // return nil, errors.WithStack(err) + //} - statsByDatabase["Clickhouse"] = stats + // statsByDatabase["Clickhouse"] = stats return statsByDatabase, nil } @@ -103,50 +101,47 @@ func (r *Service) deleteOldMessagesChecked(ctx context.Context) { } func (r *Service) DeleteOldMessages(ctx context.Context) (DeleteOldMessagesOutput, error) { - bytesFreed, err := r.mongoRepository.ReleaseMemory(ctx) - if err != nil { - return DeleteOldMessagesOutput{}, errors.WithStack(err) - } - - stats, err := r.mongoRepository.StatsForMessages(ctx) - if err != nil { - return DeleteOldMessagesOutput{}, errors.WithStack(err) - } - - desiredSizeInBytes := 1024 * 1024 * shared.AppSettings.MessagesMaxSizeMB - if desiredSizeInBytes > stats.TotalSizeBytes { - return DeleteOldMessagesOutput{}, nil - } - - sizeToDelete := stats.TotalSizeBytes - desiredSizeInBytes - - countToDelete := sizeToDelete / stats.AvgObjWithIndexSizeBytes - if countToDelete == 0 { - return DeleteOldMessagesOutput{}, nil - } - - _, err = r.mongoRepository.DeleteMessagesOldWithCount(ctx, int64(countToDelete)) - if err != nil { - return DeleteOldMessagesOutput{}, errors.WithStack(err) - } - - newStats, err := r.mongoRepository.StatsForMessages(ctx) - if err != nil { - return DeleteOldMessagesOutput{}, errors.WithStack(err) - } - - output := DeleteOldMessagesOutput{ - OldCount: stats.Count, - NewCount: newStats.Count, - OldSize: stats.TotalSizeBytes, - NewSize: newStats.TotalSizeBytes, - BytesFreed: bytesFreed, - } - zerolog.Ctx(ctx). - Info(). - Str("status", "old.messages.deleted"). - Interface("output", output). - Send() - - return output, nil + // TODO delete old messages + + //bytesFreed, err := r.mongoRepository.ReleaseMemory(ctx) + //if err != nil { + // return DeleteOldMessagesOutput{}, errors.WithStack(err) + //} + // + //stats, err := r.mongoRepository.StatsForMessages(ctx) + //if err != nil { + // return DeleteOldMessagesOutput{}, errors.WithStack(err) + //} + + //desiredSizeInBytes := 1024 * 1024 * shared.AppSettings.MessagesMaxSizeMB + //if desiredSizeInBytes > stats.TotalSizeBytes { + // return DeleteOldMessagesOutput{}, nil + //} + // + //sizeToDelete := stats.TotalSizeBytes - desiredSizeInBytes + // + //countToDelete := sizeToDelete / stats.AvgObjWithIndexSizeBytes + //if countToDelete == 0 { + // return DeleteOldMessagesOutput{}, nil + //} + // + //newStats, err := r.mongoRepository.StatsForMessages(ctx) + //if err != nil { + // return DeleteOldMessagesOutput{}, errors.WithStack(err) + //} + // + //output := DeleteOldMessagesOutput{ + // OldCount: stats.Count, + // NewCount: newStats.Count, + // OldSize: stats.TotalSizeBytes, + // NewSize: newStats.TotalSizeBytes, + // BytesFreed: bytesFreed, + //} + //zerolog.Ctx(ctx). + // Info(). + // Str("status", "old.messages.deleted"). + // Interface("output", output). + // Send() + + return DeleteOldMessagesOutput{}, nil } diff --git a/core/shared/log.go b/core/shared/log.go index fea8313..bf1bc25 100644 --- a/core/shared/log.go +++ b/core/shared/log.go @@ -43,6 +43,7 @@ func getLogger() zerolog.Logger { logger := zerolog.New(os.Stderr). With(). Timestamp(). + Caller(). Logger(). Level(level). Output(zerolog.ConsoleWriter{Out: os.Stderr}) diff --git a/core/shared/settings.go b/core/shared/settings.go index 5a2b15a..5417197 100644 --- a/core/shared/settings.go +++ b/core/shared/settings.go @@ -1,7 +1,6 @@ package shared import ( - "context" "time" "github.com/pkg/errors" @@ -50,11 +49,12 @@ type Settings struct { func mustNewSettings() Settings { var settings Settings - ctx := context.Background() _ = godotenv.Load(defaultEnvFile) err := env.Parse(&settings, env.Options{Prefix: defaultEnvPrefix}) - Check(ctx, errors.Wrap(err, "failed to env parse")) + if err != nil { + panic(errors.Wrap(err, "failed to env parse")) + } return settings } diff --git a/core/supplier/ds_supplier/supplier.go b/core/supplier/ds_supplier/supplier.go index 2423c8f..21ce5b4 100644 --- a/core/supplier/ds_supplier/supplier.go +++ b/core/supplier/ds_supplier/supplier.go @@ -99,10 +99,9 @@ func (r *Supplier) doRequest(ctx context.Context, req *http.Request) ([]byte, er shared.CloseOrLog(ctx, resp.Body) zerolog.Ctx(ctx).Debug(). - Str("status", "ds.request.done"). Str("elapsed", time.Since(t0).String()). Str("path", req.URL.Path). - Send() + Msg("ds.request.done") if resp.StatusCode >= 400 { dsError := DSError{ diff --git a/core/supplier/kandinsky_supplier/method.go b/core/supplier/kandinsky_supplier/method.go index 41ca873..e64745a 100644 --- a/core/supplier/kandinsky_supplier/method.go +++ b/core/supplier/kandinsky_supplier/method.go @@ -61,7 +61,7 @@ func (r *Supplier) getModels(ctx context.Context) (int, error) { for _, v := range gjson.ParseBytes(respBytes).Array() { model := int(v.Get("id").Int()) - zerolog.Ctx(ctx).Info().Str("status", "kandinsky.model.got").Int("model_id", model).Send() + zerolog.Ctx(ctx).Info().Int("model_id", model).Msg("kandinsky.model.got") return model, nil } diff --git a/docker-compose-infra.yaml b/docker-compose-infra.yaml index a82d2f0..e19329a 100644 --- a/docker-compose-infra.yaml +++ b/docker-compose-infra.yaml @@ -2,7 +2,7 @@ version: '3.1' services: redis: - image: redis:7.2-alpine + image: redis:7.2.4-alpine restart: always command: redis-server --save 20 1 --loglevel warning volumes: @@ -21,17 +21,20 @@ services: cpus: '0.50' memory: 100M - mongodb: - image: mongo:7.0.9-jammy + postgres: + image: postgres:16.3-bookworm ports: - - '27017:27017' + - "5432:5432" environment: - MONGO_INITDB_DATABASE: db_main + POSTGRES_USER: "main" + POSTGRES_PASSWORD: "main" + POSTGRES_DB: "main" volumes: - - "./data/mongo:/data/db" - - './extra/mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro' + - ./data/pg:/var/lib/postgresql/data + labels: + - "autoheal=true" healthcheck: - test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet + test: ["CMD", "pg_isready", "-d", "main"] interval: 2m timeout: 20s retries: 3 @@ -40,23 +43,4 @@ services: resources: limits: cpus: '0.50' - memory: 500M - - clickhouse: - image: clickhouse/clickhouse-server:23.12 - ports: - - "9000:9000" - - "8123:8123" - volumes: - - "./data/ch:/var/lib/clickhouse" - healthcheck: - test: wget --no-verbose --tries=1 --spider http://localhost:8123/?query=SELECT%201 || exit 1 - interval: 2m - timeout: 20s - retries: 3 - start_period: 5s - deploy: - resources: - limits: - cpus: '0.50' - memory: 2048M + memory: 2000M \ No newline at end of file diff --git a/extra/mongo-init.js b/extra/mongo-init.js deleted file mode 100644 index 58b8302..0000000 --- a/extra/mongo-init.js +++ /dev/null @@ -1,12 +0,0 @@ -db.createCollection("users") -db.users.createIndex({ "tg_id": 1 }, { "unique": true }) -db.users.createIndex({ "tg_username": 1 }) -db.users.createIndex({ "created_at": 1 }) - -db.createCollection("messages") -db.messages.createIndex({ "tg_chat_id": 1, "tg_user_id": 1 }) -db.messages.createIndex({ "tg_chat_id": 1, "tg_id": 1 }, { "unique": true }) -db.messages.createIndex({ "created_at": 1 }) - -db.createCollection("members") -db.members.createIndex({ "tg_chat_id": 1, "tg_user_id": 1 }, { "unique": true }) diff --git a/go.mod b/go.mod index edc2bb1..2bdfe38 100644 --- a/go.mod +++ b/go.mod @@ -3,41 +3,45 @@ module github.com/teadove/fun_telegram go 1.22 require ( - github.com/ClickHouse/clickhouse-go/v2 v2.24.0 github.com/aaaton/golem/v4 v4.0.1 github.com/aaaton/golem/v4/dicts/ru v0.0.0-20221121100719-34023a0c192d github.com/bsm/redislock v0.9.4 github.com/caarlos0/env/v7 v7.1.0 github.com/celestix/gotgproto v1.0.0-beta18 github.com/deckarep/golang-set/v2 v2.6.0 - github.com/dlclark/regexp2 v1.11.0 + github.com/dlclark/regexp2 v1.11.2 github.com/glebarez/sqlite v1.11.0 github.com/go-co-op/gocron v1.37.0 + github.com/go-latex/latex v0.0.0-20240709081214-31cef3c7570e github.com/google/uuid v1.6.0 github.com/gotd/contrib v0.20.0 - github.com/gotd/td v0.102.0 + github.com/gotd/td v0.105.0 github.com/guregu/null/v5 v5.0.0 github.com/joho/godotenv v1.5.1 - github.com/kamva/mgm/v3 v3.5.0 + github.com/lib/pq v1.10.9 github.com/pkg/errors v0.9.1 - github.com/redis/go-redis/v9 v9.5.1 - github.com/rs/zerolog v1.32.0 + github.com/redis/go-redis/v9 v9.5.4 + github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.9.0 github.com/tidwall/gjson v1.17.1 github.com/xitongsys/parquet-go v1.6.2 github.com/xitongsys/parquet-go-source v0.0.0-20240122235623-d6294584ab18 - go.mongodb.org/mongo-driver v1.15.0 - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 - golang.org/x/text v0.15.0 + go.mongodb.org/mongo-driver v1.16.0 + golang.org/x/exp v0.0.0-20240707233637-46b078467d37 + golang.org/x/text v0.16.0 golang.org/x/time v0.5.0 + gorm.io/driver/postgres v1.5.9 + gorm.io/gorm v1.25.11 ) require ( + git.sr.ht/~sbinet/gg v0.5.0 // indirect github.com/AnimeKaizoku/cacher v1.0.1 // indirect github.com/ClickHouse/ch-go v0.61.5 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 // indirect github.com/apache/thrift v0.20.0 // indirect + github.com/campoy/embedmd v1.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -49,15 +53,18 @@ require ( github.com/go-faster/errors v0.7.1 // indirect github.com/go-faster/jx v1.1.0 // indirect github.com/go-faster/xor v1.0.0 // indirect - github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea // indirect github.com/go-pdf/fpdf v0.9.0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gotd/ige v0.2.2 // indirect github.com/gotd/neo v0.1.5 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.6.0 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/klauspost/compress v1.17.8 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/montanaflynn/stats v0.7.1 // indirect @@ -76,23 +83,22 @@ require ( github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect - go.opentelemetry.io/otel v1.26.0 // indirect - go.opentelemetry.io/otel/trace v1.26.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/image v0.14.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/image v0.18.0 // indirect + golang.org/x/net v0.27.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gorm.io/gorm v1.25.10 // indirect - modernc.org/libc v1.50.7 // indirect + modernc.org/libc v1.55.2 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.8.0 // indirect - modernc.org/sqlite v1.29.10 // indirect + modernc.org/sqlite v1.30.2 // indirect nhooyr.io/websocket v1.8.11 // indirect rsc.io/qr v0.2.0 // indirect ) diff --git a/go.sum b/go.sum index cb8830c..7944bfe 100644 --- a/go.sum +++ b/go.sum @@ -30,31 +30,46 @@ cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Ud cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.2.0/go.mod h1:xlogom/6gr8RJGBe7nT2eGsQYAFUbbv8dbC29qE3Xmw= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.6.1 h1:8rBq3zRjnHx8UtBvaOWqBB1xq9jH6/wltfQLlTMh2Fw= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/kms v1.1.0/go.mod h1:WdbppnCDMDpOvoYBMn1+gNmOeEoZYqAv+HeuKARGCXI= +cloud.google.com/go/kms v1.4.0 h1:iElbfoE61VeLhnZcGOltqL8HIly8Nhbe5t6JlH9GXjo= cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= cloud.google.com/go/monitoring v1.1.0/go.mod h1:L81pzz7HKn14QCMaCs6NTQkdBnE87TElyanS95vIcl4= +cloud.google.com/go/monitoring v1.4.0 h1:05+IuNMbh40hbxcqQ4SnynbwZbLG1Wc9dysIJxnfv7U= cloud.google.com/go/monitoring v1.4.0/go.mod h1:y6xnxfwI3hTFWOdkOaD7nfJVlwuC3/mS/5kvtT131p4= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.19.0 h1:WZy66ga6/tqmZiwv1jwKVgqV8FuEuAmPR5CEJHNVCZk= cloud.google.com/go/pubsub v1.19.0/go.mod h1:/O9kmSe9bb9KRnIAWkzmqhPjHo6LtzGOBYd/kr06XSs= +cloud.google.com/go/secretmanager v1.3.0 h1:43rHc04zmpiQeqtNKpO5la4bwF5aDhHACZqxQk6D/4c= cloud.google.com/go/secretmanager v1.3.0/go.mod h1:+oLTkouyiYiabAQNugCeTS3PAArGiMJuBqvJnJsyH+U= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= @@ -63,72 +78,127 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho= cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA= +cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= +cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/trace v1.0.0/go.mod h1:4iErSByzxkyHWzzlAj63/Gmjz0NH1ASqhJguHpGcr6A= +cloud.google.com/go/trace v1.2.0 h1:oIaB4KahkIUOpLSAAjEJ8y2desbjY/x/RfP4O3KAtTI= cloud.google.com/go/trace v1.2.0/go.mod h1:Wc8y/uYyOhPy12KEnXG9XGrvfMz5F5SrYecQlbW1rwM= +contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9 h1:yxE46rQA0QaqPGqN2UnwXvgCrRqtjR1CsGSWVTRjvv4= contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= +contrib.go.opencensus.io/exporter/stackdriver v0.13.10 h1:a9+GZPUe+ONKUwULjlEOucMMG0qfSCCenlji0Nhqbys= contrib.go.opencensus.io/exporter/stackdriver v0.13.10/go.mod h1:I5htMbyta491eUxufwwZPQdcKvvgzMB4O9ni41YnIM8= +contrib.go.opencensus.io/integrations/ocsql v0.1.7 h1:G3k7C0/W44zcqkpRSFyjU9f6HZkbwIrL//qqnlqWZ60= contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +gioui.org v0.0.0-20210822154628-43a7030f6e0b h1:oNgGS0eKioZP9toKnQaxakGXfxg9ESShA4g9FeJpDOM= +gioui.org v0.0.0-20210822154628-43a7030f6e0b/go.mod h1:jmZ349gZNGWyc5FIv/VWLBQ32Ki/FOvTgEz64kh9lnk= +gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc= +gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= +gioui.org/shader v1.0.0 h1:nrcMavMhFE6Z8E25+7bI9nUZ2hFRP7/npSf6j53SlD0= +gioui.org/shader v1.0.0/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= +git.sr.ht/~sbinet/cmpimg v0.1.0 h1:E0zPRk2muWuCqSKSVZIWsgtU9pjsw3eKHi8VmQeScxo= +git.sr.ht/~sbinet/cmpimg v0.1.0/go.mod h1:FU12psLbF4TfNXkKH2ZZQ29crIqoiqTZmeQ7dkp/pxE= +git.sr.ht/~sbinet/gg v0.5.0 h1:6V43j30HM623V329xA9Ntq+WJrMjDxRjuAB1LFWF5m8= +git.sr.ht/~sbinet/gg v0.5.0/go.mod h1:G2C0eRESqlKhS7ErsNey6HHrqU1PwsnCQlekFi9Q2Oo= github.com/AnimeKaizoku/cacher v1.0.1 h1:rDjeDphztR4h234mnUxlOQWyYAB63WdzJB9zBg9HVPg= github.com/AnimeKaizoku/cacher v1.0.1/go.mod h1:jw0de/b0K6W7Y3T9rHCMGVKUf6oG7hENNcssxYcZTCc= github.com/Azure/azure-amqp-common-go/v3 v3.2.1/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI= +github.com/Azure/azure-amqp-common-go/v3 v3.2.2 h1:CJpxNAGxP7UBhDusRUoaOn0uOorQyAYhQYLnNgkRhlY= github.com/Azure/azure-amqp-common-go/v3 v3.2.2/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI= +github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-sdk-for-go v51.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v59.3.0+incompatible h1:dPIm0BO4jsMXFcCI/sLTPkBtE7mk8WMuRHA0JeWhlcQ= github.com/Azure/azure-sdk-for-go v59.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0 h1:lMW1lD/17LUA5z1XTURo7LcVG2ICBPlyMHjIUrcFZNQ= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0/go.mod h1:ceIuwmxDWptoW3eCqSXlnPsZFKh4X+R38dWPv7GS9Vs= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0 h1:Ma67P/GGprNwsslzEH6+Kb8nybI8jpDTm4Wmzu2ReK8= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0 h1:nVocQV40OQne5613EeLayJiRAJuKlBGy+m22qWG+WRg= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0/go.mod h1:7QJP7dr2wznCMeqIrhMgWGf7XpAQnVrJqDm9nvV3Cu4= +github.com/Azure/azure-service-bus-go v0.11.5 h1:EVMicXGNrSX+rHRCBgm/TRQ4VUZ1m3yAYM/AB2R/SOs= github.com/Azure/azure-service-bus-go v0.11.5/go.mod h1:MI6ge2CuQWBVq+ly456MY7XqNLJip5LO1iSFodbNLbU= +github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= github.com/Azure/go-amqp v0.16.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg= +github.com/Azure/go-amqp v0.16.4 h1:/1oIXrq5zwXLHaoYDliJyiFjJSpJZMWGgtMX9e0/Z30= github.com/Azure/go-amqp v0.16.4/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest v0.11.22 h1:bXiQwDjrRmBQOE67bwlvUKAC1EU1yZTPQ38c+bstZws= github.com/Azure/go-autorest/autorest v0.11.22/go.mod h1:BAWYUWGPEtKPzjVkp0Q6an0MJcJDsoh5Z1BFAEFs4Xs= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/adal v0.9.17 h1:esOPl2dhcz9P3jqBSJ8tPGEj2EqzPPT6zfyuloiogKY= github.com/Azure/go-autorest/autorest/adal v0.9.17/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.9 h1:Y2CgdzitFDsdMwYMzf9LIZWrrTFysqbRc7b94XVVJ78= github.com/Azure/go-autorest/autorest/azure/auth v0.5.9/go.mod h1:hg3/1yw0Bq87O3KvvnJoAh34/0zbP7SFizX/qN5JvjU= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4= github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg= github.com/ClickHouse/clickhouse-go/v2 v2.24.0 h1:L/n/pVVpk95KtkHOiKuSnO7cu2ckeW4gICbbOh5qs74= github.com/ClickHouse/clickhouse-go/v2 v2.24.0/go.mod h1:iDTViXk2Fgvf1jn2dbJd1ys+fBkdD1UMRnXlwmhijhQ= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0 h1:YNu23BtH0PKF+fg3ykSorCp6jSTjcEtfnYLzbmcjVRA= github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0/go.mod h1:spvB9eLJH9dutlbPSRmHvSXXHOwGRyeXh1jVdquA2G8= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI= +github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY= github.com/aaaton/golem/v4 v4.0.0/go.mod h1:OfK/S5v9Exsx1yO21WorREuIVV+Y5K2hygP0A9oJCCI= github.com/aaaton/golem/v4 v4.0.1 h1:jvnnTmzdfZC8cUGIo6obIcnmB3stTaf5Uw64OMx3C84= github.com/aaaton/golem/v4 v4.0.1/go.mod h1:OfK/S5v9Exsx1yO21WorREuIVV+Y5K2hygP0A9oJCCI= github.com/aaaton/golem/v4/dicts/ru v0.0.0-20221121100719-34023a0c192d h1:ifSDxtF0ndc+uDcWjJWs+CfzdOvjtO2/xaMd1YJo8xY= github.com/aaaton/golem/v4/dicts/ru v0.0.0-20221121100719-34023a0c192d/go.mod h1:n14MqOgbLBidXRIvLw9H3/vFyE4+PcjVxYOu05f55R4= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af h1:wVe6/Ea46ZMeNkQjjBW6xcqyQA/j5e0D6GytH95g0gQ= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= +github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/arrow v0.0.0-20200730104253-651201b0f516/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 h1:q4dksr6ICHXqG5hm0ZW5IHyeEJXoIJSOZeBLmWPNeIQ= @@ -140,51 +210,85 @@ github.com/apache/thrift v0.20.0/go.mod h1:hOk1BQqcp2OLzGsyVXdfMk7YFlMxK3aoEVhjD github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.30.19/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0= github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= +github.com/aws/aws-sdk-go-v2 v1.23.0 h1:PiHAzmiQQr6JULBUdvR8fKlA+UPKLT/8KbiqpFBWiAo= github.com/aws/aws-sdk-go-v2 v1.23.0/go.mod h1:i1XDttT4rnf6vxc9AuskLc6s7XBee8rlLilKlc03uAA= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1/go.mod h1:n8Bs1ElDD2wJ9kCRTczA83gYbBmjSwZp3umc6zF4EeM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.1 h1:ZY3108YtBNq96jNZTICHxN1gSBSbnvIdYwwqnvCV4Mc= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.1/go.mod h1:t8PYl/6LzdAqsU4/9tz28V/kU+asFePvpOMkdul0gEQ= github.com/aws/aws-sdk-go-v2/config v1.15.3/go.mod h1:9YL3v07Xc/ohTsxFXzan9ZpFpdTOFl4X65BAKYaz8jg= +github.com/aws/aws-sdk-go-v2/config v1.25.3 h1:E4m9LbwJOoncDNt3e9MPLbz/saxWcGUlZVBydydD6+8= github.com/aws/aws-sdk-go-v2/config v1.25.3/go.mod h1:tAByZy03nH5jcq0vZmkcVoo6tRzRHEwSFx3QW4NmDw8= github.com/aws/aws-sdk-go-v2/credentials v1.11.2/go.mod h1:j8YsY9TXTm31k4eFhspiQicfXPLZ0gYXA50i4gxPE8g= +github.com/aws/aws-sdk-go-v2/credentials v1.16.2 h1:0sdZ5cwfOAipTzZ7eOL0gw4LAhk/RZnTa16cDqIt8tg= github.com/aws/aws-sdk-go-v2/credentials v1.16.2/go.mod h1:sDdvGhXrSVT5yzBDR7qXz+rhbpiMpUYfF3vJ01QSdrc= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3/go.mod h1:uk1vhHHERfSVCUnqSqz8O48LBYDSC+k6brng09jcMOk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.4 h1:9wKDWEjwSnXZre0/O3+ZwbBl1SmlgWYBbrTV10X/H1s= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.4/go.mod h1:t4i+yGHMCcUNIX1x7YVYa6bH/Do7civ5I6cG/6PMfyA= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.3/go.mod h1:0dHuD2HZZSiwfJSy1FO5bX1hQ1TxVV1QXXjpn3XUE44= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.14.0 h1:1KdubQbnw76M0Sr8480q6OXBlymBVqpkK+RuCqJz+nQ= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.14.0/go.mod h1:UcgIwJ9KHquYxs6Q5skC9qXjhYMK+JASDYcXQ4X7JZE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.3 h1:DUwbD79T8gyQ23qVXFUthjzVMTviSHi3y4z58KvghhM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.3/go.mod h1:7sGSz1JCKHWWBHq98m6sMtWQikmYPpxjqOydDemiVoM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.3 h1:AplLJCtIaUZDCbr6+gLYdsYNxne4iuaboJhVt9d+WXI= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.3/go.mod h1:ify42Rb7nKeDDPkFjKn7q1bPscVPu/+gmHH8d2c+anU= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10/go.mod h1:8DcYQcz0+ZJaSxANlHIsbbi6S+zMwjwdDqwW3r9AzaE= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.3 h1:lMwCXiWJlrtZot0NJTjbC8G9zl+V3i68gBTBBvDeEXA= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.3/go.mod h1:5yzAuE9i2RkVAttBl8yxZgQr5OCq4D5yDnG7j9x2L0U= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1/go.mod h1:GeUru+8VzrTXV/83XyMJ80KpH8xO89VPoUileyNQ+tc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1 h1:rpkF4n0CyFcrJUG/rNNohoTmhtWlFTRI4BsZOh9PvLs= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1/go.mod h1:l9ymW25HOqymeU2m1gbUQ3rUIsTwKs8gYHXkqDQUhiI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.3/go.mod h1:Seb8KNmD6kVTjwRjVEgOT5hPin6sq+v4C2ycJQDwuH8= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.3 h1:xbwRyCy7kXrOj89iIKLB6NfE2WCpP9HoKyk8dMDvnIQ= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.3/go.mod h1:R+/S1O4TYpcktbVwddeOYg+uwUfLhADP2S/x4QwsCTM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3/go.mod h1:wlY6SVjuwvh3TVRpTqdy4I1JpBFLX4UGeKZdWntaocw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.3 h1:kJOolE8xBAD13xTCgOakByZkyP4D/owNmvEiioeUNAg= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.3/go.mod h1:Owv1I59vaghv1Ax8zz8ELY8DN7/Y0rGS+WWAmjgi950= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.3/go.mod h1:Bm/v2IaN6rZ+Op7zX+bOUMdL4fsrYZiD0dsjLhNKwZc= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.3 h1:KV0z2RDc7euMtg8aUT1czv5p29zcLlXALNFsd3jkkEc= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.3/go.mod h1:KZgs2ny8HsxRIRbDwgvJcHHBZPOzQr/+NtGwnP+w2ec= +github.com/aws/aws-sdk-go-v2/service/kms v1.16.3 h1:nUP29LA4GZZPihNSo5ZcF4Rl73u+bN5IBRnrQA0jFK4= github.com/aws/aws-sdk-go-v2/service/kms v1.16.3/go.mod h1:QuiHPBqlOFCi4LqdSskYYAWpQlx3PKmohy+rE2F+o5g= github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3/go.mod h1:g1qvDuRsJY+XghsV6zg00Z4KJ7DtFFCx8fJD2a491Ak= +github.com/aws/aws-sdk-go-v2/service/s3 v1.43.0 h1:cwTuq73Tv6jtNJIMgTDKsih5O2YsVrKGpg20H98tbmo= github.com/aws/aws-sdk-go-v2/service/s3 v1.43.0/go.mod h1:NXRKkiRF+erX2hnybnVU660cYT5/KChRD4iUgJ97cI8= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.4 h1:EmIEXOjAdXtxa2OGM1VAajZV/i06Q8qd4kBpJd9/p1k= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.4/go.mod h1:PJc8s+lxyU8rrre0/4a0pn2wgwiDvOEzoOjcJUBr67o= +github.com/aws/aws-sdk-go-v2/service/sns v1.17.4 h1:7TdmoJJBwLFyakXjfrGztejwY5Ie1JEto7YFfznCmAw= github.com/aws/aws-sdk-go-v2/service/sns v1.17.4/go.mod h1:kElt+uCcXxcqFyc+bQqZPFD9DME/eC6oHBXvFzQ9Bcw= +github.com/aws/aws-sdk-go-v2/service/sqs v1.18.3 h1:uHjK81fESbGy2Y9lspub1+C6VN5W2UXTDo2A/Pm4G0U= github.com/aws/aws-sdk-go-v2/service/sqs v1.18.3/go.mod h1:skmQo0UPvsjsuYYSYMVmrPc1HWCbHUJyrCEp+ZaLzqM= +github.com/aws/aws-sdk-go-v2/service/ssm v1.24.1 h1:zc1YLcknvxdW/i1MuJKmEnFB2TNkOfguuQaGRvJXPng= github.com/aws/aws-sdk-go-v2/service/ssm v1.24.1/go.mod h1:NR/xoKjdbRJ+qx0pMR4mI+N/H1I1ynHwXnO6FowXJc0= github.com/aws/aws-sdk-go-v2/service/sso v1.11.3/go.mod h1:7UQ/e69kU7LDPtY40OyoHYgRmgfGM4mgsLYtcObdveU= +github.com/aws/aws-sdk-go-v2/service/sso v1.17.2 h1:V47N5eKgVZoRSvx2+RQ0EpAEit/pqOhqeSQFiS4OFEQ= github.com/aws/aws-sdk-go-v2/service/sso v1.17.2/go.mod h1:/pE21vno3q1h4bbhUOEi+6Zu/aT26UK2WKkDXd+TssQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.0 h1:/XiEU7VIFcVWRDQLabyrSjBoKIm8UkYgsvWDuFW8Img= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.0/go.mod h1:dWqm5G767qwKPuayKfzm4rjzFmVjiBFbOJrpSPnAMDs= github.com/aws/aws-sdk-go-v2/service/sts v1.16.3/go.mod h1:bfBj0iVmsUyUg4weDB4NxktD9rDGeKSVWnjTnwbx9b8= +github.com/aws/aws-sdk-go-v2/service/sts v1.25.3 h1:M2w4kiMGJCCM6Ljmmx/l6mmpfa3gPJVpBencfnsgvqs= github.com/aws/aws-sdk-go-v2/service/sts v1.25.3/go.mod h1:4EqRHDCKP78hq3zOnmFXu5k0j4bXbRFfCh/zQ6KnEfQ= github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= +github.com/aws/smithy-go v1.17.0 h1:wWJD7LX6PBV6etBUwO0zElG0nWN9rUhp0WdYeHSHAaI= github.com/aws/smithy-go v1.17.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= +github.com/beevik/ntp v1.3.1 h1:Y/srlT8L1yQr58kyPWFPZIxRL8ttx2SRIpVYJqZIlAM= +github.com/beevik/ntp v1.3.1/go.mod h1:fT6PylBq86Tsq23ZMEe47b7QQrZfYBFPnpzt0a9kJxw= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bobg/gcsobj v0.1.2 h1:1xA5ybDt4HA3Z1BDTS6DdKkUUHOf0MIXT9hstZFNsD4= github.com/bobg/gcsobj v0.1.2/go.mod h1:vS49EQ1A1Ib8FgrL58C8xXYZyOCR2TgzAdopy6/ipa8= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -193,50 +297,85 @@ github.com/bsm/redislock v0.9.4 h1:X/Wse1DPpiQgHbVYRE9zv6m070UcKoOGekgvpNhiSvw= github.com/bsm/redislock v0.9.4/go.mod h1:Epf7AJLiSFwLCiZcfi6pWFO/8eAYrYpQXFxEDPoDeAk= github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg= github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E= +github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY= +github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= github.com/celestix/gotgproto v1.0.0-beta18 h1:7884H/il+mzNreOQ4SqoMa4S5njt3UmGPKZTxPu38fU= github.com/celestix/gotgproto v1.0.0-beta18/go.mod h1:osZOlN5irPByA0+3IPsZOH+Ibs0tOMSKmIdgGYEBRgE= +github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= +github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/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/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 h1:zH8ljVhhq7yC0MIeUL/IviMtY8hx2mK8cN9wEYb8ggw= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/colinmarc/hdfs/v2 v2.1.1 h1:x0hw/m+o3UE20Scso/KCkvYNc9Di39TBlCfGMkJ1/a0= github.com/colinmarc/hdfs/v2 v2.1.1/go.mod h1:M3x+k8UKKmxtFu++uAZ0OtDU8jR3jnaZIAc6yK4Ue0c= 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 h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= 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= github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA= github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= +github.com/devigned/tab v0.1.1 h1:3mD6Kb1mUOYeLpJvTVSDwSg5ZsfSxfvxGRTxRsJsITA= github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.2 h1:/u628IuisSTwri5/UKloiIsH8+qF2Pu7xEQX+yIKg68= +github.com/dlclark/regexp2 v1.11.2/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dmarkham/enumer v1.5.9 h1:NM/1ma/AUNieHZg74w67GkHFBNB15muOt3sj486QVZk= +github.com/dmarkham/enumer v1.5.9/go.mod h1:e4VILe2b1nYK3JKJpRmNdl5xbDQvELc6tQ8b+GsGk6E= github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -248,17 +387,29 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021 h1:fP+fF0up6oPY49OrjPrhIJ8yQfdIM85NXMLkMg1EXVs= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b h1:M0/hjawi9ur15zpqL/h66ga87jlYA7iAuZ4HC6ak08k= +github.com/gen2brain/dlgs v0.0.0-20211108104213-bade24837f0b/go.mod h1:/eFcjDXaU2THSOOqLxOPETIbHETnamk8FA/hMjhg/gU= +github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= +github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.7.3 h1:aMBzLJ/GMEYmv1UWs2FFTcPISLrQH2mRgL9Glz8xows= github.com/gin-gonic/gin v1.7.3/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= @@ -276,48 +427,91 @@ github.com/go-faster/xor v0.3.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7U github.com/go-faster/xor v1.0.0 h1:2o8vTOgErSGHP3/7XwA5ib1FTtUsNtwCoLLBjl31X38= github.com/go-faster/xor v1.0.0/go.mod h1:x5CaDY9UKErKzqfRfFZdfu+OSTfoZny3w5Ak7UxcipQ= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/dejavu v0.3.4 h1:Qqyx9IOs5CQFxyWTdvddeWzrX0VNwUAvbmAzL0fpjbc= +github.com/go-fonts/dejavu v0.3.4/go.mod h1:D1z0DglIz+lmpeNYMYlxW4r22IhcdOYnt+R3PShU/Kg= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/latin-modern v0.3.3 h1:g2xNgI8yzdNzIVm+qvbMryB6yGPe0pSMss8QT3QwlJ0= +github.com/go-fonts/latin-modern v0.3.3/go.mod h1:tHaiWDGze4EPB0Go4cLT5M3QzRY3peya09Z/8KSCrpY= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.3.3 h1:tM/T2vEOhjia6v5krQu8SDDegfH1SfXVRUNNKpq0Usk= +github.com/go-fonts/liberation v0.3.3/go.mod h1:eUAzNRuJnpSnd1sm2EyloQfSOT79pdw7X7++Ri+3MCU= github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +github.com/go-fonts/stix v0.2.2 h1:v9krocr13J1llaOHLEol1eaHsv8S43UuFX/1bFgEJJ4= +github.com/go-fonts/stix v0.2.2/go.mod h1:SUxggC9dxd/Q+rb5PkJuvfvTbOPtNc2Qaua00fIp9iU= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= +github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea h1:DfZQkvEbdmOe+JK2TMtBM+0I9GSdzE2y/L1/AmD8xKc= github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea/go.mod h1:Y7Vld91/HRbTBm7JwoI7HejdDB0u+e9AUBO9MB7yuZk= +github.com/go-latex/latex v0.0.0-20240709081214-31cef3c7570e h1:xcdj0LWnMSIU1j8+jIeJyfvk6SjgJedFQssSqFthJ2E= +github.com/go-latex/latex v0.0.0-20240709081214-31cef3c7570e/go.mod h1:J4SAGzkcl+28QWi7yz72tyC/4aGnppOvya+AEv4TaAQ= +github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/inflect v0.21.0 h1:FoBjBTQEcbg2cJUWX6uwL9OyIW8eqc9k4KhN4lfbeYk= +github.com/go-openapi/inflect v0.21.0/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw= github.com/go-pdf/fpdf v0.9.0 h1:PPvSaUuo1iMi9KkaAn90NuKi+P4gwMedWPHhj8YlJQw= github.com/go-pdf/fpdf v0.9.0/go.mod h1:oO8N111TkmKb9D7VvWGLvLJlaZUQVPM+6V42pp3iV4Y= +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.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +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/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4= github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -327,6 +521,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -346,12 +541,15 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v2.0.0+incompatible h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI= @@ -372,14 +570,19 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE= github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk= +github.com/google/go-replayers/httpreplay v1.1.1 h1:H91sIMlt1NZzN7R+/ASswyouLJfW0WLW7fhyUFvDEkY= github.com/google/go-replayers/httpreplay v1.1.1/go.mod h1:gN9GeLIs7l6NUoVaSSnv2RiqK1NiwAmD0MrKeC9IIks= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible h1:xmapqc1AyLoB+ddYT6r04bD9lIjlOqGaREovi0SzFaE= github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -399,7 +602,11 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -408,33 +615,81 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720 h1:zC34cGQu69FG7qzJ3WiKW244WfhDC3xxYMeNOX2gtUQ= +github.com/googleapis/google-cloud-go-testing v0.0.0-20210719221736-1c9a4c676720/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotd/contrib v0.20.0 h1:1Wc4+HMQiIKYQuGHVwVksIx152HFTP6B5n88dDe0ZYw= github.com/gotd/contrib v0.20.0/go.mod h1:P6o8W4niqhDPHLA0U+SA/L7l3BQHYLULpeHfRSePn9o= +github.com/gotd/getdoc v0.42.0 h1:sfOZVc1mTVOW09Wv33cUAME3KCzYYtMmAaOb2mVp9XQ= +github.com/gotd/getdoc v0.42.0/go.mod h1:MazeR5T7jiXeISn5/j8jyd/w+fIerlNDsE9IASlGgYM= github.com/gotd/ige v0.2.2 h1:XQ9dJZwBfDnOGSTxKXBGP4gMud3Qku2ekScRjDWWfEk= github.com/gotd/ige v0.2.2/go.mod h1:tuCRb+Y5Y3eNTo3ypIfNpQ4MFjrnONiL2jN2AKZXmb0= github.com/gotd/neo v0.1.5 h1:oj0iQfMbGClP8xI59x7fE/uHoTJD7NZH9oV1WNuPukQ= github.com/gotd/neo v0.1.5/go.mod h1:9A2a4bn9zL6FADufBdt7tZt+WMhvZoc5gWXihOPoiBQ= github.com/gotd/td v0.102.0 h1:V6zNba9FV21YiBm1t42ak5jyBFSQzY8+8fwZpOT5lGM= github.com/gotd/td v0.102.0/go.mod h1:k9JQ7ktxOs4yTpE7X2ZvNtAl+blARhz1ak+Aw0VUHiQ= +github.com/gotd/td v0.105.0 h1:FjU9pgmL5Qt10+cosPCz4agvQT/hMBz6QMi1fFH7ekY= +github.com/gotd/td v0.105.0/go.mod h1:aVe5/LP/nNIyAqaW3CwB0Ckum+MkcfvazwMOLHV0bqQ= +github.com/gotd/tl v0.4.0 h1:8k2z0drujiPyhpLDa9PRm/yU1Gwlfn3iUzeInPiXwMA= +github.com/gotd/tl v0.4.0/go.mod h1:CMIcjPWFS4qxxJ+1Ce7U/ilbtPrkoVo/t8uhN5Y/D7c= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/guregu/null/v5 v5.0.0 h1:PRxjqyOekS11W+w/7Vfz6jgJE/BCwELWtgvOJzddimw= github.com/guregu/null/v5 v5.0.0/go.mod h1:SjupzNy+sCPtwQTKWhUCqjhVCO69hpsl2QsZrWHjlwU= +github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc= github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= +github.com/hanwen/go-fuse/v2 v2.1.0 h1:+32ffteETaLYClUj0a3aHjZ1hOPxxaNEHiZiujuDaek= github.com/hanwen/go-fuse/v2 v2.1.0/go.mod h1:oRyA5eK+pvJyv5otpO/DgccS8y/RvYMaO00GgRLGryc= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ= +github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-uuid v0.0.0-20180228145832-27454136f036 h1:d8T6WIONl4rMCPcQ/eY3uSz3+e4/GaoflKjXrWMex1U= github.com/hashicorp/go-uuid v0.0.0-20180228145832-27454136f036/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/vault/api v1.12.2 h1:7YkCTE5Ni90TcmYHDBExdt4WGJxhpzaHqR6uGbQb/rE= +github.com/hashicorp/vault/api v1.12.2/go.mod h1:LSGf1NGT1BnvFFnKVtnvcaLBM2Lz+gJdpL6HUYed8KE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/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= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= @@ -442,12 +697,17 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.11.0 h1:HiHArx4yFbwl91X3qqIHtUFoiIfLNJXCQRsnzkiwwaQ= github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 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= @@ -455,22 +715,37 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns= github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 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.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38= github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 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.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w= github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.2.1 h1:gI8os0wpRXFd4FiAY2dWiqRK037tjj3t7rKFeO4X5iw= github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jcmturner/gofork v0.0.0-20180107083740-2aebee971930 h1:v4CYlQ+HeysPHsr2QFiEO60gKqnvn1xwvuKhhAhuEkk= github.com/jcmturner/gofork v0.0.0-20180107083740-2aebee971930/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= @@ -478,20 +753,31 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/k0kubun/pp/v3 v3.2.0 h1:h33hNTZ9nVFNP3u2Fsgz8JXiF5JINoZfFq4SvKJwNcs= +github.com/k0kubun/pp/v3 v3.2.0/go.mod h1:ODtJQbQcIRfAD3N+theGCV1m/CBxweERz2dapdz1EwA= github.com/kamva/mgm/v3 v3.5.0 h1:/2mNshpqwAC9spdzJZ0VR/UZ/SY/PsNTrMjT111KQjM= github.com/kamva/mgm/v3 v3.5.0/go.mod h1:F4J1hZnXQMkqL3DZgR7Z7BOuiTqQG/JTic3YzliG4jk= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -501,33 +787,46 @@ github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 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/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 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 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/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +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 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 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= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -536,16 +835,29 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +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= github.com/minio/minio-go/v7 v7.0.34/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw= +github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0= +github.com/minio/minio-go/v7 v7.0.69/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +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/modocache/gover v0.0.0-20171022184752-b58185e213c5 h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= @@ -554,32 +866,55 @@ github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8 github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/ncw/swift v1.0.52 h1:ACF3JufDGgeKp/9mrDgQlEgS8kRYC4XKcuzj/8EJjQU= github.com/ncw/swift v1.0.52/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/pascaldekloe/name v1.0.1 h1:9lnXOHeqeHHnWLbKfH6X98+4+ETVqFqxN09UXSjcMb0= +github.com/pascaldekloe/name v1.0.1/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM= github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= +github.com/paulmach/protoscan v0.2.1 h1:rM0FpcTjUMvPUNk2BhPJrreDKetq43ChnL+x1sRg8O8= github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= +github.com/pborman/getopt v0.0.0-20180729010549-6fdd0a2c7117 h1:7822vZ646Atgxkp3tqrSufChvAAYgIy+iFEGpQntwlI= github.com/pborman/getopt v0.0.0-20180729010549-6fdd0a2c7117/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13 h1:o61duiW8M9sMlkVXWlvP92sZJtGKENvW3VExs6dZukQ= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= 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= +github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= +github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/redis/go-redis/v9 v9.5.4 h1:vOFYDKKVgrI5u++QvnMT7DksSMYg7Aw/Np4vLJLKLwY= +github.com/redis/go-redis/v9 v9.5.4/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= github.com/rogpeppe/fastuuid v1.2.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= @@ -588,22 +923,34 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= @@ -635,7 +982,9 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -662,12 +1011,18 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zenazn/goji v0.9.0 h1:RSQQAbXGArQ0dIDEq+PI6WqN6if+5KHu6x2Cx/GXLTQ= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4= +go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -676,10 +1031,21 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -699,6 +1065,9 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0= +go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= @@ -706,6 +1075,7 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +gocloud.dev v0.26.0 h1:4rM/SVL0lLs+rhC0Gmc+gt/82DBpb7nbpIZKXXnfMXg= gocloud.dev v0.26.0/go.mod h1:mkUgejbnbLotorqDyvedJO20XcZNTynmSeVSQS9btVg= golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -731,6 +1101,8 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -747,6 +1119,10 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37 h1:SOSg7+sueresE4IbmmGM60GmlIys+zNX63d6/J4CMtU= +golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -758,6 +1134,8 @@ golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+o golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= +golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -769,8 +1147,10 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= @@ -786,6 +1166,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -841,6 +1223,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= 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= @@ -861,6 +1245,8 @@ golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= 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= @@ -959,12 +1345,16 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 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= @@ -980,6 +1370,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 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= @@ -1057,6 +1449,8 @@ 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/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 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= @@ -1069,8 +1463,10 @@ gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJ gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.9.3 h1:DnoIG+QAMaF5NvxnGe/oKsgKcAc6PcUyl8q0VetfQ8s= gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0 h1:3sEo36Uopv1/SA/dMFFaxXoL5XyikJ9Sf2Vll/k6+2E= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1116,12 +1512,15 @@ google.golang.org/api v0.69.0/go.mod h1:boanBiw+h5c3s+tBPgEzLDRHfFLWV0qXxRHz3ws7 google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY= +google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1209,6 +1608,12 @@ google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= +google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1238,6 +1643,9 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1253,46 +1661,79 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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-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 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec h1:RlWgLqCMMIYYEVcAR5MDsuHlVkaIPDAF+5Dehzg8L5A= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw= gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= +gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM= gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= +gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI= gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= +gopkg.in/jcmturner/gokrb5.v7 v7.3.0 h1:0709Jtq/6QXEuWRfAm260XqlpcwL1vxtO1tUE2qK8Z4= gopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= +gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU= gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= +gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E= +gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE= gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= +gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= +lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= +modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= modernc.org/cc/v4 v4.21.2 h1:dycHFB/jDc3IyacKipCNSDrjIC0Lm1hyoWOZTRR20Lk= modernc.org/cc/v4 v4.21.2/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= +modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= +modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= modernc.org/ccgo/v4 v4.17.7 h1:+MG+Np7uYtsuPvtoH3KtZ1+pqNiJAOqqqVIxggE1iIo= modernc.org/ccgo/v4 v4.17.7/go.mod h1:x87xuLLXuJv3Nn5ULTUqJn/HsTMMMiT1Eavo6rz1NiY= +modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= +modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= modernc.org/libc v1.50.7 h1:25+61e/ZI1e53ynk8dvS/BvWie3lIJPR1KVlTdGkkCg= modernc.org/libc v1.50.7/go.mod h1:8lr2m1THY5Z3ikGyUc3JhLEQg1oaIBz/AQixw8/eksQ= +modernc.org/libc v1.55.2 h1:UN5eoBYrKp1b+gPYx8nZj5H7uxeybvyoQJfvcg+Bqjc= +modernc.org/libc v1.55.2/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= @@ -1303,6 +1744,8 @@ modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= modernc.org/sqlite v1.29.10 h1:3u93dz83myFnMilBGCOLbr+HjklS6+5rJLx4q86RDAg= modernc.org/sqlite v1.29.10/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA= +modernc.org/sqlite v1.30.2 h1:IPVVkhLu5mMVnS1dQgh3h0SAACRWcVk7aoLP9Us3UCk= +modernc.org/sqlite v1.30.2/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= @@ -1310,9 +1753,13 @@ modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= +rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= +rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=