2021-01-05 22:54:24 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
2021-01-06 18:44:29 +00:00
|
|
|
"log"
|
2021-01-05 22:54:24 +00:00
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/slack-go/slack"
|
|
|
|
)
|
|
|
|
|
2021-02-09 18:20:38 +00:00
|
|
|
var version = "dev"
|
2021-02-02 21:49:10 +00:00
|
|
|
|
2021-01-05 22:54:24 +00:00
|
|
|
// statusInfo contains all args passed from the command line
|
|
|
|
type statusInfo struct {
|
|
|
|
emoji, statusText string
|
|
|
|
duration time.Duration
|
|
|
|
snooze bool
|
2021-02-09 18:20:38 +00:00
|
|
|
}
|
2021-02-02 19:57:39 +00:00
|
|
|
|
2021-02-09 18:20:38 +00:00
|
|
|
// commandOptions contains non-status options passed to the command
|
|
|
|
type commandOptions struct {
|
|
|
|
login, makeDefault, showVersion bool
|
|
|
|
domain string
|
2021-01-05 22:54:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// getExipirationTime returns epoch time that status should expire from the duration.
|
|
|
|
func (si statusInfo) getExpirationTime() int64 {
|
|
|
|
if si.duration == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return time.Now().Add(si.duration).Unix()
|
|
|
|
}
|
|
|
|
|
|
|
|
// readDurationArgs will attempt to find a duration within command line args rather than flags.
|
|
|
|
// It will look for a prefixed duration. eg. "5m :cowboy: Howdy y'all" and a postfix duration
|
|
|
|
// following the word "for". eg. ":dancing: Dancing for 1h".
|
|
|
|
func readDurationArgs(args []string) ([]string, *time.Duration) {
|
|
|
|
// If there are no args, we have no duration
|
|
|
|
if len(args) == 0 {
|
|
|
|
return args, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to parse the first value
|
|
|
|
durationVal, err := time.ParseDuration(args[0])
|
|
|
|
if err == nil {
|
|
|
|
// Found a duration, return the trimmed args and duration
|
|
|
|
return args[1:], &durationVal
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the args are less than two, then we don't have a "for <duration>" expression
|
|
|
|
minArgsForSuffix := 2
|
|
|
|
if len(args) < minArgsForSuffix {
|
|
|
|
return args, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for a "for <duration>" expression at end of args
|
|
|
|
if strings.ToLower(args[len(args)-2]) == "for" {
|
|
|
|
durationVal, err = time.ParseDuration(args[len(args)-1])
|
|
|
|
if err == nil {
|
|
|
|
// Found a duration, return the trimmed args and duration
|
|
|
|
return args[:len(args)-2], &durationVal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default return input
|
|
|
|
return args, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// readFlags will read all flags off the command line.
|
2021-02-09 18:20:38 +00:00
|
|
|
func readFlags() (statusInfo, commandOptions) {
|
2021-02-02 19:57:39 +00:00
|
|
|
// Non-status flags
|
|
|
|
login := flag.Bool("login", false, "login to a Slack workspace")
|
|
|
|
domain := flag.String("domain", "", "domain to set status on")
|
|
|
|
makeDefault := flag.Bool("make-default", false, "set the current domain to default")
|
2021-02-02 21:49:10 +00:00
|
|
|
showVersion := flag.Bool("version", false, "show version and exit")
|
2021-02-02 19:57:39 +00:00
|
|
|
|
|
|
|
// Status flags
|
2021-01-05 22:54:24 +00:00
|
|
|
snooze := flag.Bool("snooze", false, "snooze notifications")
|
|
|
|
duration := flag.Duration("duration", 0, "duration to set status for")
|
|
|
|
emoji := flag.String("emoji", "", "emoji to use as status")
|
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
// Freeform input checks the first argument to see if it's a duration
|
|
|
|
args := flag.Args()
|
|
|
|
|
|
|
|
// Duration was not set via a flag, check the args
|
|
|
|
if *duration == 0 {
|
|
|
|
var parsedDuration *time.Duration
|
|
|
|
args, parsedDuration = readDurationArgs(args)
|
|
|
|
|
|
|
|
if parsedDuration != nil {
|
|
|
|
duration = parsedDuration
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if *emoji == "" && len(args) > 0 {
|
|
|
|
if args[0][0] == ':' && args[0][len(args[0])-1] == ':' {
|
|
|
|
emoji = &args[0]
|
|
|
|
args = args[1:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
statusText := strings.Join(args, " ")
|
|
|
|
|
|
|
|
return statusInfo{
|
2021-02-09 18:20:38 +00:00
|
|
|
duration: *duration,
|
|
|
|
snooze: *snooze,
|
|
|
|
emoji: *emoji,
|
|
|
|
statusText: statusText,
|
|
|
|
}, commandOptions{
|
|
|
|
login: *login,
|
|
|
|
domain: *domain,
|
|
|
|
makeDefault: *makeDefault,
|
|
|
|
showVersion: *showVersion,
|
|
|
|
}
|
2021-01-05 22:54:24 +00:00
|
|
|
}
|
|
|
|
|
2021-02-02 19:57:39 +00:00
|
|
|
// loginAndSave will return a client after a new login flow and save the results
|
|
|
|
func loginAndSave(domain string) (*slack.Client, error) {
|
|
|
|
accessToken, err := authenticate()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to authenticate new login: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
client := slack.New(accessToken)
|
|
|
|
|
|
|
|
if domain == "" {
|
|
|
|
info, err := client.GetTeamInfo()
|
|
|
|
if err != nil {
|
|
|
|
return client, fmt.Errorf("failed to get team info: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
domain = info.Domain
|
2021-01-06 18:44:29 +00:00
|
|
|
}
|
|
|
|
|
2021-02-02 19:57:39 +00:00
|
|
|
err = saveLogin(domain, accessToken)
|
|
|
|
if err != nil {
|
|
|
|
return client, fmt.Errorf("failed saving new login info: %w", err)
|
2021-01-06 18:44:29 +00:00
|
|
|
}
|
|
|
|
|
2021-02-02 19:57:39 +00:00
|
|
|
return client, err
|
|
|
|
}
|
2021-01-06 18:44:29 +00:00
|
|
|
|
2021-02-02 19:57:39 +00:00
|
|
|
// getClient returns a client either via the provided login or default login
|
|
|
|
func getClient(domain string) (*slack.Client, error) {
|
|
|
|
var accessToken string
|
2021-02-09 18:20:38 +00:00
|
|
|
|
2021-02-02 19:57:39 +00:00
|
|
|
var err error
|
|
|
|
|
|
|
|
if domain == "" {
|
|
|
|
accessToken, err = getDefaultLogin()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get default login: %w", err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
accessToken, err = getLogin(domain)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get login for domain %s: %w", domain, err)
|
|
|
|
}
|
2021-01-06 18:44:29 +00:00
|
|
|
}
|
|
|
|
|
2021-02-02 19:57:39 +00:00
|
|
|
return slack.New(accessToken), nil
|
2021-01-06 18:44:29 +00:00
|
|
|
}
|
|
|
|
|
2021-01-05 22:54:24 +00:00
|
|
|
func main() {
|
2021-02-09 18:20:38 +00:00
|
|
|
status, options := readFlags()
|
2021-01-05 22:54:24 +00:00
|
|
|
|
2021-02-09 18:20:38 +00:00
|
|
|
if options.showVersion {
|
2021-02-02 21:49:10 +00:00
|
|
|
fmt.Println("version:", version)
|
2021-02-09 18:20:38 +00:00
|
|
|
|
2021-02-02 21:49:10 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-02-02 19:57:39 +00:00
|
|
|
var client *slack.Client
|
2021-02-09 18:20:38 +00:00
|
|
|
|
2021-02-02 19:57:39 +00:00
|
|
|
var err error
|
|
|
|
|
|
|
|
// If the new-auth flag is present, force an auth flow
|
2021-02-09 18:20:38 +00:00
|
|
|
if options.login {
|
|
|
|
client, err = loginAndSave(options.domain)
|
2021-02-02 19:57:39 +00:00
|
|
|
} else {
|
2021-02-09 18:20:38 +00:00
|
|
|
client, err = getClient(options.domain)
|
2021-02-02 19:57:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// We encountered some error in logging in
|
2021-01-05 22:54:24 +00:00
|
|
|
if err != nil {
|
2021-02-02 19:57:39 +00:00
|
|
|
fmt.Println("Unable to create Slack client. Have you logged in yet? Try using `-login`")
|
|
|
|
log.Fatal(fmt.Errorf("failed to get or save client: %w", err))
|
|
|
|
}
|
|
|
|
|
2021-02-09 18:20:38 +00:00
|
|
|
// If a domain is provided and asked to make default, save it to config
|
|
|
|
if options.makeDefault && options.domain != "" {
|
|
|
|
if err = saveDefaultLogin(options.domain); err != nil {
|
|
|
|
log.Fatal(fmt.Errorf("failed saving default domain %s: %w", options.domain, err))
|
2021-02-02 19:57:39 +00:00
|
|
|
}
|
2021-01-05 22:54:24 +00:00
|
|
|
}
|
|
|
|
|
2021-02-09 18:20:38 +00:00
|
|
|
err = client.SetUserCustomStatus(status.statusText, status.emoji, status.getExpirationTime())
|
2021-01-05 22:54:24 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println("error setting status")
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
2021-02-09 18:20:38 +00:00
|
|
|
if status.snooze {
|
|
|
|
_, err = client.SetSnooze(int(status.duration.Minutes()))
|
2021-01-05 22:54:24 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println("error setting snooze")
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_, err = client.EndSnooze()
|
|
|
|
if err != nil && err.Error() != "snooze_not_active" {
|
|
|
|
fmt.Println("error ending snooze")
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|