From 5d8130c3a44fe7871758d3bca5cdaf500e0edcc1 Mon Sep 17 00:00:00 2001 From: Xevion Date: Sun, 24 Dec 2023 20:51:30 -0600 Subject: [PATCH] Remove session id log, move DiscordGoLogger, add redis --- api.go | 25 ++++++++----------- go.mod | 3 +++ go.sum | 10 ++++++++ helpers.go | 38 ++++++++++++++++++++++++++++- main.go | 71 +++++++++++++++++++----------------------------------- 5 files changed, 85 insertions(+), 62 deletions(-) diff --git a/api.go b/api.go index 6108d50..cb122e9 100644 --- a/api.go +++ b/api.go @@ -11,14 +11,9 @@ import ( ) var ( - sessionID string + sessionID string = RandomString(5) + Nonce() ) -func init() { - sessionID = RandomString(5) + Nonce() - log.Debug().Str("sessionId", sessionID).Msg("Session ID Generated") -} - type Term struct { } @@ -40,7 +35,7 @@ func GetTerms(search string, offset int, max int) ([]Term, error) { // _body, _ := io.ReadAll(res.Body) // Assert that the response is JSON - if !ContainsContentType(res, "application/json") { + if !ContentTypeMatch(res, "application/json") { log.Printf("ERR Response was not JSON: %s", res.Header.Get("Content-Type")) } @@ -67,7 +62,7 @@ func GetPartOfTerms(search string, term int, offset int, max int) ([]TermParts, } // Assert that the response is JSON - if !ContainsContentType(res, "application/json") { + if !ContentTypeMatch(res, "application/json") { log.Printf("ERR Response was not JSON: %s", res.Header.Get("Content-Type")) } @@ -95,7 +90,7 @@ func GetInstructor(search string, term int, offset int, max int) []Instructor { } // Assert that the response is JSON - if !ContainsContentType(res, "application/json") { + if !ContentTypeMatch(res, "application/json") { log.Printf("ERR Response was not JSON: %s", res.Header.Get("Content-Type")) } @@ -119,7 +114,7 @@ func GetClassDetails(term int, crn int) *ClassDetails { } // Assert that the response is JSON - if !ContainsContentType(res, "application/json") { + if !ContentTypeMatch(res, "application/json") { log.Printf("ERR Response was not JSON: %s", res.Header.Get("Content-Type")) } @@ -149,7 +144,7 @@ func Search(subject string, keyword string, term string, startDate time.Time, en } // Assert that the response is JSON - if !ContainsContentType(res, "application/json") { + if !ContentTypeMatch(res, "application/json") { log.Printf("ERR Response was not JSON: %s", res.Header.Get("Content-Type")) } @@ -177,7 +172,7 @@ func GetSubjects(search string, term int, offset int, max int) []Subject { } // Assert that the response is JSON - if !ContainsContentType(res, "application/json") { + if !ContentTypeMatch(res, "application/json") { log.Printf("ERR Response was not JSON: %s", res.Header.Get("Content-Type")) } @@ -205,7 +200,7 @@ func GetCampuses(search string, term int, offset int, max int) []Campus { } // Assert that the response is JSON - if !ContainsContentType(res, "application/json") { + if !ContentTypeMatch(res, "application/json") { log.Printf("ERR Response was not JSON: %s", res.Header.Get("Content-Type")) } @@ -233,7 +228,7 @@ func GetInstructionalMethods(search string, term int, offset int, max int) ([]In } // Assert that the response is JSON - if !ContainsContentType(res, "application/json") { + if !ContentTypeMatch(res, "application/json") { log.Printf("ERR Response was not JSON: %s", res.Header.Get("Content-Type")) } @@ -256,7 +251,7 @@ func GetCourseMeetingTime(term int, crn int) (*MeetingTimeResponse, error) { } // Assert that the response is JSON - if !ContainsContentType(res, "application/json") { + if !ContentTypeMatch(res, "application/json") { log.Fatal().Msgf("Response was not JSON: %s", res.Header.Get("Content-Type")) } diff --git a/go.mod b/go.mod index 80228fb..de542b4 100644 --- a/go.mod +++ b/go.mod @@ -7,8 +7,11 @@ require github.com/bwmarrin/discordgo v0.27.1 require github.com/joho/godotenv v1.5.1 require ( + github.com/cespare/xxhash/v2 v2.2.0 // 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 + github.com/redis/go-redis/v9 v9.3.1 // indirect github.com/rs/zerolog v1.31.0 // indirect github.com/samber/lo v1.39.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect diff --git a/go.sum b/go.sum index 9e5f577..f17a5e5 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,16 @@ github.com/bwmarrin/discordgo v0.27.0 h1:4ZK9KN+rGIxZ0fdGTmgdCcliQeW8Zhu6MnlFI92 github.com/bwmarrin/discordgo v0.27.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY= 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/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= +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/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= @@ -28,6 +34,10 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis v6.15.9+incompatible h1:F+tnlesQSl3h9V8DdmtcYFdvkHLhbb7AgcLW6UJxnC4= +github.com/redis/go-redis v6.15.9+incompatible/go.mod h1:ic6dLmR0d9rkHSzaa0Ab3QVRZcjopJ9hSSPCrecj/+s= +github.com/redis/go-redis/v9 v9.3.1 h1:KqdY8U+3X6z+iACvumCNxnoluToB+9Me+TvyFa21Mds= +github.com/redis/go-redis/v9 v9.3.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/clock v0.0.0-20190514195947-2896927a307a/go.mod h1:4r5QyqhjIWCcK8DO4KMclc5Iknq5qVBAlbYYzAbUScQ= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= diff --git a/helpers.go b/helpers.go index 68672b1..9d80aa4 100644 --- a/helpers.go +++ b/helpers.go @@ -5,10 +5,12 @@ import ( "io" "math/rand" "net/http" + "runtime" "strconv" "strings" "time" + "github.com/rs/zerolog" log "github.com/rs/zerolog/log" ) @@ -42,7 +44,8 @@ func AddUserAgent(req *http.Request) { req.Header.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36") } -func ContainsContentType(response *http.Response, search string) bool { +// ContentTypeMatch checks if the response has the given content type +func ContentTypeMatch(response *http.Response, search string) bool { // Split on commas, check if any of the types match for _, content_type := range strings.Split(response.Header.Get("Content-Type"), ";") { if content_type == search { @@ -54,6 +57,8 @@ func ContainsContentType(response *http.Response, search string) bool { const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +// RandomString returns a random string of length n using the letterBytes constant +// The constant used is specifically chosen to mimic Ellucian's banner session ID generation. func RandomString(n int) string { b := make([]byte, n) for i := range b { @@ -62,6 +67,37 @@ func RandomString(n int) string { return string(b) } +// DiscordGoLogger is a specialized helper function that implements discordgo's global logging interface. +// It directs all logs to the zerolog implementation. +func DiscordGoLogger(msgL, caller int, format string, a ...interface{}) { + pc, file, line, _ := runtime.Caller(caller) + + files := strings.Split(file, "/") + file = files[len(files)-1] + + name := runtime.FuncForPC(pc).Name() + fns := strings.Split(name, ".") + name = fns[len(fns)-1] + + msg := fmt.Sprintf(format, a...) + + var event *zerolog.Event + switch msgL { + case 0: + event = log.Debug() + case 1: + event = log.Info() + case 2: + event = log.Warn() + case 3: + event = log.Error() + default: + event = log.Info() + } + + event.Str("file", file).Int("line", line).Str("function", name).Msg(msg) +} + // Nonce returns a string made up of the current time in milliseconds, Unix epoch/UTC func Nonce() string { return strconv.Itoa(int(time.Now().UnixMilli())) diff --git a/main.go b/main.go index 1b7f9bd..af37361 100644 --- a/main.go +++ b/main.go @@ -2,16 +2,14 @@ package main import ( "flag" - "fmt" "net/http" "net/http/cookiejar" "os" "os/signal" - "runtime" - "strings" "github.com/bwmarrin/discordgo" "github.com/joho/godotenv" + "github.com/redis/go-redis/v9" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/samber/lo" @@ -19,12 +17,12 @@ import ( var ( // Base URL for all requests to the banner system - baseURL string - client http.Client - cookies http.CookieJar - session *discordgo.Session - RemoveCommands = flag.Bool("rmcmd", true, "Remove all commands after shutdowning or not") - integerOptionMinValue = 0.0 + baseURL string + client http.Client + kv *redis.Client + cookies http.CookieJar + session *discordgo.Session + RemoveCommands = flag.Bool("rmcmd", true, "Remove all commands after shutdowning or not") ) // logOut implements zerolog.LevelWriter @@ -52,6 +50,7 @@ func (l logOut) WriteLevel(level zerolog.Level, p []byte) (n int, err error) { } func init() { + // Try to grab the environment variable, or default to development env := os.Getenv("ENVIRONMENT") if env == "" { env = os.Getenv("RAILWAY_ENVIRONMENT") @@ -59,51 +58,32 @@ func init() { env = "development" } } - var isDevelopment bool = env == "development" + // Use the custom console writer if we're in development + var isDevelopment bool = env == "development" if isDevelopment { log.Logger = zerolog.New(logOut{}).With().Timestamp().Logger() } - discordgo.Logger = func(msgL, caller int, format string, a ...interface{}) { - pc, file, line, _ := runtime.Caller(caller) + // Set discordgo's logger to use zerolog + discordgo.Logger = DiscordGoLogger - files := strings.Split(file, "/") - file = files[len(files)-1] - - name := runtime.FuncForPC(pc).Name() - fns := strings.Split(name, ".") - name = fns[len(fns)-1] - - msg := fmt.Sprintf(format, a...) - - var event *zerolog.Event - switch msgL { - case 0: - event = log.Debug() - case 1: - event = log.Info() - case 2: - event = log.Warn() - case 3: - event = log.Error() - default: - event = log.Info() - } - - event.Str("file", file).Int("line", line).Str("function", name).Msg(msg) - } -} - -func main() { // Load environment variables if err := godotenv.Load(); err != nil { log.Debug().Err(err).Msg("Error loading .env file") } baseURL = os.Getenv("BANNER_BASE_URL") +} + +func main() { + // Setup redis + options, err := redis.ParseURL(os.Getenv("REDIS_URL")) + if err != nil { + log.Fatal().Err(err).Msg("Cannot parse redis url") + } + kv = redis.NewClient(options) // Create cookie jar - var err error cookies, err = cookiejar.New(nil) if err != nil { log.Err(err).Msg("Cannot create cookie jar") @@ -125,15 +105,16 @@ func main() { }) err = session.Open() if err != nil { - log.Fatal().Msgf("Cannot open the session: %v", err) + log.Fatal().Err(err).Msg("Cannot open the session") } // Setup command handlers session.AddHandler(func(internalSession *discordgo.Session, interaction *discordgo.InteractionCreate) { - if handler, ok := commandHandlers[interaction.ApplicationCommandData().Name]; ok { + name := interaction.ApplicationCommandData().Name + if handler, ok := commandHandlers[name]; ok { handler(internalSession, interaction) } else { - log.Warn().Msgf("Unknown command '%v'", interaction.ApplicationCommandData().Name) + log.Warn().Str("commandName", name).Msg("Unknown command") } }) @@ -164,8 +145,6 @@ func main() { <-stop if *RemoveCommands { - // log.Info().Array("commandIds", registeredCommands).Msg("Removing commands") - for _, cmd := range registeredCommands { err := session.ApplicationCommandDelete(session.State.User.ID, os.Getenv("BOT_TARGET_GUILD"), cmd.ID) if err != nil {