Send push notifications from the terminal to a Slack with a Webhook
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

195 lines
4.4 KiB

package main
import (
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"os/user"
"strconv"
"strings"
"time"
"github.com/karrick/golf"
"github.com/slack-go/slack"
"git.iamthefij.com/iamthefij/notify-to-slack/figures"
)
const (
BASE_10 = 10
)
var (
applicationName = "notify-to-slack"
version = "dev"
)
type Config struct {
HookURL string
}
func (_ Config) ApplicationName() string {
return applicationName
}
func (_ Config) Filename() string {
return "config"
}
// ShellCommand takes a string and constructs a command that executs within `sh`
func ShellCommand(command string) *exec.Cmd {
shellCommand := []string{"sh", "-c", strings.TrimSpace(command)}
return exec.Command(shellCommand[0], shellCommand[1:]...)
}
func maybeHostname() string {
if hostname, err := os.Hostname(); err != nil {
return "unknown hostname"
} else {
return hostname
}
}
func maybeUsername() string {
if currentUser, err := user.Current(); err != nil {
return "unknown user"
} else {
return currentUser.Username
}
}
func printUsage() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, "Reads in and does stuff\n")
configPath, err := figures.NewFiguration(applicationName).GetConfigFilePath("")
if err == nil {
fmt.Fprintf(os.Stderr, "Config directory is %s\n", configPath)
}
golf.PrintDefaults()
}
func printVersion() {
// fmt.Fprintf(os.Stderr, "%s: at version %s\n", os.Args[0], version)
fmt.Fprintf(os.Stderr, "%s: at version %s\n", os.Args[0], version)
}
func main() {
showHelp := golf.BoolP('h', "help", false, "show usage and exit")
showVersion := golf.BoolP('V', "version", false, "show version and exit")
lastStatus := golf.IntP('s', "status", -1, "last command status")
command := golf.StringP('c', "command", "", "execute command in a sub shell and publish results")
atChannel := golf.BoolP('a', "at-channel", false, "add @channel to the message")
channel := golf.StringP('l', "channel", "", "select channel to send message to, if not the default for your integration")
hookURL := golf.StringP('u', "hook-url", "", "set the webhook URL to use")
golf.Parse()
if *showHelp {
printUsage()
return
}
if *showVersion {
printVersion()
return
}
// Read the configuration
config := Config{}
err := figures.ReadConfig(&config)
// User set a new hook url
if *hookURL != "" {
config.HookURL = *hookURL
err = figures.WriteConfig(config)
if err != nil {
panic(fmt.Sprintf("Could not write to config file: %s", err))
}
} else {
if err != nil && !errors.Is(err, figures.ConfigFileNotFoundErr) {
panic(fmt.Sprintf("Error attempting to read the config file. %s", err))
}
if config.HookURL == "" {
panic("You have not set a hook url. Try running with --hook-url <your url here>")
}
}
attachments := []slack.Attachment{}
// Execute nested command
if *command != "" {
c := ShellCommand(*command)
var footer string
color := "good"
output, err := c.CombinedOutput()
if err != nil {
color = "danger"
if exitError, ok := err.(*exec.ExitError); ok {
footer = fmt.Sprintf("status %d", exitError.ExitCode())
} else {
footer = fmt.Sprintf("unknown status %s", err)
}
}
attachments = append(attachments, slack.Attachment{
Color: color,
AuthorName: maybeUsername(),
AuthorSubname: maybeHostname(),
Text: string(output),
Footer: footer,
Ts: json.Number(strconv.FormatInt(time.Now().Unix(), BASE_10)),
})
}
// Get message text from provided arguments
args := golf.Args()
message := strings.Join(args, " ")
// Build status attachment if a status was provided
if *lastStatus >= 0 {
color := "good"
if *lastStatus > 0 {
color = "danger"
}
attachments = append(attachments, slack.Attachment{
Color: color,
AuthorName: maybeUsername(),
AuthorSubname: maybeHostname(),
Text: message,
Footer: fmt.Sprintf("status %d", *lastStatus),
Ts: json.Number(strconv.FormatInt(time.Now().Unix(), BASE_10)),
})
// Empty out message to avoid duplicating in the message content
message = ""
}
// Maybe prepend an @channel
if *atChannel {
message = strings.Join([]string{"<!channel>", message}, " ")
}
msg := slack.WebhookMessage{
Attachments: attachments,
Username: "cli-noti",
Text: message,
Channel: *channel,
}
err = slack.PostWebhook(config.HookURL, &msg)
if err != nil {
fmt.Println(err)
}
}