From c270293dfd67e1fe718ef190747044390e55b29d Mon Sep 17 00:00:00 2001 From: David Fernandez Jr <90578348+Darkchove@users.noreply.github.com> Date: Sun, 28 May 2023 04:45:18 -0600 Subject: [PATCH] Update main.go Use meaningful variable names: Improve the readability of the code by using more descriptive variable names. For example, instead of conf, you can use config. This will make the code easier to understand and maintain. Split code into functions: Break down the main function into smaller, self-contained functions. This will improve code organization and make it easier to test and reuse the code. For example, you can create separate functions for reading the configuration file, setting up HTTP servers, and connecting to Discord. Handle errors properly: Instead of using fmt.Println to print error messages, consider using the log package or returning errors. Printing error messages to the console is not a robust way of handling errors. You can log errors or return them from functions to provide more information about the error and allow for proper error handling. Use constants or flags for magic numbers: Instead of hardcoding values like time.Hour*1, "/debug/pprof/", and "/metrics", define them as constants or flags. This improves code readability and makes it easier to change these values in the future. Simplify goroutine creation: Instead of using an anonymous goroutine function, you can create named functions and use the go keyword to start them as goroutines. This makes the code easier to read and understand. Avoid busy waiting: Instead of using select {} for an infinite loop, consider using a blocking operation such as time.Sleep to pause the execution of the program indefinitely. This avoids unnecessary CPU usage. Use a logging library: Instead of printing log messages directly to the console, consider using a logging library like logrus or zap. These libraries provide more advanced logging features such as log levels, log formatting, and log rotation. Apply best practices for error handling: Handle errors consistently throughout the codebase. Consider using error wrapping to provide more context to errors and improve error traceability. --- cmd/autodelete/main.go | 138 ++++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 58 deletions(-) diff --git a/cmd/autodelete/main.go b/cmd/autodelete/main.go index 45206ed..13ec959 100644 --- a/cmd/autodelete/main.go +++ b/cmd/autodelete/main.go @@ -4,9 +4,11 @@ import ( "flag" "fmt" "io/ioutil" + "log" "net/http" "net/http/pprof" - rdebug "runtime/debug" + "os" + "runtime/debug" "time" "github.com/prometheus/client_golang/prometheus" @@ -15,78 +17,98 @@ import ( "gopkg.in/yaml.v2" ) -var flagShardID = flag.Int("shard", -1, "shard ID of this bot") -var flagNoHttp = flag.Bool("nohttp", false, "skip http handler") -var flagMetricsPort = flag.Int("metrics", 6130, "port for metrics listener; shard ID is added") -var flagMetricsListen = flag.String("metricslisten", "127.0.0.4", "addr to listen on for metrics handler") +const ( + defaultConfigFile = "config.yml" +) -func main() { - var conf autodelete.Config +var ( + flagShardID = flag.Int("shard", -1, "shard ID of this bot") + flagNoHTTP = flag.Bool("nohttp", false, "skip HTTP handler") + flagMetricsPort = flag.Int("metrics", 6130, "port for metrics listener; shard ID is added") + flagMetricsListen = flag.String("metricslisten", "127.0.0.4", "address to listen on for metrics handler") + flagConfigFile = flag.String("config", defaultConfigFile, "configuration file path") +) +func main() { flag.Parse() - confBytes, err := ioutil.ReadFile("config.yml") + config, err := readConfig(*flagConfigFile) if err != nil { - fmt.Println("Please copy config.yml.example to config.yml and fill out the values") - return + log.Fatalf("failed to read config file: %v", err) } - err = yaml.Unmarshal(confBytes, &conf) - if err != nil { - fmt.Println("yaml error:", err) - return - } - if conf.BotToken == "" { - fmt.Println("bot token must be specified") + + if config.BotToken == "" { + log.Fatal("bot token must be specified") } - if conf.Shards > 0 && *flagShardID == -1 { - fmt.Println("This AutoDelete instance is configured to be sharded; please specify --shard=n") - return + + if config.Shards > 0 && *flagShardID == -1 { + log.Fatal("this AutoDelete instance is configured to be sharded; please specify --shard=n") } - if *flagShardID > conf.Shards { - fmt.Println("error: shard nbr is > shard count") - return + + if *flagShardID > config.Shards { + log.Fatal("error: shard number is greater than shard count") } - b := autodelete.New(conf) + b := autodelete.New(config) - err = b.ConnectDiscord(*flagShardID, conf.Shards) + err = b.ConnectDiscord(*flagShardID, config.Shards) if err != nil { - fmt.Println("connect error:", err) - return + log.Fatalf("failed to connect to Discord: %v", err) } - var privHttp http.ServeMux - var pubHttp http.ServeMux - - go func() { - for { - time.Sleep(time.Hour * 1) - rdebug.FreeOSMemory() - } - }() - go func() { - privHttp.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) - privHttp.Handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{})) - metricSvr := &http.Server{ - Handler: &privHttp, - Addr: fmt.Sprintf("%s:%d", *flagMetricsListen, *flagMetricsPort+*flagShardID), - } - - err := metricSvr.ListenAndServe() - fmt.Println("exiting metric server", err) - }() - - if !*flagNoHttp { - fmt.Printf("url: %s%s\n", conf.HTTP.Public, "/discord_auto_delete/oauth/start") - pubHttp.HandleFunc("/discord_auto_delete/oauth/start", b.HTTPOAuthStart) - pubHttp.HandleFunc("/discord_auto_delete/oauth/callback", b.HTTPOAuthCallback) - pubSrv := &http.Server{ - Handler: &pubHttp, - Addr: conf.HTTP.Listen, - } - err = pubSrv.ListenAndServe() - fmt.Println("exiting main()", err) + go periodicallyFreeOSMemory() + + if !*flagNoHTTP { + go startMetricServer(*flagMetricsListen, *flagMetricsPort+*flagShardID) + go startHTTPServer(config.HTTP.Listen, b) + + fmt.Printf("URL: %s%s\n", config.HTTP.Public, "/discord_auto_delete/oauth/start") } else { select {} } } + +func readConfig(filePath string) (autodelete.Config, error) { + var config autodelete.Config + + configBytes, err := ioutil.ReadFile(filePath) + if err != nil { + return config, fmt.Errorf("failed to read config file: %w", err) + } + + err = yaml.Unmarshal(configBytes, &config) + if err != nil { + return config, fmt.Errorf("failed to unmarshal config: %w", err) + } + + return config, nil +} + +func periodicallyFreeOSMemory() { + ticker := time.NewTicker(time.Hour) + defer ticker.Stop() + + for range ticker.C { + debug.FreeOSMemory() + } +} + +func startMetricServer(listenAddress string, port int) { + mux := http.NewServeMux() + mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) + mux.Handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{})) + + addr := fmt.Sprintf("%s:%d", listenAddress, port) + metricServer := &http.Server{ + Handler: mux, + Addr: addr, + } + + err := metricServer.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Printf("failed to start metric server: %v", err) + os.Exit(1) + } +} + +func startHTTPServer