Priorities, titles, tags
This commit is contained in:
parent
7b8185c2a7
commit
1b8ebab5f3
4 changed files with 118 additions and 26 deletions
|
@ -67,12 +67,6 @@
|
|||
<code>
|
||||
curl -d "Backup successful 😀" <span class="ntfyUrl">ntfy.sh</span>/mytopic
|
||||
</code>
|
||||
<p class="smallMarginBottom">
|
||||
And another one using PUT (via <tt>curl -T</tt>):
|
||||
</p>
|
||||
<code>
|
||||
echo -en "\u26A0\uFE0F Unauthorized login" | curl -T- <span class="ntfyUrl">ntfy.sh</span>/mytopic
|
||||
</code>
|
||||
<p class="smallMarginBottom">
|
||||
Here's an example in JS with <tt>fetch()</tt> (see <a href="https://github.com/binwiederhier/ntfy/tree/main/examples">full example</a>):
|
||||
</p>
|
||||
|
@ -82,6 +76,19 @@
|
|||
body: 'Hello from the other side.'<br/>
|
||||
})
|
||||
</code>
|
||||
<p class="smallMarginBottom">
|
||||
There are <a href="#other-features">more features</a> related to publishing messages: You can set a
|
||||
<a href="#priority">notification priority</a>, a <a href="#title">title</a>, and <a href="#tags">tag messages</a>.
|
||||
Here's an example using all of them:
|
||||
</p>
|
||||
<code>
|
||||
curl \<br/>
|
||||
-H "Title: Unauthorized access detected" \<br/>
|
||||
-H "Priority: urgent" \<br/>
|
||||
-H "Tags: warn,skull" \<br/>
|
||||
-d "Remote access to $(hostname) detected. Act right away." \<br/>
|
||||
<span class="ntfyUrl">ntfy.sh</span>/mytopic
|
||||
</code>
|
||||
|
||||
<h2 id="subscribe" class="anchor">Subscribe to a topic</h2>
|
||||
<p>
|
||||
|
@ -196,6 +203,43 @@
|
|||
{"id":"Cm02DsxUHb","time":1637182643,"event":"message","topic":"mytopic2","message":"for topic 2"}
|
||||
</code>
|
||||
|
||||
<h3 id="priority" class="anchor">Message priority (<tt>X-Priority</tt>, <tt>Priority</tt>, <tt>prio</tt>, or <tt>p</tt>)</h3>
|
||||
<p>
|
||||
All messages have a priority, which defines how your urgently your phone notifies you. You can set custom
|
||||
notification sounds and vibration patterns on your phone to map to these priorities.
|
||||
</p>
|
||||
<p class="smallMarginBottom">
|
||||
The following priorities exist: <tt>1</tt> (<tt>min</tt>), <tt>2</tt> (<tt>low</tt>), <tt>3</tt> (<tt>default</tt>),
|
||||
<tt>4</tt> (<tt>high</tt>), and <tt>5</tt> (<tt>max</tt>/<tt>urgent</tt>). You can set the priority with the
|
||||
header <tt>X-Priority</tt> (or any of its aliases: <tt>Priority</tt>, <tt>prio</tt>, or <tt>p</tt>). Here are a few examples:
|
||||
</p>
|
||||
<code>
|
||||
curl -H "X-Priority: urgent" -d "An urgent message" <span class="ntfyUrl">ntfy.sh</span>/mytopic<br/>
|
||||
curl -H "Priority: 2" -d "Low priority message" <span class="ntfyUrl">ntfy.sh</span>/mytopic<br/>
|
||||
curl -H p:4 -d "A high priority message" <span class="ntfyUrl">ntfy.sh</span>/mytopic
|
||||
</code>
|
||||
|
||||
<h3 id="title" class="anchor">Notification title (<tt>X-Title</tt>, <tt>Title</tt>, <tt>ti</tt>, or <tt>t</tt>)</h3>
|
||||
<p class="smallMarginBottom">
|
||||
The notification title is typically set to the topic short URL (e.g. <tt><span class="ntfyUrl">ntfy.sh</span>/mytopic</tt>.
|
||||
To override it, you can set the <tt>X-Title</tt> header (or any of its aliases: <tt>Title</tt>, <tt>ti</tt>, or <tt>t</tt>).
|
||||
</p>
|
||||
<code>
|
||||
curl -H "Title: Dogs are better than cats" -d "Oh my ..." <span class="ntfyUrl">ntfy.sh</span>/mytopic<br/>
|
||||
</code>
|
||||
|
||||
<h3 id="tags" class="anchor">Tagging messages (<tt>X-Tags</tt>, <tt>Tags</tt>, or <tt>ta</tt>)</h3>
|
||||
<p class="smallMarginBottom">
|
||||
You can tag notifications with emojis (or other relevant strings). In the phone app, the tags will be converted
|
||||
to emojis and prepended to the message or title in the notification. You can set tags with the <tt>X-Tags</tt> header
|
||||
(or any of its aliases: <tt>Tags</tt>, or <tt>ta</tt>). Use <a href="https://github.com/vdurmont/emoji-java/blob/master/EMOJIS.md">this reference</a>
|
||||
to figure out what tags you can use to send emojis.
|
||||
</p>
|
||||
<code>
|
||||
curl -H "Tags: warn,skull" -d "Unauthorized SSH access" <span class="ntfyUrl">ntfy.sh</span>/mytopic<br/>
|
||||
curl -H tags:thumbsup -d "Backup successful" <span class="ntfyUrl">ntfy.sh</span>/mytopic<br/>
|
||||
</code>
|
||||
|
||||
<h2 id="examples" class="anchor">Examples</h2>
|
||||
<p>
|
||||
There are a million ways to use ntfy, but here are some inspirations. I try to collect
|
||||
|
@ -213,7 +257,7 @@
|
|||
rsync -a root@laptop /backups/laptop \<br/>
|
||||
&& zfs snapshot ... \<br/>
|
||||
&& curl -d "Laptop backup succeeded" <span class="ntfyUrl">ntfy.sh</span>/backups \<br/>
|
||||
|| echo -en "\u26A0\uFE0F Laptop backup failed" | curl -sT- <span class="ntfyUrl">ntfy.sh</span>/backups
|
||||
|| curl -H tags:warn -H prio:high -d "Laptop backup failed" <span class="ntfyUrl">ntfy.sh</span>/backups
|
||||
</code>
|
||||
|
||||
<h3 id="example-web" class="anchor">Example: Server-sent messages in your web app</h3>
|
||||
|
@ -240,7 +284,7 @@
|
|||
<code>
|
||||
#!/bin/bash<br/>
|
||||
if [ "${PAM_TYPE}" = "open_session" ]; then<br/>
|
||||
echo -en "\u26A0\uFE0F SSH login: ${PAM_USER} from ${PAM_RHOST}" | curl -T- <span class="ntfyUrl">ntfy.sh</span>/alerts<br/>
|
||||
curl -H tags:warn -d "SSH login: ${PAM_USER} from ${PAM_RHOST}" <span class="ntfyUrl">ntfy.sh</span>/alerts<br/>
|
||||
fi
|
||||
</code>
|
||||
|
||||
|
|
|
@ -18,11 +18,14 @@ const (
|
|||
|
||||
// message represents a message published to a topic
|
||||
type message struct {
|
||||
ID string `json:"id"` // Random message ID
|
||||
Time int64 `json:"time"` // Unix time in seconds
|
||||
Event string `json:"event"` // One of the above
|
||||
Topic string `json:"topic"`
|
||||
Message string `json:"message,omitempty"`
|
||||
ID string `json:"id"` // Random message ID
|
||||
Time int64 `json:"time"` // Unix time in seconds
|
||||
Event string `json:"event"` // One of the above
|
||||
Topic string `json:"topic"`
|
||||
Priority int `json:"priority,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// messageEncoder is a function that knows how to encode a message
|
||||
|
@ -31,11 +34,14 @@ type messageEncoder func(msg *message) (string, error)
|
|||
// newMessage creates a new message with the current timestamp
|
||||
func newMessage(event, topic, msg string) *message {
|
||||
return &message{
|
||||
ID: util.RandomString(messageIDLength),
|
||||
Time: time.Now().Unix(),
|
||||
Event: event,
|
||||
Topic: topic,
|
||||
Message: msg,
|
||||
ID: util.RandomString(messageIDLength),
|
||||
Time: time.Now().Unix(),
|
||||
Event: event,
|
||||
Topic: topic,
|
||||
Priority: 0,
|
||||
Tags: nil,
|
||||
Title: "",
|
||||
Message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ var (
|
|||
indexTemplate = template.Must(template.New("index").Parse(indexSource))
|
||||
|
||||
//go:embed "example.html"
|
||||
exampleSource string
|
||||
exampleSource string
|
||||
|
||||
//go:embed static
|
||||
webStaticFs embed.FS
|
||||
|
@ -150,11 +150,14 @@ func createFirebaseSubscriber(conf *config.Config) (subscriber, error) {
|
|||
_, err := msg.Send(context.Background(), &messaging.Message{
|
||||
Topic: m.Topic,
|
||||
Data: map[string]string{
|
||||
"id": m.ID,
|
||||
"time": fmt.Sprintf("%d", m.Time),
|
||||
"event": m.Event,
|
||||
"topic": m.Topic,
|
||||
"message": m.Message,
|
||||
"id": m.ID,
|
||||
"time": fmt.Sprintf("%d", m.Time),
|
||||
"event": m.Event,
|
||||
"topic": m.Topic,
|
||||
"priority": fmt.Sprintf("%d", m.Priority),
|
||||
"tags": strings.Join(m.Tags, ","),
|
||||
"title": m.Title,
|
||||
"message": m.Message,
|
||||
},
|
||||
})
|
||||
return err
|
||||
|
@ -246,6 +249,10 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito
|
|||
if m.Message == "" {
|
||||
return errHTTPBadRequest
|
||||
}
|
||||
title, priority, tags := parseHeaders(r.Header)
|
||||
m.Title = title
|
||||
m.Priority = priority
|
||||
m.Tags = tags
|
||||
if err := t.Publish(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -262,6 +269,40 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito
|
|||
return nil
|
||||
}
|
||||
|
||||
func parseHeaders(header http.Header) (title string, priority int, tags []string) {
|
||||
title = readHeader(header, "x-title", "title", "ti", "t")
|
||||
priorityStr := readHeader(header, "x-priority", "priority", "prio", "p")
|
||||
if priorityStr != "" {
|
||||
switch strings.ToLower(priorityStr) {
|
||||
case "1", "min":
|
||||
priority = 1
|
||||
case "2", "low":
|
||||
priority = 2
|
||||
case "4", "high":
|
||||
priority = 4
|
||||
case "5", "max", "urgent":
|
||||
priority = 5
|
||||
default:
|
||||
priority = 3
|
||||
}
|
||||
}
|
||||
tagsStr := readHeader(header, "x-tags", "tags", "ta")
|
||||
if tagsStr != "" {
|
||||
tags = strings.Split(tagsStr, ",")
|
||||
}
|
||||
return title, priority, tags
|
||||
}
|
||||
|
||||
func readHeader(header http.Header, names ...string) string {
|
||||
for _, name := range names {
|
||||
value := header.Get(name)
|
||||
if value != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *Server) handleSubscribeJSON(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
encoder := func(msg *message) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
|
@ -414,11 +455,11 @@ func (s *Server) topicFromID(id string) (*topic, error) {
|
|||
return topics[0], nil
|
||||
}
|
||||
|
||||
func (s *Server) topicsFromIDs(ids... string) ([]*topic, error) {
|
||||
func (s *Server) topicsFromIDs(ids ...string) ([]*topic, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
topics := make([]*topic, 0)
|
||||
for _, id := range ids {
|
||||
for _, id := range ids {
|
||||
if _, ok := s.topics[id]; !ok {
|
||||
if len(s.topics) >= s.config.GlobalTopicLimit {
|
||||
return nil, errHTTPTooManyRequests
|
||||
|
|
|
@ -69,6 +69,7 @@ code {
|
|||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Lato font (OFL), https://fonts.google.com/specimen/Lato#about,
|
||||
|
|
Loading…
Reference in a new issue