From 8577deea38283d02df09f529161f696ad823952b Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 20 May 2024 03:17:01 +0300 Subject: [PATCH] feat: add params command for discord + updating commands --- pkg/app.go | 1 + pkg/reporters/discord/discord.go | 71 ++++++++++++++++--- pkg/reporters/discord/list_mutes.go | 3 +- .../{proposals.go => list_proposals.go} | 0 pkg/reporters/discord/params.go | 32 +++++++++ pkg/utils/utils.go | 53 ++++++++++++++ pkg/utils/utils_test.go | 63 ++++++++++++++++ templates/discord/help.html | 7 +- templates/discord/params.html | 7 ++ templates/telegram/help.html | 4 +- 10 files changed, 225 insertions(+), 16 deletions(-) rename pkg/reporters/discord/{proposals.go => list_proposals.go} (100%) create mode 100644 pkg/reporters/discord/params.go create mode 100644 templates/discord/params.html diff --git a/pkg/app.go b/pkg/app.go index c78ad13..6858617 100644 --- a/pkg/app.go +++ b/pkg/app.go @@ -80,6 +80,7 @@ func NewApp(configPath string, filesystem fs.FS, version string) *App { log, stateManager, mutesManager, + dataManager, stateGenerator, timeZone, tracer, diff --git a/pkg/reporters/discord/discord.go b/pkg/reporters/discord/discord.go index d4abbab..c515915 100644 --- a/pkg/reporters/discord/discord.go +++ b/pkg/reporters/discord/discord.go @@ -2,6 +2,7 @@ package discord import ( "context" + "main/pkg/data" mutes "main/pkg/mutes" "main/pkg/report/entry" statePkg "main/pkg/state" @@ -30,6 +31,7 @@ type Reporter struct { Config *types.Config Manager *statePkg.Manager MutesManager *mutes.Manager + DataManager *data.Manager TemplatesManager templatesPkg.Manager Commands map[string]*Command Tracer trace.Tracer @@ -42,6 +44,7 @@ func NewReporter( logger *zerolog.Logger, manager *statePkg.Manager, mutesManager *mutes.Manager, + dataManager *data.Manager, stateGenerator *statePkg.Generator, timezone *time.Location, tracer trace.Tracer, @@ -54,6 +57,7 @@ func NewReporter( Logger: logger.With().Str("component", "discord_reporter").Logger(), Manager: manager, MutesManager: mutesManager, + DataManager: dataManager, StateGenerator: stateGenerator, TemplatesManager: templatesPkg.NewDiscordTemplatesManager(logger, timezone), Commands: make(map[string]*Command, 0), @@ -91,6 +95,7 @@ func (reporter *Reporter) Init() error { "proposals_mute": reporter.GetAddMuteCommand(), "proposals_unmute": reporter.GetDeleteMuteCommand(), "proposals_mutes": reporter.GetMutesCommand(), + "params": reporter.GetParamsCommand(), } go reporter.InitCommands() @@ -116,8 +121,32 @@ func (reporter *Reporter) InitCommands() { return } - for _, command := range registeredCommands { - wg.Add(1) + desiredCommands := utils.Map( + utils.MapToArray(reporter.Commands), + func(c *Command) *discordgo.ApplicationCommand { return c.Info }, + ) + + commandsToAdd := utils.Subtract(desiredCommands, registeredCommands, func(v *discordgo.ApplicationCommand) string { + return v.Name + }) + + commandsToDelete := utils.Subtract(registeredCommands, desiredCommands, func(v *discordgo.ApplicationCommand) string { + return v.Name + }) + + commandsToUpdate := utils.Union(registeredCommands, desiredCommands, func(v *discordgo.ApplicationCommand) string { + return v.Name + }) + + reporter.Logger.Info(). + Int("commands_to_add", len(commandsToAdd)). + Int("commands_to_delete", len(commandsToDelete)). + Int("commands_to_update", len(commandsToUpdate)). + Msg("Updating Discord slash commands") + + wg.Add(len(commandsToAdd) + len(commandsToDelete) + len(commandsToUpdate)) + + for _, command := range commandsToDelete { go func(command *discordgo.ApplicationCommand) { defer wg.Done() @@ -130,27 +159,47 @@ func (reporter *Reporter) InitCommands() { }(command) } - wg.Wait() - - for key, command := range reporter.Commands { - wg.Add(1) - go func(key string, command *Command) { + for _, command := range commandsToAdd { + go func(command *discordgo.ApplicationCommand) { defer wg.Done() - cmd, err := session.ApplicationCommandCreate(session.State.User.ID, reporter.Guild, command.Info) + cmd, err := session.ApplicationCommandCreate(session.State.User.ID, reporter.Guild, command) if err != nil { - reporter.Logger.Error().Err(err).Str("command", command.Info.Name).Msg("Could not create command") + reporter.Logger.Error().Err(err).Str("command", command.Name).Msg("Could not create command") return } reporter.Logger.Info().Str("command", cmd.Name).Msg("Created command") mutex.Lock() - reporter.Commands[key].Info = cmd + reporter.Commands[command.Name].Info = cmd mutex.Unlock() - }(key, command) + }(command) + } + + for _, command := range commandsToUpdate { + go func(command *discordgo.ApplicationCommand) { + defer wg.Done() + + cmd, err := session.ApplicationCommandEdit( + session.State.User.ID, + reporter.Guild, + command.ID, + command, + ) + if err != nil { + reporter.Logger.Error().Err(err).Str("command", command.Name).Msg("Could not update command") + return + } + reporter.Logger.Info().Str("command", cmd.Name).Msg("Updated command") + + mutex.Lock() + reporter.Commands[command.Name].Info = cmd + mutex.Unlock() + }(command) } wg.Wait() + reporter.Logger.Info().Msg("All commands updated") } func (reporter *Reporter) Enabled() bool { diff --git a/pkg/reporters/discord/list_mutes.go b/pkg/reporters/discord/list_mutes.go index a7c87bc..0424d6b 100644 --- a/pkg/reporters/discord/list_mutes.go +++ b/pkg/reporters/discord/list_mutes.go @@ -1,9 +1,10 @@ package discord import ( - "github.com/bwmarrin/discordgo" mutes "main/pkg/mutes" "main/pkg/utils" + + "github.com/bwmarrin/discordgo" ) func (reporter *Reporter) GetMutesCommand() *Command { diff --git a/pkg/reporters/discord/proposals.go b/pkg/reporters/discord/list_proposals.go similarity index 100% rename from pkg/reporters/discord/proposals.go rename to pkg/reporters/discord/list_proposals.go diff --git a/pkg/reporters/discord/params.go b/pkg/reporters/discord/params.go new file mode 100644 index 0000000..b5e519b --- /dev/null +++ b/pkg/reporters/discord/params.go @@ -0,0 +1,32 @@ +package discord + +import ( + "context" + "fmt" + + "github.com/bwmarrin/discordgo" +) + +func (reporter *Reporter) GetParamsCommand() *Command { + return &Command{ + Info: &discordgo.ApplicationCommand{ + Name: "params", + Description: "List all chains params.", + }, + Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { + params, err := reporter.DataManager.GetParams(context.Background()) + if err != nil { + reporter.BotRespond(s, i, fmt.Sprintf("Error getting chain params: %s", err)) + return + } + + template, err := reporter.TemplatesManager.Render("params", params) + if err != nil { + reporter.Logger.Error().Err(err).Str("template", "params").Msg("Error rendering template") + return + } + + reporter.BotRespond(s, i, template) + }, + } +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 3182af6..caa0701 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -41,6 +41,59 @@ func Contains[T comparable](slice []T, elt T) bool { return false } +// Subtract returns a new slice than includes values that are presented in the first, but not +// the second array. +func Subtract[T any, C comparable](first, second []T, predicate func(T) C) []T { + valuesMap := make(map[C]bool, len(second)) + for _, value := range second { + valuesMap[predicate(value)] = true + } + + newSlice := make([]T, 0) + + for _, value := range first { + predicateResult := predicate(value) + _, ok := valuesMap[predicateResult] + if !ok { + newSlice = append(newSlice, value) + } + } + + return newSlice +} + +func Union[T any, C comparable](first, second []T, predicate func(T) C) []T { + valuesMap := make(map[C]bool, len(second)) + for _, value := range second { + valuesMap[predicate(value)] = true + } + + newSlice := make([]T, 0) + + for _, value := range first { + predicateResult := predicate(value) + _, ok := valuesMap[predicateResult] + if ok { + newSlice = append(newSlice, value) + } + } + + return newSlice +} + +func MapToArray[K comparable, T any](source map[K]T) []T { + newSlice := make([]T, len(source)) + + index := 0 + + for _, value := range source { + newSlice[index] = value + index++ + } + + return newSlice +} + func FormatDuration(duration time.Duration) string { days := int64(duration.Hours() / 24) hours := int64(math.Mod(duration.Hours(), 24)) diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 2a93cc6..f6e47d1 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -147,3 +147,66 @@ func TestSplitStringIntoChunksMoreChunks(t *testing.T) { chunks := SplitStringIntoChunks(str, 10) assert.Len(t, chunks, 3, "There should be 3 chunks!") } + +func TestSubtract(t *testing.T) { + t.Parallel() + + type TestStruct struct { + Value string + } + + first := []TestStruct{ + {Value: "1"}, + {Value: "2"}, + {Value: "3"}, + } + + second := []TestStruct{ + {Value: "2"}, + {Value: "4"}, + } + + result := Subtract(first, second, func(v TestStruct) any { return v.Value }) + assert.Len(t, result, 2) + assert.Equal(t, "1", result[0].Value) + assert.Equal(t, "3", result[1].Value) +} + +func TestUnion(t *testing.T) { + t.Parallel() + + type TestStruct struct { + Value string + } + + first := []TestStruct{ + {Value: "1"}, + {Value: "2"}, + {Value: "3"}, + } + + second := []TestStruct{ + {Value: "2"}, + {Value: "4"}, + } + + result := Union(first, second, func(v TestStruct) any { return v.Value }) + assert.Len(t, result, 1) + assert.Equal(t, "2", result[0].Value) +} + +func TestMapToArray(t *testing.T) { + t.Parallel() + + testMap := map[string]string{ + "test1": "1", + "test2": "2", + "test3": "3", + } + + result := MapToArray(testMap) + assert.Len(t, result, 3) + assert.Equal(t, "1", result[0]) + assert.Equal(t, "2", result[1]) + assert.Equal(t, "3", result[2]) +} diff --git a/templates/discord/help.html b/templates/discord/help.html index f337ce3..be8c3c3 100644 --- a/templates/discord/help.html +++ b/templates/discord/help.html @@ -3,9 +3,10 @@ Notifies you about the proposals your wallets hasn't voted upon. Can understand the following commands: - - displays active proposals and your wallets' votes on them -- - mute notifications for a specific chain/proposal -- - unmute notifications for a specific chain/proposal -- - displays the active mutes list +- - mute notifications for a specific chain or proposal +- - unmute notifications for a specific chain or proposal +- - displays the active mutes list +- - list chains params - - display this message Created by [🐹 Quokka Stake]() with ❤️. diff --git a/templates/discord/params.html b/templates/discord/params.html new file mode 100644 index 0000000..dc80d07 --- /dev/null +++ b/templates/discord/params.html @@ -0,0 +1,7 @@ +{{- range $chainName, $params := . }} +**Params of chain {{ $params.Chain.GetName }}:** +{{- range $param := .Params }} +{{ $param.GetDescription }}: {{ $param.Serialize }} +{{- end }} + +{{ end }} diff --git a/templates/telegram/help.html b/templates/telegram/help.html index 6f1ddd9..8533d7d 100644 --- a/templates/telegram/help.html +++ b/templates/telegram/help.html @@ -2,8 +2,10 @@ Notifies you about the proposals your wallets hasn't voted upon. Can understand the following commands: - /proposals - displays active proposals and your wallets' votes on them -- /proposals_mute <duration> <chain> <proposal ID> - mute notifications for a specific proposal +- /proposals_mute <duration> <chain> <proposal ID> - mute notifications for a specific chain/proposal +- /proposals_unmute [<chain> <proposal ID>] - unmute notifications for a specific chain/proposal - /proposals_mutes - display the active proposals mutes list +- /params - list chains params - /help - display this command Created by 🐹 Quokka Stake with ❤️.