I think we're getting there

This commit is contained in:
Philipp Heckel 2021-12-18 16:12:36 -05:00
parent f266afa1de
commit 2c1989beb0
3 changed files with 73 additions and 58 deletions

View file

@ -11,8 +11,8 @@
# Here's a (hopefully self-explanatory) example: # Here's a (hopefully self-explanatory) example:
# subscribe: # subscribe:
# - topic: mytopic # - topic: mytopic
# exec: /usr/local/bin/mytopic-triggered.sh # command: /usr/local/bin/mytopic-triggered.sh
# - topic: myserver.com/anothertopic # - topic: myserver.com/anothertopic
# exec: 'echo "$message"' # command: 'echo "$message"'
# #
# subscribe: # subscribe:

View file

@ -8,7 +8,7 @@ type Config struct {
DefaultHost string DefaultHost string
Subscribe []struct { Subscribe []struct {
Topic string Topic string
Exec string Command string
} }
} }

View file

@ -27,6 +27,7 @@ var cmdSubscribe = &cli.Command{
&cli.BoolFlag{Name: "from-config", Aliases: []string{"C"}, Usage: "read subscriptions from config file (service mode)"}, &cli.BoolFlag{Name: "from-config", Aliases: []string{"C"}, Usage: "read subscriptions from config file (service mode)"},
&cli.BoolFlag{Name: "poll", Aliases: []string{"p"}, Usage: "return events and exit, do not listen for new events"}, &cli.BoolFlag{Name: "poll", Aliases: []string{"p"}, Usage: "return events and exit, do not listen for new events"},
&cli.BoolFlag{Name: "scheduled", Aliases: []string{"sched", "S"}, Usage: "also return scheduled/delayed events"}, &cli.BoolFlag{Name: "scheduled", Aliases: []string{"sched", "S"}, Usage: "also return scheduled/delayed events"},
&cli.BoolFlag{Name: "verbose", Aliases: []string{"v"}, Usage: "print verbose output"},
}, },
Description: `Subscribe to a topic from a ntfy server, and either print or execute a command for Description: `Subscribe to a topic from a ntfy server, and either print or execute a command for
every arriving message. There are 3 modes in which the command can be run: every arriving message. There are 3 modes in which the command can be run:
@ -71,39 +72,9 @@ ntfy subscribe --from-config
} }
func execSubscribe(c *cli.Context) error { func execSubscribe(c *cli.Context) error {
fromConfig := c.Bool("from-config")
if fromConfig {
return execSubscribeFromConfig(c)
}
return execSubscribeWithoutConfig(c)
}
func execSubscribeFromConfig(c *cli.Context) error {
conf, err := loadConfig(c)
if err != nil {
return err
}
cl := client.New(conf)
commands := make(map[string]string)
for _, s := range conf.Subscribe {
topicURL := cl.Subscribe(s.Topic)
commands[topicURL] = s.Exec
}
for m := range cl.Messages {
command, ok := commands[m.TopicURL]
if !ok {
continue
}
_ = dispatchMessage(c, command, m)
}
return nil
}
func execSubscribeWithoutConfig(c *cli.Context) error {
if c.NArg() < 1 {
return errors.New("topic missing")
}
fmt.Fprintln(c.App.ErrWriter, "\x1b[1;33mThis command is incubating. The interface may change without notice.\x1b[0m") fmt.Fprintln(c.App.ErrWriter, "\x1b[1;33mThis command is incubating. The interface may change without notice.\x1b[0m")
// Read config and options
conf, err := loadConfig(c) conf, err := loadConfig(c)
if err != nil { if err != nil {
return err return err
@ -112,8 +83,12 @@ func execSubscribeWithoutConfig(c *cli.Context) error {
since := c.String("since") since := c.String("since")
poll := c.Bool("poll") poll := c.Bool("poll")
scheduled := c.Bool("scheduled") scheduled := c.Bool("scheduled")
fromConfig := c.Bool("from-config")
topic := c.Args().Get(0) topic := c.Args().Get(0)
command := c.Args().Get(1) command := c.Args().Get(1)
if !fromConfig {
conf.Subscribe = nil // wipe if --from-config not passed
}
var options []client.SubscribeOption var options []client.SubscribeOption
if since != "" { if since != "" {
options = append(options, client.WithSince(since)) options = append(options, client.WithSince(since))
@ -124,41 +99,78 @@ func execSubscribeWithoutConfig(c *cli.Context) error {
if scheduled { if scheduled {
options = append(options, client.WithScheduled()) options = append(options, client.WithScheduled())
} }
if topic == "" && len(conf.Subscribe) == 0 {
return errors.New("must specify topic, or have at least one topic defined in config")
}
// Execute poll or subscribe
if poll { if poll {
return execPoll(c, cl, conf, topic, command, options...)
}
return execSubscribeInternal(c, cl, conf, topic, command, options...)
}
func execPoll(c *cli.Context, cl *client.Client, conf *client.Config, topic, command string, options ...client.SubscribeOption) error {
for _, s := range conf.Subscribe { // may be nil
if err := execPollSingle(c, cl, s.Topic, s.Command, options...); err != nil {
return err
}
}
if topic != "" {
if err := execPollSingle(c, cl, topic, command, options...); err != nil {
return err
}
}
return nil
}
func execPollSingle(c *cli.Context, cl *client.Client, topic, command string, options ...client.SubscribeOption) error {
messages, err := cl.Poll(topic, options...) messages, err := cl.Poll(topic, options...)
if err != nil { if err != nil {
return err return err
} }
for _, m := range messages { for _, m := range messages {
_ = dispatchMessage(c, command, m) printMessageOrRunCommand(c, m, command)
}
return nil
}
func execSubscribeInternal(c *cli.Context, cl *client.Client, conf *client.Config, topic, command string, options ...client.SubscribeOption) error {
commands := make(map[string]string)
for _, s := range conf.Subscribe { // May be nil
topicURL := cl.Subscribe(s.Topic, options...)
commands[topicURL] = s.Command
}
if topic != "" {
topicURL := cl.Subscribe(topic, options...)
commands[topicURL] = command
} }
} else {
cl.Subscribe(topic, options...)
for m := range cl.Messages { for m := range cl.Messages {
_ = dispatchMessage(c, command, m) command, ok := commands[m.TopicURL]
if !ok {
continue
} }
printMessageOrRunCommand(c, m, command)
} }
return nil return nil
} }
func dispatchMessage(c *cli.Context, command string, m *client.Message) error { func printMessageOrRunCommand(c *cli.Context, m *client.Message, command string) {
if m.Event != client.MessageEvent {
return
}
if command != "" { if command != "" {
return execCommand(c, command, m) runCommand(c, command, m)
} else {
fmt.Fprintln(c.App.Writer, m.Raw)
} }
fmt.Println(m.Raw)
return nil
} }
func execCommand(c *cli.Context, command string, m *client.Message) error { func runCommand(c *cli.Context, command string, m *client.Message) {
if m.Event == client.OpenEvent {
log.Printf("[%s] Connection opened, subscribed to topic", collapseTopicURL(m.TopicURL))
} else if m.Event == client.MessageEvent {
if err := runCommandInternal(c, command, m); err != nil { if err := runCommandInternal(c, command, m); err != nil {
log.Printf("[%s] Command failed: %s", collapseTopicURL(m.TopicURL), err.Error()) fmt.Fprintf(c.App.ErrWriter, "Command failed: %s\n", err.Error())
} }
} }
return nil
}
func runCommandInternal(c *cli.Context, command string, m *client.Message) error { func runCommandInternal(c *cli.Context, command string, m *client.Message) error {
scriptFile, err := createTmpScript(command) scriptFile, err := createTmpScript(command)
@ -166,7 +178,10 @@ func runCommandInternal(c *cli.Context, command string, m *client.Message) error
return err return err
} }
defer os.Remove(scriptFile) defer os.Remove(scriptFile)
verbose := c.Bool("verbose")
if verbose {
log.Printf("[%s] Executing: %s (for message: %s)", collapseTopicURL(m.TopicURL), command, m.Raw) log.Printf("[%s] Executing: %s (for message: %s)", collapseTopicURL(m.TopicURL), command, m.Raw)
}
cmd := exec.Command("sh", "-c", scriptFile) cmd := exec.Command("sh", "-c", scriptFile)
cmd.Stdin = c.App.Reader cmd.Stdin = c.App.Reader
cmd.Stdout = c.App.Writer cmd.Stdout = c.App.Writer