Răsfoiți Sursa

Priorities, titles, tags

Philipp Heckel 4 ani în urmă
părinte
comite
1b8ebab5f3
4 a modificat fișierele cu 118 adăugiri și 26 ștergeri
  1. 52 8
      server/index.gohtml
  2. 16 10
      server/message.go
  3. 49 8
      server/server.go
  4. 1 0
      server/static/css/app.css

+ 52 - 8
server/index.gohtml

@@ -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 @@
         &nbsp;&nbsp;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/>
+        &nbsp;&nbsp;-H "Title: Unauthorized access detected" \<br/>
+        &nbsp;&nbsp;-H "Priority: urgent" \<br/>
+        &nbsp;&nbsp;-H "Tags: warn,skull" \<br/>
+        &nbsp;&nbsp;-d "Remote access to $(hostname) detected. Act right away." \<br/>
+        &nbsp;&nbsp;<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/>
         &nbsp;&nbsp;&& zfs snapshot ... \<br/>
         &nbsp;&nbsp;&& curl -d "Laptop backup succeeded" <span class="ntfyUrl">ntfy.sh</span>/backups \<br/>
-        &nbsp;&nbsp;|| echo -en "\u26A0\uFE0F Laptop backup failed" | curl -sT- <span class="ntfyUrl">ntfy.sh</span>/backups
+        &nbsp;&nbsp;|| 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/>
-        &nbsp;&nbsp;echo -en "\u26A0\uFE0F SSH login: ${PAM_USER} from ${PAM_RHOST}" | curl -T- <span class="ntfyUrl">ntfy.sh</span>/alerts<br/>
+        &nbsp;&nbsp;curl -H tags:warn -d "SSH login: ${PAM_USER} from ${PAM_RHOST}" <span class="ntfyUrl">ntfy.sh</span>/alerts<br/>
         fi
     </code>
 

+ 16 - 10
server/message.go

@@ -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,
 	}
 }
 

+ 49 - 8
server/server.go

@@ -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

+ 1 - 0
server/static/css/app.css

@@ -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,