diff --git a/cmd/app.go b/cmd/app.go index 504bde5f..f726587e 100644 --- a/cmd/app.go +++ b/cmd/app.go @@ -9,11 +9,6 @@ import ( "os" ) -var ( - defaultClientRootConfigFile = "/etc/ntfy/client.yml" - defaultClientUserConfigFile = "~/.config/ntfy/client.yml" -) - const ( categoryClient = "Client commands" categoryServer = "Server commands" diff --git a/cmd/publish.go b/cmd/publish.go index 34d7bf70..728185e0 100644 --- a/cmd/publish.go +++ b/cmd/publish.go @@ -63,8 +63,7 @@ Examples: Please also check out the docs on publishing messages. Especially for the --tags and --delay options, it has incredibly useful information: https://ntfy.sh/docs/publish/. -The default config file for all client commands is /etc/ntfy/client.yml (if root user), -or ~/.config/ntfy/client.yml for all other users.`, +` + defaultClientConfigFileDescriptionSuffix, } func execPublish(c *cli.Context) error { diff --git a/cmd/subscribe.go b/cmd/subscribe.go index 95ad02a4..035f4abe 100644 --- a/cmd/subscribe.go +++ b/cmd/subscribe.go @@ -6,10 +6,7 @@ import ( "github.com/urfave/cli/v2" "heckel.io/ntfy/client" "heckel.io/ntfy/util" - "log" "os" - "os/exec" - "os/user" "strings" ) @@ -64,19 +61,17 @@ ntfy subscribe TOPIC COMMAND Examples: ntfy sub mytopic 'notify-send "$m"' # Execute command for incoming messages - ntfy sub topic1 /my/script.sh # Execute script for incoming messages + ntfy sub topic1 myscript.sh # Execute script for incoming messages ntfy subscribe --from-config - Service mode (used in ntfy-client.service). This reads the config file (/etc/ntfy/client.yml - or ~/.config/ntfy/client.yml) and sets up subscriptions for every topic in the "subscribe:" - block (see config file). + Service mode (used in ntfy-client.service). This reads the config file and sets up + subscriptions for every topic in the "subscribe:" block (see config file). Examples: ntfy sub --from-config # Read topics from config file - ntfy sub --config=/my/client.yml --from-config # Read topics from alternate config file + ntfy sub --config=myclient.yml --from-config # Read topics from alternate config file -The default config file for all client commands is /etc/ntfy/client.yml (if root user), -or ~/.config/ntfy/client.yml for all other users.`, +` + defaultClientConfigFileDescriptionSuffix, } func execSubscribe(c *cli.Context) error { @@ -160,8 +155,8 @@ func doPollSingle(c *cli.Context, cl *client.Client, topic, command string, opti } func doSubscribe(c *cli.Context, cl *client.Client, conf *client.Config, topic, command string, options ...client.SubscribeOption) error { - commands := make(map[string]string) // Subscription ID -> command - for _, s := range conf.Subscribe { // May be nil + cmds := make(map[string]string) // Subscription ID -> command + for _, s := range conf.Subscribe { // May be nil topicOptions := append(make([]client.SubscribeOption, 0), options...) for filter, value := range s.If { topicOptions = append(topicOptions, client.WithFilter(filter, value)) @@ -170,18 +165,18 @@ func doSubscribe(c *cli.Context, cl *client.Client, conf *client.Config, topic, topicOptions = append(topicOptions, client.WithBasicAuth(s.User, s.Password)) } subscriptionID := cl.Subscribe(s.Topic, topicOptions...) - commands[subscriptionID] = s.Command + cmds[subscriptionID] = s.Command } if topic != "" { subscriptionID := cl.Subscribe(topic, options...) - commands[subscriptionID] = command + cmds[subscriptionID] = command } for m := range cl.Messages { - command, ok := commands[m.SubscriptionID] + cmd, ok := cmds[m.SubscriptionID] if !ok { continue } - printMessageOrRunCommand(c, m, command) + printMessageOrRunCommand(c, m, cmd) } return nil } @@ -200,33 +195,6 @@ func runCommand(c *cli.Context, command string, m *client.Message) { } } -func runCommandInternal(c *cli.Context, command string, m *client.Message) error { - scriptFile, err := createTmpScript(command) - if err != nil { - return err - } - defer os.Remove(scriptFile) - verbose := c.Bool("verbose") - if verbose { - log.Printf("[%s] Executing: %s (for message: %s)", util.ShortTopicURL(m.TopicURL), command, m.Raw) - } - cmd := exec.Command("sh", "-c", scriptFile) - cmd.Stdin = c.App.Reader - cmd.Stdout = c.App.Writer - cmd.Stderr = c.App.ErrWriter - cmd.Env = envVars(m) - return cmd.Run() -} - -func createTmpScript(command string) (string, error) { - scriptFile := fmt.Sprintf("%s/ntfy-subscribe-%s.sh.tmp", os.TempDir(), util.RandomString(10)) - script := fmt.Sprintf("#!/bin/sh\n%s", command) - if err := os.WriteFile(scriptFile, []byte(script), 0700); err != nil { - return "", err - } - return scriptFile, nil -} - func envVars(m *client.Message) []string { env := os.Environ() env = append(env, envVar(m.ID, "NTFY_ID", "id")...) @@ -253,11 +221,7 @@ func loadConfig(c *cli.Context) (*client.Config, error) { if filename != "" { return client.LoadConfig(filename) } - u, _ := user.Current() - configFile := defaultClientRootConfigFile - if u.Uid != "0" { - configFile = util.ExpandHome(defaultClientUserConfigFile) - } + configFile := defaultConfigFile() if s, _ := os.Stat(configFile); s != nil { return client.LoadConfig(configFile) } diff --git a/cmd/subscribe_linux.go b/cmd/subscribe_linux.go new file mode 100644 index 00000000..2b55f806 --- /dev/null +++ b/cmd/subscribe_linux.go @@ -0,0 +1,57 @@ +package cmd + +import ( + "fmt" + "github.com/urfave/cli/v2" + "heckel.io/ntfy/client" + "heckel.io/ntfy/util" + "log" + "os" + "os/exec" + "os/user" + "path/filepath" +) + +const ( + defaultClientRootConfigFile = "/etc/ntfy/client.yml" + defaultClientUserConfigFileRelative = "ntfy/client.yml" + defaultClientConfigFileDescriptionSuffix = `The default config file for all client commands is /etc/ntfy/client.yml (if root user), +or ~/.config/ntfy/client.yml for all other users.` +) + +func runCommandInternal(c *cli.Context, command string, m *client.Message) error { + scriptFile, err := createTmpScript(command) + if err != nil { + return err + } + defer os.Remove(scriptFile) + verbose := c.Bool("verbose") + if verbose { + log.Printf("[%s] Executing: %s (for message: %s)", util.ShortTopicURL(m.TopicURL), command, m.Raw) + } + cmd := exec.Command("sh", "-c", scriptFile) + cmd.Stdin = c.App.Reader + cmd.Stdout = c.App.Writer + cmd.Stderr = c.App.ErrWriter + cmd.Env = envVars(m) + return cmd.Run() +} + +func createTmpScript(command string) (string, error) { + scriptFile := fmt.Sprintf("%s/ntfy-subscribe-%s.sh.tmp", os.TempDir(), util.RandomString(10)) + script := fmt.Sprintf("#!/bin/sh\n%s", command) + if err := os.WriteFile(scriptFile, []byte(script), 0700); err != nil { + return "", err + } + return scriptFile, nil +} + +func defaultConfigFile() string { + u, _ := user.Current() + configFile := defaultClientRootConfigFile + if u.Uid != "0" { + homeDir, _ := os.UserConfigDir() + return filepath.Join(homeDir, defaultClientUserConfigFileRelative) + } + return configFile +} diff --git a/cmd/subscribe_windows.go b/cmd/subscribe_windows.go new file mode 100644 index 00000000..cd441bae --- /dev/null +++ b/cmd/subscribe_windows.go @@ -0,0 +1,48 @@ +package cmd + +import ( + "fmt" + "github.com/urfave/cli/v2" + "heckel.io/ntfy/client" + "heckel.io/ntfy/util" + "log" + "os" + "os/exec" + "path/filepath" +) + +const ( + defaultClientUserConfigFileRelative = "ntfy\\client.yml" + defaultClientConfigFileDescriptionSuffix = `The default config file for all client commands is %AppData%\ntfy\client.yml.` +) + +func runCommandInternal(c *cli.Context, command string, m *client.Message) error { + scriptFile, err := createTmpScript(command) + if err != nil { + return err + } + defer os.Remove(scriptFile) + verbose := c.Bool("verbose") + if verbose { + log.Printf("[%s] Executing: %s (for message: %s)", util.ShortTopicURL(m.TopicURL), command, m.Raw) + } + cmd := exec.Command("cmd.exe", "/Q", "/C", scriptFile) + cmd.Stdin = c.App.Reader + cmd.Stdout = c.App.Writer + cmd.Stderr = c.App.ErrWriter + cmd.Env = envVars(m) + return cmd.Run() +} + +func createTmpScript(command string) (string, error) { + scriptFile := fmt.Sprintf("%s/ntfy-subscribe-%s.bat", os.TempDir(), util.RandomString(10)) + if err := os.WriteFile(scriptFile, []byte(command), 0700); err != nil { + return "", err + } + return scriptFile, nil +} + +func defaultConfigFile() string { + homeDir, _ := os.UserConfigDir() + return filepath.Join(homeDir, defaultClientUserConfigFileRelative) +} diff --git a/docs/publish.md b/docs/publish.md index 69d162d0..e6ffddec 100644 --- a/docs/publish.md +++ b/docs/publish.md @@ -38,7 +38,7 @@ Here's an example showing how to publish a simple message using a POST request: === "PowerShell" ``` powershell - Invoke-RestMethod -Method 'Post' -Uri https://ntfy.sh/topic -Body "Backup successful 😀" -UseBasicParsing + Invoke-RestMethod -Method 'Post' -Uri https://ntfy.sh/mytopic -Body "Backup successful" -UseBasicParsing ``` === "Python" diff --git a/docs/subscribe/cli.md b/docs/subscribe/cli.md index 52e005c0..85468166 100644 --- a/docs/subscribe/cli.md +++ b/docs/subscribe/cli.md @@ -145,12 +145,27 @@ Here's an example config file that subscribes to three different topics, executi fi ``` + === "%AppData%\ntfy\client.yml" + ``` + subscribe: + - topic: echo-this + command: 'echo Message received: %message%' + - topic: calc + command: calc + if: + priority: high,urgent + - topic: toastthis + command: | + notifu /p "a title: %NTFY_TITLE%" /m "%NTFY_MESSAGE%" + exit 0 + ``` + In this example, when `ntfy subscribe --from-config` is executed: * Messages to `echo-this` simply echos to standard out * Messages to `alerts` display as desktop notification for high priority messages using [notify-send](https://manpages.ubuntu.com/manpages/focal/man1/notify-send.1.html) * Messages to `calc` open the gnome calculator 😀 (*because, why not*) -* Messages to `print-temp` execute an inline script and print the CPU temperature +* Messages to `print-temp` execute an inline script and print the CPU temperature (Linux version only) I hope this shows how powerful this command is. Here's a short video that demonstrates the above example: diff --git a/util/util.go b/util/util.go index 3919d3e2..7fc22d09 100644 --- a/util/util.go +++ b/util/util.go @@ -183,11 +183,6 @@ func PriorityString(priority int) (string, error) { } } -// ExpandHome replaces "~" with the user's home directory -func ExpandHome(path string) string { - return os.ExpandEnv(strings.ReplaceAll(path, "~", "$HOME")) -} - // ShortTopicURL shortens the topic URL to be human-friendly, removing the http:// or https:// func ShortTopicURL(s string) string { return strings.TrimPrefix(strings.TrimPrefix(s, "https://"), "http://") diff --git a/util/util_test.go b/util/util_test.go index a3cf4a6c..508e96bf 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -3,7 +3,6 @@ package util import ( "github.com/stretchr/testify/require" "io/ioutil" - "os" "path/filepath" "testing" "time" @@ -75,14 +74,6 @@ func TestSplitNoEmpty(t *testing.T) { require.Equal(t, []string{"tag1", "tag2"}, SplitNoEmpty("tag1,tag2,", ",")) } -func TestExpandHome_WithTilde(t *testing.T) { - require.Equal(t, os.Getenv("HOME")+"/this/is/a/path", ExpandHome("~/this/is/a/path")) -} - -func TestExpandHome_NoTilde(t *testing.T) { - require.Equal(t, "/this/is/an/absolute/path", ExpandHome("/this/is/an/absolute/path")) -} - func TestParsePriority(t *testing.T) { priorities := []string{"", "1", "2", "3", "4", "5", "min", "LOW", " default ", "HIgh", "max", "urgent"} expected := []int{0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 5}