Documentation, fix test, return JSON on publish, add --quiet flag for publish

This commit is contained in:
Philipp Heckel 2021-12-19 21:01:49 -05:00
parent ddd5ce2c21
commit f24855ca9a
10 changed files with 142 additions and 32 deletions

View file

@ -6,6 +6,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strings"
@ -20,6 +21,10 @@ const (
OpenEvent = "open"
)
const (
maxResponseBytes = 4096
)
// Client is the ntfy client that can be used to publish and subscribe to ntfy topics
type Client struct {
Messages chan *Message
@ -63,22 +68,31 @@ func New(config *Config) *Client {
//
// To pass title, priority and tags, check out WithTitle, WithPriority, WithTagsList, WithDelay, WithNoCache,
// WithNoFirebase, and the generic WithHeader.
func (c *Client) Publish(topic, message string, options ...PublishOption) error {
func (c *Client) Publish(topic, message string, options ...PublishOption) (*Message, error) {
topicURL := c.expandTopicURL(topic)
req, _ := http.NewRequest("POST", topicURL, strings.NewReader(message))
for _, option := range options {
if err := option(req); err != nil {
return err
return nil, err
}
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected response %d from server", resp.StatusCode)
return nil, fmt.Errorf("unexpected response %d from server", resp.StatusCode)
}
return err
b, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes))
if err != nil {
return nil, err
}
m, err := toMessage(string(b), topicURL)
if err != nil {
return nil, err
}
return m, nil
}
// Poll queries a topic for all (or a limited set) of messages. Unlike Subscribe, this method only polls for
@ -192,14 +206,21 @@ func performSubscribeRequest(ctx context.Context, msgChan chan *Message, topicUR
defer resp.Body.Close()
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
var m *Message
line := scanner.Text()
if err := json.NewDecoder(strings.NewReader(line)).Decode(&m); err != nil {
m, err := toMessage(scanner.Text(), topicURL)
if err != nil {
return err
}
m.TopicURL = topicURL
m.Raw = line
msgChan <- m
}
return nil
}
func toMessage(s, topicURL string) (*Message, error) {
var m *Message
if err := json.NewDecoder(strings.NewReader(s)).Decode(&m); err != nil {
return nil, err
}
m.TopicURL = topicURL
m.Raw = s
return m, nil
}

View file

@ -2,6 +2,7 @@ package cmd
import (
"errors"
"fmt"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/client"
"strings"
@ -9,17 +10,19 @@ import (
var cmdPublish = &cli.Command{
Name: "publish",
Aliases: []string{"pub", "send", "push", "trigger"},
Aliases: []string{"pub", "send", "trigger"},
Usage: "Send message via a ntfy server",
UsageText: "ntfy send [OPTIONS..] TOPIC MESSAGE",
UsageText: "ntfy send [OPTIONS..] TOPIC [MESSAGE]",
Action: execPublish,
Flags: []cli.Flag{
&cli.StringFlag{Name: "config", Aliases: []string{"c"}, Usage: "client config file"},
&cli.StringFlag{Name: "title", Aliases: []string{"t"}, Usage: "message title"},
&cli.StringFlag{Name: "priority", Aliases: []string{"p"}, Usage: "priority of the message (1=min, 2=low, 3=default, 4=high, 5=max)"},
&cli.StringFlag{Name: "tags", Aliases: []string{"ta"}, Usage: "comma separated list of tags and emojis"},
&cli.StringFlag{Name: "delay", Aliases: []string{"at", "in"}, Usage: "delay/schedule message"},
&cli.StringFlag{Name: "tags", Aliases: []string{"tag", "T"}, Usage: "comma separated list of tags and emojis"},
&cli.StringFlag{Name: "delay", Aliases: []string{"at", "in", "D"}, Usage: "delay/schedule message"},
&cli.BoolFlag{Name: "no-cache", Aliases: []string{"C"}, Usage: "do not cache message server-side"},
&cli.BoolFlag{Name: "no-firebase", Aliases: []string{"F"}, Usage: "do not forward message to Firebase"},
&cli.BoolFlag{Name: "quiet", Aliases: []string{"q"}, Usage: "do print message"},
},
Description: `Publish a message to a ntfy server.
@ -33,12 +36,19 @@ Examples:
ntfy trigger mywebhook # Sending without message, useful for webhooks
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/.`,
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.`,
}
func execPublish(c *cli.Context) error {
if c.NArg() < 1 {
return errors.New("topic missing")
return errors.New("must specify topic, type 'ntfy publish --help' for help")
}
conf, err := loadConfig(c)
if err != nil {
return err
}
title := c.String("title")
priority := c.String("priority")
@ -46,6 +56,7 @@ func execPublish(c *cli.Context) error {
delay := c.String("delay")
noCache := c.Bool("no-cache")
noFirebase := c.Bool("no-firebase")
quiet := c.Bool("quiet")
topic := c.Args().Get(0)
message := ""
if c.NArg() > 1 {
@ -70,10 +81,13 @@ func execPublish(c *cli.Context) error {
if noFirebase {
options = append(options, client.WithNoFirebase())
}
conf, err := loadConfig(c)
cl := client.New(conf)
m, err := cl.Publish(topic, message, options...)
if err != nil {
return err
}
cl := client.New(conf)
return cl.Publish(topic, message, options...)
if !quiet {
fmt.Fprintln(c.App.Writer, strings.TrimSpace(m.Raw))
}
return nil
}

View file

@ -6,7 +6,7 @@ import (
"testing"
)
func TestCLI_Publish_Real_Server(t *testing.T) {
func TestCLI_Publish_Subscribe_Poll_Real_Server(t *testing.T) {
testMessage := util.RandomString(10)
app, _, _, _ := newTestApp()

View file

@ -47,6 +47,10 @@ Examples:
}
func execServe(c *cli.Context) error {
if c.NArg() > 0 {
return errors.New("no arguments expected, see 'ntfy serve --help' for help")
}
// Read all the options
listenHTTP := c.String("listen-http")
listenHTTPS := c.String("listen-https")

View file

@ -26,7 +26,7 @@ var cmdSubscribe = &cli.Command{
},
Flags: []cli.Flag{
&cli.StringFlag{Name: "config", Aliases: []string{"c"}, Usage: "config file `FILE`"},
&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)"},
&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"},
@ -72,7 +72,9 @@ ntfy subscribe --from-config
Examples:
ntfy sub --from-config # Read topics from config file
ntfy sub --config=/my/client.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.`,
}
func execSubscribe(c *cli.Context) error {

View file

@ -1,10 +1,10 @@
# Installing ntfy
The `ntfy` CLI allows you to [publish messages](publish.md), [subscribe to topics](subscribe/cli.md) as well as to
**self-host your own ntfy server**. It's all pretty straight forward. Just install the binary, package or Docker image,
The `ntfy` CLI allows you to [publish messages](publish.md), [subscribe to topics](subscribe/cli.md) as well as to
self-host your own ntfy server. It's all pretty straight forward. Just install the binary, package or Docker image,
configure it and run it. Just like any other software. No fuzz.
!!! info
The following steps are only required if you want to **self-host your own ntfy server** or you want to use the ntfy CLI.
The following steps are only required if you want to **self-host your own ntfy server or you want to use the ntfy CLI**.
If you just want to [send messages using ntfy.sh](publish.md), you don't need to install anything. You can just use
`curl`.

View file

@ -8,6 +8,10 @@
width: unset !important;
}
.admonition {
font-size: .74rem !important;
}
article {
padding-bottom: 50px;
}

View file

@ -1,7 +1,7 @@
# Subscribe via API
You can create and subscribe to a topic either in the [web UI](web.md), via the [phone app](phone.md), or in your own
app or script by subscribing the API. This page describes how to subscribe via API. You may also want to check out the
page that describes how to [publish messages](../publish.md).
You can create and subscribe to a topic in the [web UI](web.md), via the [phone app](phone.md), via the [ntfy CLI](cli.md),
or in your own app or script by subscribing the API. This page describes how to subscribe via API. You may also want to
check out the page that describes how to [publish messages](../publish.md).
The subscription API relies on a simple HTTP GET request with a streaming HTTP response, i.e **you open a GET request and
the connection stays open forever**, sending messages back as they come in. There are three different API endpoints, which
@ -26,6 +26,13 @@ recommended way to subscribe to a topic**. The notable exception is JavaScript,
...
```
=== "ntfy CLI"
```
$ ntfy subcribe disk-alerts
{"id":"hwQ2YpKdmg","time":1635528741,"event":"message","topic":"mytopic","message":"Disk full"}
...
```
=== "HTTP"
``` http
GET /disk-alerts/json HTTP/1.1

View file

@ -1,6 +1,64 @@
# Subscribe via CLI
# Subscribe via ntfy CLI
In addition to subscribing via the [web UI](web.md), the [phone app](phone.md), or the [API](api.md), you can subscribe
to topics via the ntfy CLI. The CLI is included in the same `ntfy` binary that can be used to [self-host a server](../install.md).
!!! info
The `ntfy subscribe` command is incubating. It's very much work in progress.
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. 😀
## Install + configure
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.
If you just want to use [ntfy.sh](https://ntfy.sh), you don't have to change anything. If you **self-host your own server**,
you may want to edit the `default-host` option:
``` yaml
# Base URL used to expand short topic names in the "ntfy publish" and "ntfy subscribe" commands.
# If you self-host a ntfy server, you'll likely want to change this.
#
default-host: https://ntfy.myhost.com
```
## Sending 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:
=== "Simple send"
```
ntfy publish mytopic This is a message
ntfy publish mytopic "This is a message"
ntfy pub mytopic "This is a message"
```
=== "Send with title, priority, and tags"
```
ntfy publish \
--title="Thing sold on eBay" \
--priority=high \
--tags=partying_face \
mytopic \
"Somebody just bought the thing that you sell"
```
=== "Triggering a webhook"
```
ntfy trigger mywebhook
ntfy pub mywebhook
```
## Using the systemd service
```
[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:
(This page is a stub. I'll write something once I'm happy with what the command looks like.)

View file

@ -74,7 +74,7 @@ nav:
- "Subscribing":
- "From your phone": subscribe/phone.md
- "From the Web UI": subscribe/web.md
- "Using the CLI": subscribe/cli.md
- "From the CLI": subscribe/cli.md
- "Using the API": subscribe/api.md
- "Self-hosting":
- "Installation": install.md