Skip to content

Commit

Permalink
Docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Philipp Heckel committed Dec 21, 2021
1 parent edb6b0c commit 85b4abd
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 55 deletions.
27 changes: 19 additions & 8 deletions client/client.yml
Expand Up @@ -5,14 +5,25 @@
#
# default-host: https://ntfy.sh

# Subscriptions to topics and their actions. This option is only used by the "ntfy subscribe --from-config"
# command.
# Subscriptions to topics and their actions. This option is primarily used by the systemd service,
# or if you cann "ntfy subscribe --from-config" directly.
#
# Here's a (hopefully self-explanatory) example:
# subscribe:
# - topic: mytopic
# command: /usr/local/bin/mytopic-triggered.sh
# - topic: myserver.com/anothertopic
# command: 'echo "$message"'
# Example:
# subscribe:
# - topic: mytopic
# command: /usr/local/bin/mytopic-triggered.sh
# - topic: myserver.com/anothertopic
# command: 'echo "$message"'
#
# Variables:
# Variable Aliases Description
# --------------- --------------- -----------------------------------
# $NTFY_ID $id Unique message ID
# $NTFY_TIME $time Unix timestamp of the message delivery
# $NTFY_TOPIC $topic Topic name
# $NTFY_MESSAGE $message, $m Message body
# $NTFY_TITLE $title, $t Message title
# $NTFY_PRIORITY $priority, $p Message priority (1=min, 5=max)
# $NTFY_TAGS $tags, $ta Message tags (comma separated list)
#
# subscribe:
8 changes: 4 additions & 4 deletions client/config.go
Expand Up @@ -7,12 +7,12 @@ const (

// Config is the config struct for a Client
type Config struct {
DefaultHost string
DefaultHost string `yaml:"default-host"`
Subscribe []struct {
Topic string
Command string
Topic string `yaml:"topic"`
Command string `yaml:"command"`
// If []map[string]string TODO This would be cool
}
} `yaml:"subscribe"`
}

