mirror of
https://github.com/binwiederhier/ntfy.git
synced 2024-11-22 03:13:33 +01:00
Bla
This commit is contained in:
parent
9247dac50d
commit
a04f2f9c9a
3 changed files with 85 additions and 42 deletions
|
@ -938,6 +938,37 @@ 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');
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## JSON templating
|
||||||
|
Some services let you specify a webhook URL but do not let you modify the webhook body (e.g. GitHub, Grafana). Instead of using a separate
|
||||||
|
bridge program to parse the webhook body into the format ntfy expects, you can include a templated message and/or a templated title
|
||||||
|
which will be populated based on the fields of the webhook body (so long as the webhook body is valid JSON).
|
||||||
|
|
||||||
|
Enable templating by setting the `X-Template` header (or its aliases `Template` or `tpl`) to `yes`, or (more appropriately for webhooks)
|
||||||
|
by setting the `?template=yes` query parameter. Then, include templates in your message and/or title by including paths to the
|
||||||
|
appropriate JSON fields surrounded by `${` and `}`, e.g. `${alert.title}` or `${error.desc}`, depending on your JSON payload.
|
||||||
|
|
||||||
|
Please refer to the [GJSON docs](https://github.com/tidwall/gjson/blob/master/SYNTAX.md) for supported JSON path syntax, as well as
|
||||||
|
[gjson.dev](https://gjson.dev/) to test your templates.
|
||||||
|
|
||||||
|
=== "HTTP"
|
||||||
|
``` http
|
||||||
|
POST /mytopic HTTP/1.1
|
||||||
|
Host: ntfy.sh
|
||||||
|
X-Message: Error message: ${error.desc}
|
||||||
|
X-Title: ${hostname}: A ${error.level} error has occurred
|
||||||
|
X-Template: yes
|
||||||
|
|
||||||
|
{"hostname": "philipp-pc", "error": {"level": "severe", "desc": "Disk has run out of space"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
The example above would send a notification with a title "philipp-pc: A severe error has occurred" and a message "Error message: Disk has run out of space".
|
||||||
|
|
||||||
|
For Grafana webhooks, you might find it helpful to use the headers `X-Title: Grafana alert: ${title}` and `X-Message: ${message}`.
|
||||||
|
Alternatively, you can include the params in the webhook URL. For example, by
|
||||||
|
appending `?template=yes&title=Grafana alert: ${title}&message=${message}` to the URL.
|
||||||
|
|
||||||
|
|
||||||
## Publish as JSON
|
## Publish as JSON
|
||||||
_Supported on:_ :material-android: :material-apple: :material-firefox:
|
_Supported on:_ :material-android: :material-apple: :material-firefox:
|
||||||
|
|
||||||
|
@ -3557,34 +3588,6 @@ ntfy server plays the role of the Push Gateway, as well as the Push Provider. Un
|
||||||
!!! info
|
!!! info
|
||||||
This is not a generic Matrix Push Gateway. It only works in combination with UnifiedPush and ntfy.
|
This is not a generic Matrix Push Gateway. It only works in combination with UnifiedPush and ntfy.
|
||||||
|
|
||||||
### Message and Title Templates
|
|
||||||
Some services let you specify a webhook URL but do not let you modify the webhook body (e.g., Grafana). Instead of using a separate
|
|
||||||
bridge program to parse the webhook body into the format ntfy expects, you can include a templated message and/or a templated title
|
|
||||||
which will be populated based on the fields of the webhook body (so long as the webhook body is valid JSON).
|
|
||||||
|
|
||||||
Enable templating by setting the `X-Template` header (or its aliases `Template` or `tpl`) to "yes". Then, include templates
|
|
||||||
in your message and/or title (no other fields can be filled with a template at this time) by including paths to the
|
|
||||||
appropriate JSON fields surrounded by `${` and `}`. See an example below.
|
|
||||||
See [GJSON docs](https://github.com/tidwall/gjson/blob/master/SYNTAX.md) for supported JSON path syntax.
|
|
||||||
[https://gjson.dev/](https://gjson.dev/) is a great resource for testing your templates.
|
|
||||||
|
|
||||||
=== "HTTP"
|
|
||||||
``` http
|
|
||||||
POST /mytopic HTTP/1.1
|
|
||||||
Host: ntfy.sh
|
|
||||||
X-Message: Error message: ${error.desc}
|
|
||||||
X-Title: ${hostname}: A ${error.level} error has occurred
|
|
||||||
X-Template: yes
|
|
||||||
|
|
||||||
{"hostname": "philipp-pc", "error": {"level": "severe", "desc": "Disk has run out of space"}}
|
|
||||||
```
|
|
||||||
|
|
||||||
The example above would send a notification with a title "philipp-pc: A severe error has occurred" and a message "Error message: Disk has run out of space".
|
|
||||||
|
|
||||||
For Grafana webhooks, you might find it helpful to use the headers `X-Title: Grafana alert: ${title}` and `X-Message: ${message}`.
|
|
||||||
Alternatively, you can include the params in the webhook URL. For example, by
|
|
||||||
appending `?template=yes&title=Grafana alert: ${title}&message=${message}` to the URL.
|
|
||||||
|
|
||||||
## Public topics
|
## Public topics
|
||||||
Obviously all topics on ntfy.sh are public, but there are a few designated topics that are used in examples, and topics
|
Obviously all topics on ntfy.sh are public, but there are a few designated topics that are used in examples, and topics
|
||||||
that you can use to try out what [authentication and access control](#authentication) looks like.
|
that you can use to try out what [authentication and access control](#authentication) looks like.
|
||||||
|
|
|
@ -23,13 +23,13 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/emersion/go-smtp"
|
"github.com/emersion/go-smtp"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/tidwall/gjson"
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"heckel.io/ntfy/v2/log"
|
"heckel.io/ntfy/v2/log"
|
||||||
"heckel.io/ntfy/v2/user"
|
"heckel.io/ntfy/v2/user"
|
||||||
|
@ -1095,32 +1095,43 @@ func (s *Server) handleBodyAsTextMessage(m *message, body *util.PeekedReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleBodyAsTemplatedTextMessage(m *message, body *util.PeekedReadCloser) error {
|
func (s *Server) handleBodyAsTemplatedTextMessage(m *message, body *util.PeekedReadCloser) error {
|
||||||
body, err := util.Peek(body, jsonBodyBytesLimit)
|
body, err := util.Peek(body, max(s.config.MessageSizeLimit, jsonBodyBytesLimit))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if body.LimitReached {
|
} else if body.LimitReached {
|
||||||
return errHTTPEntityTooLargeJSONBody
|
return errHTTPEntityTooLargeJSONBody
|
||||||
}
|
}
|
||||||
peekedBody := strings.TrimSpace(string(body.PeekedBytes))
|
peekedBody := strings.TrimSpace(string(body.PeekedBytes))
|
||||||
if !gjson.Valid(peekedBody) {
|
m.Message = replaceTemplate(m.Message, peekedBody)
|
||||||
return errHTTPBadRequestTemplatedMessageNotJSON
|
m.Title = replaceTemplate(m.Title, peekedBody)
|
||||||
}
|
|
||||||
m.Message = replaceGJSONTemplate(m.Message, peekedBody)
|
|
||||||
m.Title = replaceGJSONTemplate(m.Title, peekedBody)
|
|
||||||
if len(m.Message) > s.config.MessageSizeLimit {
|
if len(m.Message) > s.config.MessageSizeLimit {
|
||||||
return errHTTPBadRequestTemplatedMessageTooLarge
|
return errHTTPBadRequestTemplatedMessageTooLarge
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func replaceGJSONTemplate(template string, source string) string {
|
func replaceTemplate(tpl string, source string) string {
|
||||||
matches := templateVarRegex.FindAllStringSubmatch(template, -1)
|
rendered, err := replaceTemplateInternal(tpl, source)
|
||||||
for _, m := range matches {
|
if err != nil {
|
||||||
if result := gjson.Get(source, m[1]); result.Exists() {
|
return "<invalid template>"
|
||||||
template = strings.ReplaceAll(template, fmt.Sprintf(templateVarFormat, m[1]), result.String())
|
|
||||||
}
|
}
|
||||||
|
return rendered
|
||||||
}
|
}
|
||||||
return template
|
|
||||||
|
func replaceTemplateInternal(tpl string, source string) (string, error) {
|
||||||
|
var data any
|
||||||
|
if err := json.Unmarshal([]byte(source), &data); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
t, err := template.New("").Parse(tpl)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := t.Execute(&buf, data); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleBodyAsAttachment(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser) error {
|
func (s *Server) handleBodyAsAttachment(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser) error {
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue