forked from mirrors/ntfy
More docs
This commit is contained in:
parent
8900df27c9
commit
712c292183
8 changed files with 275 additions and 89 deletions
2
Makefile
2
Makefile
|
@ -206,7 +206,7 @@ release-check-tags:
|
|||
# Installing targets
|
||||
|
||||
install-amd64: remove-binary
|
||||
sudo cp -a dist/ntfy_amd64_linux_amd64/ntfy /usr/bin/ntfy
|
||||
sudo cp -a dist/ntfy_amd64_linux_amd64_v1/ntfy /usr/bin/ntfy
|
||||
|
||||
install-armv6: remove-binary
|
||||
sudo cp -a dist/ntfy_armv6_linux_armv6/ntfy /usr/bin/ntfy
|
||||
|
|
|
@ -56,6 +56,12 @@ func WithClick(url string) PublishOption {
|
|||
return WithHeader("X-Click", url)
|
||||
}
|
||||
|
||||
// WithActions adds custom user actions to the notification. The value can be either a JSON array or the
|
||||
// simple format definition. See https://ntfy.sh/docs/publish/#action-buttons for details.
|
||||
func WithActions(value string) PublishOption {
|
||||
return WithHeader("X-Actions", value)
|
||||
}
|
||||
|
||||
// WithAttach sets a URL that will be used by the client to download an attachment
|
||||
func WithAttach(attach string) PublishOption {
|
||||
return WithHeader("X-Attach", attach)
|
||||
|
|
|
@ -26,6 +26,7 @@ var cmdPublish = &cli.Command{
|
|||
&cli.StringFlag{Name: "tags", Aliases: []string{"tag", "T"}, EnvVars: []string{"NTFY_TAGS"}, Usage: "comma separated list of tags and emojis"},
|
||||
&cli.StringFlag{Name: "delay", Aliases: []string{"at", "in", "D"}, EnvVars: []string{"NTFY_DELAY"}, Usage: "delay/schedule message"},
|
||||
&cli.StringFlag{Name: "click", Aliases: []string{"U"}, EnvVars: []string{"NTFY_CLICK"}, Usage: "URL to open when notification is clicked"},
|
||||
&cli.StringFlag{Name: "actions", Aliases: []string{"A"}, EnvVars: []string{"NTFY_ACTIONS"}, Usage: "actions JSON array or simple definition"},
|
||||
&cli.StringFlag{Name: "attach", Aliases: []string{"a"}, EnvVars: []string{"NTFY_ATTACH"}, Usage: "URL to send as an external attachment"},
|
||||
&cli.StringFlag{Name: "filename", Aliases: []string{"name", "n"}, EnvVars: []string{"NTFY_FILENAME"}, Usage: "filename for the attachment"},
|
||||
&cli.StringFlag{Name: "file", Aliases: []string{"f"}, EnvVars: []string{"NTFY_FILE"}, Usage: "file to upload as an attachment"},
|
||||
|
@ -72,6 +73,7 @@ func execPublish(c *cli.Context) error {
|
|||
tags := c.String("tags")
|
||||
delay := c.String("delay")
|
||||
click := c.String("click")
|
||||
actions := c.String("actions")
|
||||
attach := c.String("attach")
|
||||
filename := c.String("filename")
|
||||
file := c.String("file")
|
||||
|
@ -112,6 +114,9 @@ func execPublish(c *cli.Context) error {
|
|||
if click != "" {
|
||||
options = append(options, client.WithClick(click))
|
||||
}
|
||||
if actions != "" {
|
||||
options = append(options, client.WithActions(strings.ReplaceAll(actions, "\n", " ")))
|
||||
}
|
||||
if attach != "" {
|
||||
options = append(options, client.WithAttach(attach))
|
||||
}
|
||||
|
|
331
docs/publish.md
331
docs/publish.md
|
@ -789,21 +789,21 @@ The JSON message format closely mirrors the format of the message you can consum
|
|||
(see [JSON message format](subscribe/api.md#json-message-format) for details), but is not exactly identical. Here's an overview of
|
||||
all the supported fields:
|
||||
|
||||
| Field | Required | Type | Example | Description |
|
||||
|------------|----------|----------------------------------|---------------------------------------|-----------------------------------------------------------------------|
|
||||
| `topic` | ✔️ | *string* | `topic1` | Target topic name |
|
||||
| `message` | - | *string* | `Some message` | Message body; set to `triggered` if empty or not passed |
|
||||
| `title` | - | *string* | `Some title` | Message [title](#message-title) |
|
||||
| `tags` | - | *string array* | `["tag1","tag2"]` | List of [tags](#tags-emojis) that may or not map to emojis |
|
||||
| `priority` | - | *int (one of: 1, 2, 3, 4, or 5)* | `4` | Message [priority](#message-priority) with 1=min, 3=default and 5=max |
|
||||
| `actions` | - | *JSON array* | *(see [user actions](#user-actions))* | Custom [user action buttons](#user-actions) for notifications |
|
||||
| `click` | - | *URL* | `https://example.com` | Website opened when notification is [clicked](#click-action) |
|
||||
| `attach` | - | *URL* | `https://example.com/file.jpg` | URL of an attachment, see [attach via URL](#attach-file-from-url) |
|
||||
| `filename` | - | *string* | `file.jpg` | File name of the attachment |
|
||||
| `delay` | - | *string* | `30min`, `9am` | Timestamp or duration for delayed delivery |
|
||||
| `email` | - | *e-mail address* | `phil@example.com` | E-mail address for e-mail notifications |
|
||||
| Field | Required | Type | Example | Description |
|
||||
|------------|----------|----------------------------------|-------------------------------------------|-----------------------------------------------------------------------|
|
||||
| `topic` | ✔️ | *string* | `topic1` | Target topic name |
|
||||
| `message` | - | *string* | `Some message` | Message body; set to `triggered` if empty or not passed |
|
||||
| `title` | - | *string* | `Some title` | Message [title](#message-title) |
|
||||
| `tags` | - | *string array* | `["tag1","tag2"]` | List of [tags](#tags-emojis) that may or not map to emojis |
|
||||
| `priority` | - | *int (one of: 1, 2, 3, 4, or 5)* | `4` | Message [priority](#message-priority) with 1=min, 3=default and 5=max |
|
||||
| `actions` | - | *JSON array* | *(see [actiom buttons](#action-buttons))* | Custom [user action buttons](#action-buttons) for notifications |
|
||||
| `click` | - | *URL* | `https://example.com` | Website opened when notification is [clicked](#click-action) |
|
||||
| `attach` | - | *URL* | `https://example.com/file.jpg` | URL of an attachment, see [attach via URL](#attach-file-from-url) |
|
||||
| `filename` | - | *string* | `file.jpg` | File name of the attachment |
|
||||
| `delay` | - | *string* | `30min`, `9am` | Timestamp or duration for delayed delivery |
|
||||
| `email` | - | *e-mail address* | `phil@example.com` | E-mail address for e-mail notifications |
|
||||
|
||||
## User actions
|
||||
## Action buttons
|
||||
You can add action buttons to notifications to allow yourself to react to a notification directly. This is incredibly
|
||||
useful and has countless applications. As of today, the following actions are supported:
|
||||
|
||||
|
@ -812,6 +812,9 @@ useful and has countless applications. As of today, the following actions are su
|
|||
when the action button is tapped
|
||||
* [`http`](#send-http-request): Sends HTTP POST/GET/PUT request when the action button is tapped
|
||||
|
||||
To define the user actions, you can either pass the `actions` field as part of the JSON body (if you're
|
||||
[publishing via JSON](#publish-as-json)), or use the `X-Actions` header (or any of its aliases: `Actions`, `Action`).
|
||||
|
||||
Here's an example of what that a notification with actions can look like:
|
||||
|
||||
<figure markdown>
|
||||
|
@ -819,12 +822,91 @@ Here's an example of what that a notification with actions can look like:
|
|||
<figcaption>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</figcaption>
|
||||
</figure>
|
||||
|
||||
Using the `X-Actions` header and the **simple format** (details see below), you can create the above notification like
|
||||
this. This format is much easier to write, but less powerful:
|
||||
|
||||
=== "Command line (curl)"
|
||||
```
|
||||
curl \
|
||||
-d "You left the house. Turn down the A/C?" \
|
||||
-H "Actions: view, Open portal, https://home.nest.com/; \
|
||||
http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65" \
|
||||
ntfy.sh/myhome
|
||||
```
|
||||
|
||||
=== "ntfy CLI"
|
||||
```
|
||||
ntfy publish \
|
||||
--actions="view, Open portal, https://home.nest.com/; \
|
||||
http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65" \
|
||||
myhome \
|
||||
"You left the house. Turn down the A/C?"
|
||||
```
|
||||
|
||||
=== "HTTP"
|
||||
``` http
|
||||
POST /myhome HTTP/1.1
|
||||
Host: ntfy.sh
|
||||
Actions: view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65
|
||||
|
||||
You left the house. Turn down the A/C?
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
``` javascript
|
||||
fetch('https://ntfy.sh/myhome', {
|
||||
method: 'POST',
|
||||
body: 'You left the house. Turn down the A/C?',
|
||||
headers: {
|
||||
'Actions': 'view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
``` go
|
||||
req, _ := http.NewRequest("POST", "https://ntfy.sh/myhome", strings.NewReader("You left the house. Turn down the A/C?"))
|
||||
req.Header.Set("Actions", "view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65")
|
||||
http.DefaultClient.Do(req)
|
||||
```
|
||||
|
||||
=== "PowerShell"
|
||||
``` powershell
|
||||
$uri = "https://ntfy.sh/myhome"
|
||||
$headers = @{ Actions="view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65" }
|
||||
$body = "You left the house. Turn down the A/C?"
|
||||
Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $body -UseBasicParsing
|
||||
```
|
||||
|
||||
=== "Python"
|
||||
``` python
|
||||
requests.post("https://ntfy.sh/myhome",
|
||||
data="You left the house. Turn down the A/C?",
|
||||
headers={ "Actions": "view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65" })
|
||||
```
|
||||
|
||||
=== "PHP"
|
||||
``` php-inline
|
||||
file_get_contents('https://ntfy.sh/reddit_alerts', false, stream_context_create([
|
||||
'http' => [
|
||||
'method' => 'POST',
|
||||
'header' =>
|
||||
"Content-Type: text/plain\r\n" .
|
||||
"Actions: view, Open portal, https://home.nest.com/; http, Turn down, https://api.nest.com/device/XZ1D2, body=target_temp_f=65",
|
||||
'content' => 'You left the house. Turn down the A/C?'
|
||||
]
|
||||
]));
|
||||
```
|
||||
|
||||
Alternatively, you can define actions as **JSON array** (details see below), and pass them as part of the JSON body
|
||||
(see [publish as JSON](#publish-as-json)):
|
||||
|
||||
=== "Command line (curl)"
|
||||
```
|
||||
curl ntfy.sh \
|
||||
-d '{
|
||||
"topic": "myhome",
|
||||
"message": "You seem to have left the house. Want to turn down the A/C?",
|
||||
"message": "You left the house. Turn down the A/C?",
|
||||
"actions": [
|
||||
{
|
||||
"action": "view",
|
||||
|
@ -834,39 +916,54 @@ Here's an example of what that a notification with actions can look like:
|
|||
{
|
||||
"action": "http",
|
||||
"label": "Turn down",
|
||||
"method": "POST",
|
||||
"url": "https://developer-api.nest.com/devices/thermostats/XZA124D",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ...",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"body": "{\"target_temperature_f\": 65}"
|
||||
},
|
||||
{
|
||||
"action": "broadcast",
|
||||
"label": "Enter deep sleep 💤",
|
||||
"extras": {
|
||||
"command": "deepsleep"
|
||||
}
|
||||
"url": "https://api.nest.com/device/XZ1D2",
|
||||
"body": "target_temp_f=65"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
=== "ntfy CLI"
|
||||
```
|
||||
ntfy publish \
|
||||
--actions '[
|
||||
{
|
||||
"action": "view",
|
||||
"label": "Open portal",
|
||||
"url": "https://home.nest.com/"
|
||||
},
|
||||
{
|
||||
"action": "http",
|
||||
"label": "Turn down",
|
||||
"url": "https://api.nest.com/device/XZ1D2",
|
||||
"body": "target_temp_f=65"
|
||||
}
|
||||
]' \
|
||||
myhome \
|
||||
"You left the house. Turn down the A/C?"
|
||||
```
|
||||
|
||||
=== "HTTP"
|
||||
``` http
|
||||
POST / HTTP/1.1
|
||||
Host: ntfy.sh
|
||||
|
||||
{
|
||||
"topic": "mytopic",
|
||||
"message": "Disk space is low at 5.1 GB",
|
||||
"title": "Low disk space alert",
|
||||
"tags": ["warning","cd"],
|
||||
"priority": 4,
|
||||
"attach": "https://filesrv.lan/space.jpg",
|
||||
"filename": "diskspace.jpg",
|
||||
"click": "https://homecamera.lan/xasds1h2xsSsa/"
|
||||
"topic": "myhome",
|
||||
"message": "You left the house. Turn down the A/C?",
|
||||
"actions": [
|
||||
{
|
||||
"action": "view",
|
||||
"label": "Open portal",
|
||||
"url": "https://home.nest.com/"
|
||||
},
|
||||
{
|
||||
"action": "http",
|
||||
"label": "Turn down",
|
||||
"url": "https://api.nest.com/device/XZ1D2",
|
||||
"body": "target_temp_f=65"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -875,14 +972,21 @@ Here's an example of what that a notification with actions can look like:
|
|||
fetch('https://ntfy.sh', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
"topic": "mytopic",
|
||||
"message": "Disk space is low at 5.1 GB",
|
||||
"title": "Low disk space alert",
|
||||
"tags": ["warning","cd"],
|
||||
"priority": 4,
|
||||
"attach": "https://filesrv.lan/space.jpg",
|
||||
"filename": "diskspace.jpg",
|
||||
"click": "https://homecamera.lan/xasds1h2xsSsa/"
|
||||
topic: "myhome",
|
||||
message": "You left the house. Turn down the A/C?",
|
||||
actions: [
|
||||
{
|
||||
action: "view",
|
||||
label: "Open portal",
|
||||
url: "https://home.nest.com/"
|
||||
},
|
||||
{
|
||||
action: "http",
|
||||
label: "Turn down",
|
||||
url: "https://api.nest.com/device/XZ1D2",
|
||||
body: "target_temp_f=65"
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
```
|
||||
|
@ -890,18 +994,24 @@ Here's an example of what that a notification with actions can look like:
|
|||
=== "Go"
|
||||
``` go
|
||||
// You should probably use json.Marshal() instead and make a proper struct,
|
||||
// or even just use req.Header.Set() like in the other examples, but for the
|
||||
// sake of the example, this is easier.
|
||||
// but for the sake of the example, this is easier.
|
||||
|
||||
body := `{
|
||||
"topic": "mytopic",
|
||||
"message": "Disk space is low at 5.1 GB",
|
||||
"title": "Low disk space alert",
|
||||
"tags": ["warning","cd"],
|
||||
"priority": 4,
|
||||
"attach": "https://filesrv.lan/space.jpg",
|
||||
"filename": "diskspace.jpg",
|
||||
"click": "https://homecamera.lan/xasds1h2xsSsa/"
|
||||
"topic": "myhome",
|
||||
"message": "You left the house. Turn down the A/C?",
|
||||
"actions": [
|
||||
{
|
||||
"action": "view",
|
||||
"label": "Open portal",
|
||||
"url": "https://home.nest.com/"
|
||||
},
|
||||
{
|
||||
"action": "http",
|
||||
"label": "Turn down",
|
||||
"url": "https://api.nest.com/device/XZ1D2",
|
||||
"body": "target_temp_f=65"
|
||||
}
|
||||
]
|
||||
}`
|
||||
req, _ := http.NewRequest("POST", "https://ntfy.sh/", strings.NewReader(body))
|
||||
http.DefaultClient.Do(req)
|
||||
|
@ -911,15 +1021,22 @@ Here's an example of what that a notification with actions can look like:
|
|||
``` powershell
|
||||
$uri = "https://ntfy.sh"
|
||||
$body = @{
|
||||
"topic"="powershell"
|
||||
"title"="Low disk space alert"
|
||||
"message"="Disk space is low at 5.1 GB"
|
||||
"priority"=4
|
||||
"attach"="https://filesrv.lan/space.jpg"
|
||||
"filename"="diskspace.jpg"
|
||||
"tags"=@("warning","cd")
|
||||
"click"= "https://homecamera.lan/xasds1h2xsSsa/"
|
||||
} | ConvertTo-Json
|
||||
"topic"="myhome"
|
||||
"message"="You left the house. Turn down the A/C?"
|
||||
"actions"=@(
|
||||
@{
|
||||
"action"="view"
|
||||
"label"="Open portal"
|
||||
"url"="https://home.nest.com/"
|
||||
},
|
||||
@{
|
||||
"action"="http",
|
||||
"label"="Turn down"
|
||||
"url"="https://api.nest.com/device/XZ1D2"
|
||||
"body"="target_temp_f=65"
|
||||
}
|
||||
)
|
||||
} | ConvertTo-Json
|
||||
Invoke-RestMethod -Method 'Post' -Uri $uri -Body $body -ContentType "application/json" -UseBasicParsing
|
||||
```
|
||||
|
||||
|
@ -927,14 +1044,21 @@ Here's an example of what that a notification with actions can look like:
|
|||
``` python
|
||||
requests.post("https://ntfy.sh/",
|
||||
data=json.dumps({
|
||||
"topic": "mytopic",
|
||||
"message": "Disk space is low at 5.1 GB",
|
||||
"title": "Low disk space alert",
|
||||
"tags": ["warning","cd"],
|
||||
"priority": 4,
|
||||
"attach": "https://filesrv.lan/space.jpg",
|
||||
"filename": "diskspace.jpg",
|
||||
"click": "https://homecamera.lan/xasds1h2xsSsa/"
|
||||
"topic": "myhome",
|
||||
"message": "You left the house. Turn down the A/C?",
|
||||
"actions": [
|
||||
{
|
||||
"action": "view",
|
||||
"label": "Open portal",
|
||||
"url": "https://home.nest.com/"
|
||||
},
|
||||
{
|
||||
"action": "http",
|
||||
"label": "Turn down",
|
||||
"url": "https://api.nest.com/device/XZ1D2",
|
||||
"body": "target_temp_f=65"
|
||||
}
|
||||
]
|
||||
})
|
||||
)
|
||||
```
|
||||
|
@ -946,31 +1070,57 @@ Here's an example of what that a notification with actions can look like:
|
|||
'method' => 'POST',
|
||||
'header' => "Content-Type: application/json",
|
||||
'content' => json_encode([
|
||||
"topic": "mytopic",
|
||||
"message": "Disk space is low at 5.1 GB",
|
||||
"title": "Low disk space alert",
|
||||
"tags": ["warning","cd"],
|
||||
"priority": 4,
|
||||
"attach": "https://filesrv.lan/space.jpg",
|
||||
"filename": "diskspace.jpg",
|
||||
"click": "https://homecamera.lan/xasds1h2xsSsa/"
|
||||
"topic": "myhome",
|
||||
"message": "You left the house. Turn down the A/C?",
|
||||
"actions": [
|
||||
[
|
||||
"action": "view",
|
||||
"label": "Open portal",
|
||||
"url": "https://home.nest.com/"
|
||||
],
|
||||
[
|
||||
"action": "http",
|
||||
"label": "Turn down",
|
||||
"url": "https://api.nest.com/device/XZ1D2",
|
||||
"headers": [
|
||||
"Authorization": "Bearer ..."
|
||||
],
|
||||
"body": "target_temp_f=65"
|
||||
]
|
||||
]
|
||||
])
|
||||
]
|
||||
]));
|
||||
```
|
||||
|
||||
**Simple format syntax:**
|
||||
|
||||
Generally, the `X-Actions` header is formatted like this:
|
||||
```
|
||||
Actions: <action>, <label>, <params, ...>
|
||||
```
|
||||
or:
|
||||
```
|
||||
Actions: action=<action>, label=<label>, param1=..., param2=..., ...
|
||||
```
|
||||
|
||||
An `action` is either [`view`](#open-websiteapp), [`broadcast`](#send-android-broadcast), or [`http`](#send-http-request),
|
||||
and the `label` defines the button text. The other parameters depend on the action itself.
|
||||
|
||||
| Field | Required | Type | Example | Description |
|
||||
|----------|----------|----------------------------|-----------------|------------------------------------------------|
|
||||
| `action` | ✔️ | *view, broadcast, or http* | `view` | Action type |
|
||||
| `label` | ✔️ | *string* | `Turn on light` | Label of the action button in the notification |
|
||||
|
||||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx
|
||||
|
||||
|
||||
### Open website/app
|
||||
The `view` action opens a website or app when the action button is tapped, e.g. a browser, a Google Maps location, or
|
||||
even a deep link into Twitter or a show ntfy topic.
|
||||
|
||||
XXXXXXXXXXXXXXXXXXx
|
||||
|
||||
|
||||
### Send Android broadcast
|
||||
The `broadcast` action sends an [Android broadcast](https://developer.android.com/guide/components/broadcasts) intent
|
||||
when the action button is tapped. This allows integration into automation apps such as [MacroDroid](https://play.google.com/store/apps/details?id=com.arlosoft.macrodroid)
|
||||
|
@ -978,10 +1128,15 @@ or [Tasker](https://play.google.com/store/apps/details?id=net.dinglisch.android.
|
|||
you can do everything your phone is capable of. Examples include taking pictures, launching/killing apps, change device
|
||||
settings, write/read files, etc.
|
||||
|
||||
XXXXXXXXXXXXXXxx
|
||||
|
||||
|
||||
### Send HTTP request
|
||||
The `http` action sends a HTTP POST/GET/PUT request when the action button is tapped. You can use this to trigger REST APIs
|
||||
for whatever systems you have, e.g. opening the garage door, or turning on/off lights.
|
||||
|
||||
XXXXXXXXXXXXXXXXXXXXx
|
||||
|
||||
=== "`view` action"
|
||||
``` json
|
||||
{
|
||||
|
@ -1079,6 +1234,18 @@ You can define which URL to open when a notification is clicked. This may be use
|
|||
to a Zabbix alert or a transaction that you'd like to provide the deep-link for. Tapping the notification will open
|
||||
the web browser (or the app) and open the website.
|
||||
|
||||
To define a click action for the notification, pass a URL as the value of the `X-Click` header (or its aliase `Click`).
|
||||
If you pass a website URL (`http://` or `https://`) the web browser will open. If you pass another URI that can be handled
|
||||
by another app, the responsible app may open.
|
||||
|
||||
Examples:
|
||||
|
||||
* `http://` or `https://` will open your browser (or an app if it registered for a URL)
|
||||
* `mailto:` links will open your mail app
|
||||
* `geo:` links will open Google Maps (or your maps application)
|
||||
* `ntfy://` links will open ntfy (see [ntfy:// links](subscribe/phone.md#ntfy-links))
|
||||
* ...
|
||||
|
||||
Here's an example that will open Reddit when the notification is clicked:
|
||||
|
||||
=== "Command line (curl)"
|
||||
|
@ -1751,7 +1918,7 @@ and can be passed as **HTTP headers** or **query parameters in the URL**. They a
|
|||
| `X-Priority` | `Priority`, `prio`, `p` | [Message priority](#message-priority) |
|
||||
| `X-Tags` | `Tags`, `Tag`, `ta` | [Tags and emojis](#tags-emojis) |
|
||||
| `X-Delay` | `Delay`, `X-At`, `At`, `X-In`, `In` | Timestamp or duration for [delayed delivery](#scheduled-delivery) |
|
||||
| `X-Actions` | `Actions`, `Action` | JSON array or short format of [user actions](#user-actions) |
|
||||
| `X-Actions` | `Actions`, `Action` | JSON array or short format of [user actions](#action-buttons) |
|
||||
| `X-Click` | `Click` | URL to open when [notification is clicked](#click-action) |
|
||||
| `X-Attach` | `Attach`, `a` | URL to send as an [attachment](#attachments), as an alternative to PUT/POST-ing an attachment |
|
||||
| `X-Filename` | `Filename`, `file`, `f` | Optional [attachment](#attachments) filename, as it appears in the client |
|
||||
|
|
3
go.mod
3
go.mod
|
@ -24,6 +24,8 @@ require (
|
|||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require github.com/pkg/errors v0.9.1
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.100.2 // indirect
|
||||
cloud.google.com/go/compute v1.5.0 // indirect
|
||||
|
@ -35,7 +37,6 @@ require (
|
|||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
|
|
|
@ -39,7 +39,7 @@ var (
|
|||
errHTTPBadRequestAttachmentsExpiryBeforeDelivery = &errHTTP{40015, http.StatusBadRequest, "invalid request: attachment expiry before delayed delivery date", "https://ntfy.sh/docs/publish/#scheduled-delivery"}
|
||||
errHTTPBadRequestWebSocketsUpgradeHeaderMissing = &errHTTP{40016, http.StatusBadRequest, "invalid request: client not using the websocket protocol", "https://ntfy.sh/docs/subscribe/api/#websockets"}
|
||||
errHTTPBadRequestJSONInvalid = &errHTTP{40017, http.StatusBadRequest, "invalid request: request body must be message JSON", "https://ntfy.sh/docs/publish/#publish-as-json"}
|
||||
errHTTPBadRequestActionsInvalid = &errHTTP{40018, http.StatusBadRequest, "invalid request: actions are invalid format", "https://ntfy.sh/docs/publish/#user-actions"}
|
||||
errHTTPBadRequestActionsInvalid = &errHTTP{40018, http.StatusBadRequest, "invalid request: actions are invalid format", "https://ntfy.sh/docs/publish/#action-buttons"}
|
||||
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", ""}
|
||||
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication"}
|
||||
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication"}
|
||||
|
|
|
@ -93,7 +93,6 @@ const (
|
|||
emptyMessageBody = "triggered" // Used if message body is empty
|
||||
defaultAttachmentMessage = "You received a file: %s" // Used if message body is empty, and there is an attachment
|
||||
encodingBase64 = "base64"
|
||||
actionIDLength = 10
|
||||
)
|
||||
|
||||
// WebSocket constants
|
||||
|
|
|
@ -9,6 +9,11 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
actionIDLength = 10
|
||||
actionsMax = 3
|
||||
)
|
||||
|
||||
func readBoolParam(r *http.Request, defaultValue bool, names ...string) bool {
|
||||
value := strings.ToLower(readParam(r, names...))
|
||||
if value == "" {
|
||||
|
@ -63,12 +68,15 @@ func parseActions(s string) (actions []*action, err error) {
|
|||
}
|
||||
|
||||
// Validate
|
||||
if len(actions) > actionsMax {
|
||||
return nil, fmt.Errorf("too many actions, only %d allowed", actionsMax)
|
||||
}
|
||||
for _, action := range actions {
|
||||
if !util.InStringList([]string{"view", "broadcast", "http"}, action.Action) {
|
||||
return nil, fmt.Errorf("cannot parse actions: action '%s' unknown", action.Action)
|
||||
} else if action.Label == "" {
|
||||
return nil, fmt.Errorf("cannot parse actions: label must be set")
|
||||
} else if util.InStringList([]string{"view", "http"}, action.Action) && action.URL != "" {
|
||||
} else if util.InStringList([]string{"view", "http"}, action.Action) && action.URL == "" {
|
||||
return nil, fmt.Errorf("parameter 'url' is required for action '%s'", action.Action)
|
||||
}
|
||||
}
|
||||
|
@ -99,8 +107,8 @@ func parseActionsFromSimple(s string) ([]*action, error) {
|
|||
newAction.Action = value
|
||||
} else if key == "" && i == 1 {
|
||||
newAction.Label = value
|
||||
} else if key == "" && i == 2 {
|
||||
newAction.URL = value // This works, because both "http" and "view" need a URL
|
||||
} else if key == "" && util.InStringList([]string{"view", "http"}, newAction.Action) && i == 2 {
|
||||
newAction.URL = value
|
||||
} else if key != "" {
|
||||
switch strings.ToLower(key) {
|
||||
case "action":
|
||||
|
|
Loading…
Reference in a new issue