Publish as JSON
parent
37d4d5d647
commit
8fcc40942f
145
docs/publish.md
145
docs/publish.md
|
@ -499,7 +499,7 @@ Here are a few examples (assuming today's date is **12/10/2021, 9am, Eastern Tim
|
||||||
</td>
|
</td>
|
||||||
</tr></table>
|
</tr></table>
|
||||||
|
|
||||||
## Webhooks (Send via GET)
|
## Webhooks (publish via GET)
|
||||||
In addition to using PUT/POST, you can also send to topics via simple HTTP GET requests. This makes it easy to use
|
In addition to using PUT/POST, you can also send to topics via simple HTTP GET requests. This makes it easy to use
|
||||||
a ntfy topic as a [webhook](https://en.wikipedia.org/wiki/Webhook), or if your client has limited HTTP support (e.g.
|
a ntfy topic as a [webhook](https://en.wikipedia.org/wiki/Webhook), or if your client has limited HTTP support (e.g.
|
||||||
like the [MacroDroid](https://play.google.com/store/apps/details?id=com.arlosoft.macrodroid) Android app).
|
like the [MacroDroid](https://play.google.com/store/apps/details?id=com.arlosoft.macrodroid) Android app).
|
||||||
|
@ -592,6 +592,141 @@ Here's an example with a custom message, tags and a priority:
|
||||||
file_get_contents('https://ntfy.sh/mywebhook/publish?message=Webhook+triggered&priority=high&tags=warning,skull');
|
file_get_contents('https://ntfy.sh/mywebhook/publish?message=Webhook+triggered&priority=high&tags=warning,skull');
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Publish as JSON
|
||||||
|
For some integrations with other tools (e.g. [Jellyfin](https://jellyfin.org/), [overseerr](https://overseerr.dev/)),
|
||||||
|
adding custom headers to HTTP requests may be tricky or impossible, so ntfy also allows publishing the entire message
|
||||||
|
as JSON in the request body.
|
||||||
|
|
||||||
|
To publish as JSON, simple PUT/POST the JSON object directly to the ntfy root URL. The message format is described below
|
||||||
|
the example.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
To publish as JSON, you must **PUT/POST to the ntfy root URL**, not to the topic URL. Be sure to check that you're
|
||||||
|
POST-ing to `https://ntfy.sh/` (correct), and not to `https://ntfy.sh/mytopic` (incorrect).
|
||||||
|
|
||||||
|
Here's an example using all supported parameters. The `topic` parameter is the only required one:
|
||||||
|
|
||||||
|
=== "Command line (curl)"
|
||||||
|
```
|
||||||
|
curl ntfy.sh \
|
||||||
|
-d '{
|
||||||
|
"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/"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "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/"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "JavaScript"
|
||||||
|
``` javascript
|
||||||
|
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/"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "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.
|
||||||
|
|
||||||
|
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/"
|
||||||
|
}`
|
||||||
|
req, _ := http.NewRequest("POST", "https://ntfy.sh/", strings.NewReader(body))
|
||||||
|
http.DefaultClient.Do(req)
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Python"
|
||||||
|
``` 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/"
|
||||||
|
})
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "PHP"
|
||||||
|
``` php-inline
|
||||||
|
file_get_contents('https://ntfy.sh/', false, stream_context_create([
|
||||||
|
'http' => [
|
||||||
|
'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/"
|
||||||
|
])
|
||||||
|
]
|
||||||
|
]));
|
||||||
|
```
|
||||||
|
|
||||||
|
The JSON message format closely mirrors the format of the message you can consume when you [subscribe via the API](subscribe/api.md)
|
||||||
|
(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 |
|
||||||
|
| `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 |
|
||||||
|
|
||||||
|
|
||||||
## Click action
|
## Click action
|
||||||
You can define which URL to open when a notification is clicked. This may be useful if your notification is related
|
You can define which URL to open when a notification is clicked. This may be useful if your notification is related
|
||||||
to a Zabbix alert or a transaction that you'd like to provide the deep-link for. Tapping the notification will open
|
to a Zabbix alert or a transaction that you'd like to provide the deep-link for. Tapping the notification will open
|
||||||
|
@ -756,7 +891,13 @@ This could be a Dropbox link, a file from social media, or any other publicly av
|
||||||
externally hosted, the expiration or size limits from above do not apply here.
|
externally hosted, the expiration or size limits from above do not apply here.
|
||||||
|
|
||||||
To attach an external file, simple pass the `X-Attach` header or query parameter (or any of its aliases `Attach` or `a`)
|
To attach an external file, simple pass the `X-Attach` header or query parameter (or any of its aliases `Attach` or `a`)
|
||||||
to specify the attachment URL. It can be any type of file. Here's an example showing how to attach an APK file:
|
to specify the attachment URL. It can be any type of file.
|
||||||
|
|
||||||
|
ntfy will automatically try to derive the file name from the URL (e.g `https://example.com/flower.jpg` will yield a
|
||||||
|
filename `flower.jpg`). To override this filename, you may send the `X-Filename` header or query parameter (or any of its
|
||||||
|
aliases `Filename`, `File` or `f`).
|
||||||
|
|
||||||
|
Here's an example showing how to attach an APK file:
|
||||||
|
|
||||||
=== "Command line (curl)"
|
=== "Command line (curl)"
|
||||||
```
|
```
|
||||||
|
|
|
@ -5,25 +5,28 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
|
||||||
## ntfy server v1.18.0
|
## ntfy server v1.18.0
|
||||||
Released XXXXXXXXXXXXXXX
|
Released XXXXXXXXXXXXXXX
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
|
||||||
|
* Publish messages as JSON ([#133](https://github.com/binwiederhier/ntfy/issues/133), thanks [@cmeis](https://github.com/cmeis) for reporting)
|
||||||
|
|
||||||
**Bug fixes:**
|
**Bug fixes:**
|
||||||
|
|
||||||
* rpm: do not overwrite server.yaml on package upgrade (#166, thanks @waclaw66 for reporting)
|
* rpm: do not overwrite server.yaml on package upgrade ([#166](https://github.com/binwiederhier/ntfy/issues/166), thanks [@waclaw66](https://github.com/waclaw66) for reporting)
|
||||||
|
|
||||||
**Deprecations:**
|
**Deprecations:**
|
||||||
|
|
||||||
* Removed the ability to run server as `ntfy serve` as per [deprecation](https://ntfy.sh/docs/deprecations)
|
* Removed the ability to run server as `ntfy serve` as per [deprecation](https://ntfy.sh/docs/deprecations)
|
||||||
|
|
||||||
## ntfy Android app v1.10.0
|
## ntfy Android app v1.10.0 (UNRELEASED)
|
||||||
Released XXXXXXXXXXXXXXX
|
|
||||||
|
|
||||||
**Features:**
|
**Features:**
|
||||||
|
|
||||||
* Support for UnifiedPush 2.0 specification (bytes messages, #130)
|
* Support for UnifiedPush 2.0 specification (bytes messages, [#130](https://github.com/binwiederhier/ntfy/issues/130))
|
||||||
* Export/import settings and subscriptions (#115, thanks @cmeis for reporting)
|
* Export/import settings and subscriptions ([#115](https://github.com/binwiederhier/ntfy/issues/115), thanks [@cmeis](https://github.com/cmeis) for reporting)
|
||||||
|
|
||||||
**Bug fixes:**
|
**Bug fixes:**
|
||||||
|
|
||||||
* Display locale-specific times, with AM/PM or 24h format (#140, thanks @hl2guide for reporting)
|
* Display locale-specific times, with AM/PM or 24h format ([#140](https://github.com/binwiederhier/ntfy/issues/140), thanks [@hl2guide](https://github.com/hl2guide) for reporting)
|
||||||
|
|
||||||
## ntfy server v1.17.1
|
## ntfy server v1.17.1
|
||||||
Released Mar 12, 2022
|
Released Mar 12, 2022
|
||||||
|
|
|
@ -35,10 +35,11 @@ var (
|
||||||
errHTTPBadRequestTopicDisallowed = &errHTTP{40010, http.StatusBadRequest, "invalid topic: topic name is disallowed", ""}
|
errHTTPBadRequestTopicDisallowed = &errHTTP{40010, http.StatusBadRequest, "invalid topic: topic name is disallowed", ""}
|
||||||
errHTTPBadRequestMessageNotUTF8 = &errHTTP{40011, http.StatusBadRequest, "invalid message: message must be UTF-8 encoded", ""}
|
errHTTPBadRequestMessageNotUTF8 = &errHTTP{40011, http.StatusBadRequest, "invalid message: message must be UTF-8 encoded", ""}
|
||||||
errHTTPBadRequestAttachmentTooLarge = &errHTTP{40012, http.StatusBadRequest, "invalid request: attachment too large, or bandwidth limit reached", ""}
|
errHTTPBadRequestAttachmentTooLarge = &errHTTP{40012, http.StatusBadRequest, "invalid request: attachment too large, or bandwidth limit reached", ""}
|
||||||
errHTTPBadRequestAttachmentURLInvalid = &errHTTP{40013, http.StatusBadRequest, "invalid request: attachment URL is invalid", ""}
|
errHTTPBadRequestAttachmentURLInvalid = &errHTTP{40013, http.StatusBadRequest, "invalid request: attachment URL is invalid", "https://ntfy.sh/docs/publish/#attachments"}
|
||||||
errHTTPBadRequestAttachmentsDisallowed = &errHTTP{40014, http.StatusBadRequest, "invalid request: attachments not allowed", ""}
|
errHTTPBadRequestAttachmentsDisallowed = &errHTTP{40014, http.StatusBadRequest, "invalid request: attachments not allowed", "https://ntfy.sh/docs/config/#attachments"}
|
||||||
errHTTPBadRequestAttachmentsExpiryBeforeDelivery = &errHTTP{40015, http.StatusBadRequest, "invalid request: attachment expiry before delayed delivery date", ""}
|
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", ""}
|
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"}
|
||||||
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", ""}
|
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", ""}
|
||||||
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication"}
|
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication"}
|
||||||
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication"}
|
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication"}
|
||||||
|
|
|
@ -263,8 +263,6 @@ func (s *Server) handle(w http.ResponseWriter, r *http.Request) {
|
||||||
func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
if r.Method == http.MethodGet && r.URL.Path == "/" {
|
if r.Method == http.MethodGet && r.URL.Path == "/" {
|
||||||
return s.handleHome(w, r)
|
return s.handleHome(w, r)
|
||||||
} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && r.URL.Path == "/" {
|
|
||||||
return s.limitRequests(s.fromMessageJSON(s.authWrite(s.handlePublish)))(w, r, v)
|
|
||||||
} else if r.Method == http.MethodGet && r.URL.Path == "/example.html" {
|
} else if r.Method == http.MethodGet && r.URL.Path == "/example.html" {
|
||||||
return s.handleExample(w, r)
|
return s.handleExample(w, r)
|
||||||
} else if r.Method == http.MethodHead && r.URL.Path == "/" {
|
} else if r.Method == http.MethodHead && r.URL.Path == "/" {
|
||||||
|
@ -279,6 +277,8 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit
|
||||||
return s.limitRequests(s.handleFile)(w, r, v)
|
return s.limitRequests(s.handleFile)(w, r, v)
|
||||||
} else if r.Method == http.MethodOptions {
|
} else if r.Method == http.MethodOptions {
|
||||||
return s.handleOptions(w, r)
|
return s.handleOptions(w, r)
|
||||||
|
} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && r.URL.Path == "/" {
|
||||||
|
return s.limitRequests(s.transformBodyJSON(s.authWrite(s.handlePublish)))(w, r, v)
|
||||||
} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && topicPathRegex.MatchString(r.URL.Path) {
|
} else if (r.Method == http.MethodPut || r.Method == http.MethodPost) && topicPathRegex.MatchString(r.URL.Path) {
|
||||||
return s.limitRequests(s.authWrite(s.handlePublish))(w, r, v)
|
return s.limitRequests(s.authWrite(s.handlePublish))(w, r, v)
|
||||||
} else if r.Method == http.MethodGet && publishPathRegex.MatchString(r.URL.Path) {
|
} else if r.Method == http.MethodGet && publishPathRegex.MatchString(r.URL.Path) {
|
||||||
|
@ -1095,7 +1095,9 @@ func (s *Server) limitRequests(next handleFunc) handleFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) fromMessageJSON(next handleFunc) handleFunc {
|
// transformBodyJSON peaks the request body, reads the JSON, and converts it to headers
|
||||||
|
// before passing it on to the next handler. This is meant to be used in combination with handlePublish.
|
||||||
|
func (s *Server) transformBodyJSON(next handleFunc) handleFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
return func(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
body, err := util.Peak(r.Body, s.config.MessageLimit)
|
body, err := util.Peak(r.Body, s.config.MessageLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1104,10 +1106,10 @@ func (s *Server) fromMessageJSON(next handleFunc) handleFunc {
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
var m publishMessage
|
var m publishMessage
|
||||||
if err := json.NewDecoder(body).Decode(&m); err != nil {
|
if err := json.NewDecoder(body).Decode(&m); err != nil {
|
||||||
return err
|
return errHTTPBadRequestJSONInvalid
|
||||||
}
|
}
|
||||||
if !topicRegex.MatchString(m.Topic) {
|
if !topicRegex.MatchString(m.Topic) {
|
||||||
return errors.New("invalid message")
|
return errHTTPBadRequestTopicInvalid
|
||||||
}
|
}
|
||||||
if m.Message == "" {
|
if m.Message == "" {
|
||||||
m.Message = emptyMessageBody
|
m.Message = emptyMessageBody
|
||||||
|
@ -1117,11 +1119,11 @@ func (s *Server) fromMessageJSON(next handleFunc) handleFunc {
|
||||||
if m.Title != "" {
|
if m.Title != "" {
|
||||||
r.Header.Set("X-Title", m.Title)
|
r.Header.Set("X-Title", m.Title)
|
||||||
}
|
}
|
||||||
if m.Priority != "" {
|
if m.Priority != 0 {
|
||||||
r.Header.Set("X-Priority", m.Priority)
|
r.Header.Set("X-Priority", fmt.Sprintf("%d", m.Priority))
|
||||||
}
|
}
|
||||||
if m.Tags != "" {
|
if m.Tags != nil && len(m.Tags) > 0 {
|
||||||
r.Header.Set("X-Tags", m.Tags)
|
r.Header.Set("X-Tags", strings.Join(m.Tags, ","))
|
||||||
}
|
}
|
||||||
if m.Attach != "" {
|
if m.Attach != "" {
|
||||||
r.Header.Set("X-Attach", m.Attach)
|
r.Header.Set("X-Attach", m.Attach)
|
||||||
|
|
|
@ -862,6 +862,31 @@ func TestServer_PublishUnifiedPushText(t *testing.T) {
|
||||||
require.Equal(t, "this is a unifiedpush text message", m.Message)
|
require.Equal(t, "this is a unifiedpush text message", m.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServer_PublishAsJSON(t *testing.T) {
|
||||||
|
s := newTestServer(t, newTestConfig(t))
|
||||||
|
body := `{"topic":"mytopic","message":"A message","title":"a title\nwith lines","tags":["tag1","tag 2"],` +
|
||||||
|
`"not-a-thing":"ok", "attach":"http://google.com","filename":"google.pdf", "click":"http://ntfy.sh","priority":4}`
|
||||||
|
response := request(t, s, "PUT", "/", body, nil)
|
||||||
|
require.Equal(t, 200, response.Code)
|
||||||
|
|
||||||
|
m := toMessage(t, response.Body.String())
|
||||||
|
require.Equal(t, "mytopic", m.Topic)
|
||||||
|
require.Equal(t, "A message", m.Message)
|
||||||
|
require.Equal(t, "a title\nwith lines", m.Title)
|
||||||
|
require.Equal(t, []string{"tag1", "tag 2"}, m.Tags)
|
||||||
|
require.Equal(t, "http://google.com", m.Attachment.URL)
|
||||||
|
require.Equal(t, "google.pdf", m.Attachment.Name)
|
||||||
|
require.Equal(t, "http://ntfy.sh", m.Click)
|
||||||
|
require.Equal(t, 4, m.Priority)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServer_PublishAsJSON_Invalid(t *testing.T) {
|
||||||
|
s := newTestServer(t, newTestConfig(t))
|
||||||
|
body := `{"topic":"mytopic",INVALID`
|
||||||
|
response := request(t, s, "PUT", "/", body, nil)
|
||||||
|
require.Equal(t, 400, response.Code)
|
||||||
|
}
|
||||||
|
|
||||||
func TestServer_PublishAttachment(t *testing.T) {
|
func TestServer_PublishAttachment(t *testing.T) {
|
||||||
content := util.RandomString(5000) // > 4096
|
content := util.RandomString(5000) // > 4096
|
||||||
s := newTestServer(t, newTestConfig(t))
|
s := newTestServer(t, newTestConfig(t))
|
||||||
|
|
|
@ -44,14 +44,14 @@ type attachment struct {
|
||||||
|
|
||||||
// publishMessage is used as input when publishing as JSON
|
// publishMessage is used as input when publishing as JSON
|
||||||
type publishMessage struct {
|
type publishMessage struct {
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Priority string `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
Tags string `json:"tags"`
|
Tags []string `json:"tags"`
|
||||||
Click string `json:"click"`
|
Click string `json:"click"`
|
||||||
Attach string `json:"attach"`
|
Attach string `json:"attach"`
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// messageEncoder is a function that knows how to encode a message
|
// messageEncoder is a function that knows how to encode a message
|
||||||
|
|
Loading…
Reference in New Issue