119 lines
2.6 KiB
Go
119 lines
2.6 KiB
Go
package cli
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"gitea.pglikers.com/tools/slacksend/internal/slack"
|
|
"gitea.pglikers.com/tools/slacksend/internal/version"
|
|
)
|
|
|
|
type Options struct {
|
|
ShowVersion bool
|
|
WebhookURL string
|
|
TimeoutSec int
|
|
Title string
|
|
}
|
|
|
|
func Run(args []string) int {
|
|
return run(args, os.Stdin, os.Stdout, os.Stderr)
|
|
}
|
|
|
|
func run(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
|
|
fs := flag.NewFlagSet("slacksend", flag.ContinueOnError)
|
|
fs.SetOutput(stderr)
|
|
fs.Usage = func() { Usage(stdout) }
|
|
|
|
var opt Options
|
|
fs.BoolVar(&opt.ShowVersion, "version", false, "show version")
|
|
fs.BoolVar(&opt.ShowVersion, "V", false, "show version (short)")
|
|
// other options
|
|
fs.StringVar(&opt.WebhookURL, "webhook", "", "Slack Incoming Webhook URL (or env SLACK_WEBHOOK_URL)")
|
|
fs.IntVar(&opt.TimeoutSec, "timeout", 10, "HTTP timeout seconds")
|
|
fs.StringVar(&opt.Title, "title", "", "message title (shown as bold)")
|
|
|
|
if err := fs.Parse(args[1:]); err != nil {
|
|
if err == flag.ErrHelp {
|
|
Usage(stdout)
|
|
return 0
|
|
}
|
|
fmt.Fprintln(stderr, err)
|
|
Usage(stderr)
|
|
return 2
|
|
}
|
|
|
|
if opt.ShowVersion {
|
|
fmt.Fprintln(stdout, version.String())
|
|
return 0
|
|
}
|
|
|
|
if len(fs.Args()) == 0 && isTerminal(stdin) {
|
|
fmt.Fprintln(stderr, "message is required. provide args or pipe via stdin")
|
|
Usage(stderr)
|
|
return 2
|
|
}
|
|
|
|
// webhook URL の解決: flag > env
|
|
webhookURL := strings.TrimSpace(opt.WebhookURL)
|
|
if webhookURL == "" {
|
|
webhookURL = strings.TrimSpace(os.Getenv("SLACK_WEBHOOK_URL"))
|
|
}
|
|
if webhookURL == "" {
|
|
fmt.Fprintln(stderr, "Slack webhook URL is required. Set --webhook or SLACK_WEBHOOK_URL.")
|
|
return 2
|
|
}
|
|
|
|
msg, err := message(fs.Args(), stdin)
|
|
if err != nil {
|
|
fmt.Fprintln(stderr, "read message error:", err)
|
|
return 1
|
|
}
|
|
if msg == "" {
|
|
Usage(stderr)
|
|
return 2
|
|
}
|
|
|
|
client := slack.WebhookClient{
|
|
URL: webhookURL,
|
|
Timeout: time.Duration(opt.TimeoutSec) * time.Second,
|
|
}
|
|
|
|
if err := client.Send(opt.Title, msg); err != nil {
|
|
fmt.Fprintln(stderr, "send failed:", err)
|
|
return 1
|
|
}
|
|
|
|
fmt.Fprintln(stdout, "ok")
|
|
return 0
|
|
}
|
|
|
|
func message(args []string, stdin io.Reader) (string, error) {
|
|
if len(args) > 0 {
|
|
return strings.Join(args, " "), nil
|
|
}
|
|
b, err := io.ReadAll(stdin)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.TrimSpace(string(b)), nil
|
|
}
|
|
|
|
// isTerminal reports whether the given reader is a character device (e.g., TTY).
|
|
func isTerminal(r io.Reader) bool {
|
|
fd, ok := r.(interface {
|
|
Stat() (os.FileInfo, error)
|
|
})
|
|
if !ok {
|
|
return false
|
|
}
|
|
info, err := fd.Stat()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return info.Mode()&os.ModeCharDevice != 0
|
|
}
|