package main import ( "bytes" "encoding/json" "fmt" "gopkg.in/yaml.v2" "io/ioutil" "net/http" "os" "text/template" ) type Hook struct { Url string Schema map[string]string } func (h *Hook) String() string { return fmt.Sprintf("Hook: Url: %s Schema: %s", h.Url, h.Schema) } func readConf(fn string, cfg map[string]*Hook) error { if fn == "" { fn = os.Getenv("HOME") + "/.hook.yml" } content, err := ioutil.ReadFile(fn) if err != nil { return err } err = yaml.Unmarshal(content, cfg) if err != nil { return err } return nil } func help() { n := os.Args[0] fmt.Fprintf(os.Stderr, `Usage: %s [-c CONFIG] -n HOOK_NAME [-v VarName VarVal]... For example: echo "Contents!" \ | %s -c hook.yml -n backups \ -v Hostname "$(hostname)" \ -v Message "Backup failure! :bomb:" Where 'hook.yml' contains: backups: url: https://discordapp.com/api/webhooks/fill/me/in schema: username: 'backups@{{.Vars.Hostname}}' content: '{{.Vars.Message}} `+"```{{.Stdin}}```'"+` Note how '-v Var Assignment' populates '.Vars'. '.Stdin' is special, and populated via the command's standard input which is always read. If no configuration file is specified ~/.hook.yml will be used instead. `, n, n) os.Exit(1) } func (h *Hook) ProcessSchema(vars map[string]string) error { stdin, _ := ioutil.ReadAll(os.Stdin) // Unpack the args into an anonymous struct literal to be used // inside of each template data := struct { Vars map[string]string Stdin string }{ Vars: vars, Stdin: string(stdin), } // Loops over every key/value pair in the `schema:` field, treating // the text as a template and supplying the command line vars for k, v := range h.Schema { buf := &bytes.Buffer{} templ, err := template.New("schema").Parse(v) if err != nil { return err } err = templ.Execute(buf, data) if err != nil { return err } // Need to check that this gets modified properly result, err := ioutil.ReadAll(buf) if err != nil { return err } h.Schema[k] = string(result) } return nil } func (h *Hook) Post() (*http.Response, error) { body, err := json.Marshal(h.Schema) if err != nil { fmt.Fprintln(os.Stderr, err) } buf := bytes.NewBuffer(body) resp, err := http.Post(h.Url, "Application/json", buf) return resp, err } func main() { cfgFN := "" hookName := "" vars := map[string]string{} for i := 1; i < len(os.Args); i += 1 { switch os.Args[i] { case "-c", "-config", "--config": i += 1 cfgFN = os.Args[i] case "-n", "-name", "--name": i += 1 hookName = os.Args[i] case "-v", "-var", "--var": vars[os.Args[i+1]] = os.Args[i+2] i += 2 default: help() } } cfg := make(map[string]*Hook) err := readConf(cfgFN, cfg) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if hookName == "" { help() } else if _, ok := cfg[hookName]; !ok { fmt.Fprintf(os.Stderr, "Hook name \"%s\" does not exist!\n", hookName) help() } hook := cfg[hookName] err = hook.ProcessSchema(vars) if err != nil { fmt.Fprintln(os.Stderr, err) } _, err = hook.Post() if err != nil { fmt.Fprintln(os.Stderr, err) } }