From a545cf2b1c992cb9aba6d2b3e2d0f9f3880afbfa Mon Sep 17 00:00:00 2001 From: Xevion Date: Sun, 24 Dec 2023 21:59:03 -0600 Subject: [PATCH] Add structhash, hash command definition with xxhash, store & compare with redis --- go.mod | 1 + go.sum | 2 ++ main.go | 55 +++++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index de542b4..6b3f0b5 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require github.com/joho/godotenv v1.5.1 require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect diff --git a/go.sum b/go.sum index f17a5e5..a864793 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4Ho github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ= +github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/main.go b/main.go index af37361..0885094 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,18 @@ package main import ( + "context" + "encoding/base64" "flag" + "fmt" "net/http" "net/http/cookiejar" "os" "os/signal" "github.com/bwmarrin/discordgo" + "github.com/cespare/xxhash/v2" + "github.com/cnf/structhash" "github.com/joho/godotenv" "github.com/redis/go-redis/v9" "github.com/rs/zerolog" @@ -20,6 +25,7 @@ var ( baseURL string client http.Client kv *redis.Client + ctx context.Context cookies http.CookieJar session *discordgo.Session RemoveCommands = flag.Bool("rmcmd", true, "Remove all commands after shutdowning or not") @@ -50,6 +56,8 @@ func (l logOut) WriteLevel(level zerolog.Level, p []byte) (n int, err error) { } func init() { + ctx = context.Background() + // Try to grab the environment variable, or default to development env := os.Getenv("ENVIRONMENT") if env == "" { @@ -83,6 +91,13 @@ func main() { } kv = redis.NewClient(options) + // Test the redis instance + pong, err := kv.Ping(ctx).Result() + if err != nil { + log.Fatal().Err(err).Msg("Cannot connect to redis") + } + log.Debug().Str("ping", pong).Msg("Redis connection successful") + // Create cookie jar cookies, err = cookiejar.New(nil) if err != nil { @@ -128,14 +143,39 @@ func main() { // Register commands registeredCommands := make([]*discordgo.ApplicationCommand, len(commandDefinitions)) for i, cmdDefinition := range commandDefinitions { - cmdInstance, err := session.ApplicationCommandCreate(session.State.User.ID, os.Getenv("BOT_TARGET_GUILD"), cmdDefinition) + // Compare the hash to the hash stored in redis + hash := xxhash.Sum64(structhash.Dump(cmdDefinition, 1)) + key := fmt.Sprintf("command:%s:xxhash", cmdDefinition.Name) + keyB64 := base64.StdEncoding.EncodeToString([]byte(key)) + storedHash, err := kv.Get(ctx, key).Uint64() if err != nil { - log.Panic().Err(err).Str("name", cmdDefinition.Name).Msgf("Cannot register command") + if err != redis.Nil { + log.Err(err).Msg("Cannot get command hash from redis") + } else { + log.Debug().Str("command", cmdDefinition.Name).Str("key", keyB64).Msg("Command hash not found in redis") + } + } + + // If the hash is the same, skip registering the command + if hash == storedHash { + log.Debug().Str("command", cmdDefinition.Name).Str("key", keyB64).Msg("Command hash matches, skipping registration") + continue + } + + // Register the command + cmdInstance, err := session.ApplicationCommandCreate(session.State.User.ID, "", cmdDefinition) + if err != nil { + log.Panic().Err(err).Str("name", cmdDefinition.Name).Str("key", keyB64).Msg("Cannot register command") + } + err = kv.Set(ctx, key, hash, 0).Err() + if err != nil { + log.Err(err).Str("name", cmdDefinition.Name).Str("key", keyB64).Msg("Cannot set command hash in redis") } registeredCommands[i] = cmdInstance + log.Info().Str("name", cmdDefinition.Name).Str("key", keyB64).Msg("Registered command") } - // Cloes session, ensure + // Cloes session, ensure http client closes idle connections defer session.Close() defer client.CloseIdleConnections() @@ -144,15 +184,6 @@ func main() { log.Info().Msg("Press Ctrl+C to exit") <-stop - if *RemoveCommands { - for _, cmd := range registeredCommands { - err := session.ApplicationCommandDelete(session.State.User.ID, os.Getenv("BOT_TARGET_GUILD"), cmd.ID) - if err != nil { - log.Err(err).Str("command", cmd.Name) - } - } - } - log.Warn().Msg("Gracefully shutting down") }