// NewConfig creates a new Config struct for a Client
Expand Down
19 changes: 7 additions & 12 deletions cmd/subscribe.go
Expand Up @@ -20,11 +20,6 @@ var cmdSubscribe = &cli.Command{
Usage: "Subscribe to one or more topics on a ntfy server",
UsageText: "ntfy subscribe [OPTIONS..] [TOPIC]",
Action: execSubscribe,
OnUsageError: func(context *cli.Context, err error, isSubcommand bool) error {
println("ee")
return nil
},

Flags: []cli.Flag{
&cli.StringFlag{Name: "config", Aliases: []string{"c"}, Usage: "client config file"},
&cli.StringFlag{Name: "since", Aliases: []string{"s"}, Usage: "return events since `SINCE` (Unix timestamp, or all)"},
Expand Down Expand Up @@ -109,26 +104,26 @@ func execSubscribe(c *cli.Context) error {

// Execute poll or subscribe
if poll {
return execPoll(c, cl, conf, topic, command, options...)
return doPoll(c, cl, conf, topic, command, options...)
}
return execSubscribeInternal(c, cl, conf, topic, command, options...)
return doSubscribe(c, cl, conf, topic, command, options...)
}

func execPoll(c *cli.Context, cl *client.Client, conf *client.Config, topic, command string, options ...client.SubscribeOption) error {
func doPoll(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 {
if err := doPollSingle(c, cl, s.Topic, s.Command, options...); err != nil {
return err
}
}
if topic != "" {
if err := execPollSingle(c, cl, topic, command, options...); err != nil {
if err := doPollSingle(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 {
func doPollSingle(c *cli.Context, cl *client.Client, topic, command string, options ...client.SubscribeOption) error {
messages, err := cl.Poll(topic, options...)
if err != nil {
return err
Expand All @@ -139,7 +134,7 @@ func execPollSingle(c *cli.Context, cl *client.Client, topic, command string, op
return nil
}

func execSubscribeInternal(c *cli.Context, cl *client.Client, conf *client.Config, topic, command string, options ...client.SubscribeOption) error {
func doSubscribe(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...)
Expand Down
Binary file added docs/static/img/cli-subscribe-video-3.webm
Binary file not shown.
124 changes: 93 additions & 31 deletions docs/subscribe/cli.md
Expand Up @@ -5,10 +5,10 @@ to topics via the ntfy CLI. The CLI is included in the same `ntfy` binary that c
!!! info
The **ntfy CLI is not required to send or receive messages**. You can instead [send messages with curl](../publish.md),
and even use it to [subscribe to topics](api.md). It may be a little more convenient to use the ntfy CLI than writing
your own script. Or it may not be. It all depends on the use case. 😀
your own script. It all depends on the use case. 😀

## Install + configure
To install the ntfy CLI, simply follow the steps outlined on the [install page](../install.md). The ntfy server and
To install the ntfy CLI, simply **follow the steps outlined on the [install page](../install.md)**. The ntfy server and
client are the same binary, so it's all very convenient. After installing, you can (optionally) configure the client
by creating `~/.config/ntfy/client.yml` (for the non-root user), or `/etc/ntfy/client.yml` (for the root user). You
can find a [skeleton config](https://github.com/binwiederhier/ntfy/blob/main/client/client.yml) on GitHub.
Expand All @@ -23,7 +23,7 @@ you may want to edit the `default-host` option:
default-host: https://ntfy.myhost.com
```

## Publish using the ntfy CLI
## Publish messages
You can send messages with the ntfy CLI using the `ntfy publish` command (or any of its aliases `pub`, `send` or
`trigger`). There are a lot of examples on the page about [publishing messages](../publish.md), but here are a few
quick ones:
Expand Down Expand Up @@ -56,36 +56,52 @@ quick ones:
ntfy pub mywebhook
```

## Subscribe using the ntfy CLI
## Subscribe to topics
You can subscribe to topics using `ntfy subscribe`. Depending on how it is called, this command
will either print or execute a command for every arriving message. There are a few different ways
in which the command can be run:

### Stream messages and print JSON
If run like this `ntfy subscribe TOPIC`, the command prints the JSON representation of every incoming
message. This is useful when you have a command that wants to stream-read incoming JSON messages.
Unless `--poll` is passed, this command stays open forever.
### Stream messages as JSON
```
ntfy subscribe TOPIC
```
If you run the command like this, it prints the JSON representation of every incoming message. This is useful
when you have a command that wants to stream-read incoming JSON messages. Unless `--poll` is passed, this command
stays open forever.

```
$ ntfy sub mytopic
{"id":"nZ8PjH5oox","time":1639971913,"event":"message","topic":"mytopic","message":"hi there"}
{"id":"sekSLWTujn","time":1639972063,"event":"message","topic":"mytopic","tags":["warning","skull"],"message":"Oh no, something went wrong"}
{"id":"sekSLWTujn","time":1639972063,"event":"message","topic":"mytopic",priority:5,"message":"Oh no!"}
```

<figure>
<video controls muted autoplay loop width="650" src="../../static/img/cli-subscribe-video-1.mp4"></video>
<figcaption>Subscribe in JSON mode</figcaption>
</figure>

### Execute a command for every incoming message
If run like this `ntfy subscribe TOPIC COMMAND`, a COMMAND is executed for every incoming messages.
The message fields are passed to the command as environment variables and can be used in scripts:
### Run command for every message
```
ntfy subscribe TOPIC COMMAND
```
If you run it like this, a COMMAND is executed for every incoming messages. Here are a few
examples:

```
ntfy sub mytopic 'notify-send "$m"'
ntfy sub topic1 /my/script.sh
ntfy sub topic1 'echo "Message $m was received. Its title was $t and it had priority $p'
```

<figure>
<video controls muted autoplay loop width="650" src="../../static/img/cli-subscribe-video-2.webm"></video>
<figcaption>Execute command on incoming messages</figcaption>
</figure>

The message fields are passed to the command as environment variables and can be used in scripts. Note that since
these are environment variables, you typically don't have to worry about quoting too much, as long as you enclose them
in double-quotes, you should be fine:

| Variable | Aliases | Description |
|---|---|---
| `$NTFY_ID` | `$id` | Unique message ID |
Expand All @@ -96,32 +112,78 @@ The message fields are passed to the command as environment variables and can be
| `$NTFY_PRIORITY` | `$priority`, `$p` | Message priority (1=min, 5=max) |
| `$NTFY_TAGS` | `$tags`, `$ta` | Message tags (comma separated list) |

Examples:
ntfy sub mytopic 'notify-send "$m"' # Execute command for incoming messages
ntfy sub topic1 /my/script.sh # Execute script for incoming messages

### Using a config file
### Subscribing to multiple topics
```
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).
```
To subscribe to multiple topics at once, and run different commands for each one, you can use `ntfy subscribe --from-config`,
which will read the `subscribe` config from the config file. Please also check out the [ntfy-client systemd service](#using-the-systemd-service).

Here's an example config file that subscribes to three different topics, executing a different command for each of them:

=== "~/.config/ntfy/client.yml"
```yaml
subscribe:
- topic: echo-this
command: 'echo "Message received: $message"'
- topic: get-temp
command: |
temp="$(sensors | awk '/Package/ { print $4 }')"
ntfy publish --quiet temp "$temp";
echo "CPU temp is $temp; published to topic 'temp'"
- topic: alerts
command: notify-send "$m"
- topic: calc
command: 'gnome-calculator 2>/dev/null &'
```

In this example, when `ntfy subscribe --from-config` is executed:

Examples:
ntfy sub --from-config # Read topics from config file
ntfy sub --config=/my/client.yml --from-config # Read topics from alternate config file
* Messages to topic `echo-this` will be simply echoed to standard out
* Messages to topic `get-temp` will publish the CPU core temperature to topic `temp`
* Messages to topic `alerts` will be displayed as desktop notification using `notify-send`
* And messages to topic `calc` will open the gnome calculator 😀 (*because, why not*)

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.
I hope this shows how powerful this command is. Here's a short video that demonstrates the above example:

<figure>
<video controls muted autoplay loop width="650" src="../../static/img/cli-subscribe-video-3.webm"></video>
<figcaption>Execute all the things</figcaption>
</figure>

### Using the systemd service
You can use the `ntfy-client` systemd service (see [ntfy-client.service](https://github.com/binwiederhier/ntfy/blob/main/client/ntfy-client.service))
to subscribe to multiple topics just like in the example above. The service is automatically installed (but not started)
if you install the deb/rpm package. To configure it, simply edit `/etc/ntfy/client.yml` and run `sudo systemctl restart ntfy-client`.

!!! info
The `ntfy-client.service` runs as user `ntfy`, meaning that typical Linux permission restrictions apply. See below
for how to fix this.

If it runs on your personal desktop machine, you may want to override the service user/group (`User=` and `Group=`), and
adjust the `DISPLAY` and DBUS environment variables. This will allow you to run commands in your X session as the primary
machine user.

You can either manually override these systemd service entries with `sudo systemctl edit ntfy-client`, and add this
(assuming your user is `pheckel`):

=== "/etc/systemd/system/ntfy-client.service.d/override.conf"
```
[Service]
User=pheckel
Group=pheckel
Environment="DISPLAY=:0" "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus"
```
Or you can run the following script that creates this override config for you:

```
sudo sh -c 'cat > /etc/systemd/system/ntfy-client.service.d/override.conf' <<EOF
[Service]
User=pheckel
Group=pheckel
Environment="DISPLAY=:0" "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus"
```

Here's an example for a complete client config for a self-hosted server:
User=$USER
Group=$USER
Environment="DISPLAY=:0" "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus"
EOF
sudo systemctl daemon-reload
sudo systemctl restart ntfy-client
```

0 comments on commit 85b4abd

Please sign in to comment.