package main import ( "bytes" "crypto/rand" "encoding/json" "fmt" "math/big" "os" "text/template" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/Luzifer/rconfig/v2" "github.com/Luzifer/twitch-bot/v3/plugins" ) var ( cfg = struct { BotRespond bool `flag:"bot-respond,b" default:"false" description:"Wrap output in a respond directive for twitch-bot"` Count int64 `flag:"count,c" default:"1" description:"How many dice to throw?"` LogLevel string `flag:"log-level" default:"info" description:"Log level (debug, info, warn, error, fatal)"` Sides int64 `flag:"sides,s" default:"6" description:"How many sides does the dice have?"` Template string `flag:"template" vardefault:"tpl" description:"Template to format the result with"` Type string `flag:"type,t" default:"dice" description:"What to throw (coin, dice)"` VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"` }{} version = "dev" ) func initApp() error { rconfig.AutoEnv(true) rconfig.SetVariableDefaults(map[string]string{ "tpl": `I threw {{.count}}x {{if eq .type "coin"}}coin{{else}}W{{.sides}}{{end}} for you and got:{{ range .results}} {{ if eq $.type "coin" }}{{ if eq . 1 }}Head{{ else }}Number{{ end }}{{ else }}{{ . }}{{ end }}{{ end }}`, }) if err := rconfig.ParseAndValidate(&cfg); err != nil { return errors.Wrap(err, "parsing cli options") } l, err := logrus.ParseLevel(cfg.LogLevel) if err != nil { return errors.Wrap(err, "parsing log-level") } logrus.SetLevel(l) return nil } func main() { var ( err error results []int64 sum int64 ) if err = initApp(); err != nil { logrus.WithError(err).Fatal("initializing app") } if cfg.VersionAndExit { logrus.WithField("version", version).Info("twitch-bot-tools/dice") os.Exit(0) } switch cfg.Type { case "coin": results, sum = getResults(2, cfg.Count) //nolint:gomnd // Makes no sense to extract case "dice": if cfg.Sides == 0 { // There is no [0..0] dice. logrus.Fatal("there is no 0-sided dice") } results, sum = getResults(cfg.Sides, cfg.Count) default: logrus.WithField("type", cfg.Type).Fatal("don't know how to throw that") } t, err := template.New("output").Parse(cfg.Template) if err != nil { logrus.WithError(err).Fatal("parsing template") } text := new(bytes.Buffer) if err = t.Execute(text, map[string]interface{}{ "count": cfg.Count, "results": results, "sides": cfg.Sides, "sum": sum, "type": cfg.Type, }); err != nil { logrus.WithError(err).Fatal("executing template") } if !cfg.BotRespond { fmt.Println(text.String()) //nolint:forbidigo // This is an expected stdout output return } output := []plugins.RuleAction{ { Type: "respond", Attributes: plugins.FieldCollectionFromData(map[string]any{ "message": text.String(), }), }, } if err = json.NewEncoder(os.Stdout).Encode(output); err != nil { logrus.WithError(err).Fatal("encoding bot response") } } func getRandomNumber(sides int64) int64 { n, _ := rand.Int(rand.Reader, big.NewInt(sides)) return n.Int64() } func getResults(sides, count int64) (res []int64, sum int64) { for i := int64(0); i < count; i++ { v := getRandomNumber(sides) + 1 res = append(res, v) sum += v } return res, sum }