Skip to content

Commit 9241b05

Browse files
committed
feat: add subscribe param
1 parent 630f295 commit 9241b05

File tree

7 files changed

+48
-5
lines changed

7 files changed

+48
-5
lines changed

docs/releases.md

+1
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
13781378
**Features:**
13791379

13801380
* Add username/password auth to email publishing ([#1164](https://github.com/binwiederhier/ntfy/pull/1164), thanks to [@bishtawi](https://github.com/bishtawi))
1381+
* Add `latest` subscription param for grabbing just the most recent message (thanks to [@wunter8](https://github.com/wunter8))
13811382

13821383
**Bug fixes + maintenance:**
13831384

docs/subscribe/api.md

+8
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,14 @@ curl -s "ntfy.sh/mytopic/json?since=1645970742"
257257
curl -s "ntfy.sh/mytopic/json?since=nFS3knfcQ1xe"
258258
```
259259

260+
### Fetch latest message
261+
If you only want the most recent message sent to a topic and do not have a message ID or timestamp to use with
262+
`since=`, you can use `since=latest` to grab the most recent message from the cache for a particular topic.
263+
264+
```
265+
curl -s "ntfy.sh/mytopic/json?poll=1&since=latest"
266+
```
267+
260268
### Fetch scheduled messages
261269
Messages that are [scheduled to be delivered](../publish.md#scheduled-delivery) at a later date are not typically
262270
returned when subscribing via the API, which makes sense, because after all, the messages have technically not been

server/message_cache.go

+17
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ const (
9999
WHERE topic = ? AND (id > ? OR published = 0)
100100
ORDER BY time, id
101101
`
102+
selectMessagesLatestQuery = `
103+
SELECT mid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding
104+
FROM messages
105+
WHERE topic = ? AND published = 1
106+
ORDER BY time DESC, id DESC
107+
LIMIT 1
108+
`
102109
selectMessagesDueQuery = `
103110
SELECT mid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding
104111
FROM messages
@@ -416,6 +423,8 @@ func (c *messageCache) addMessages(ms []*message) error {
416423
func (c *messageCache) Messages(topic string, since sinceMarker, scheduled bool) ([]*message, error) {
417424
if since.IsNone() {
418425
return make([]*message, 0), nil
426+
} else if since.IsLatest() {
427+
return c.messagesLatest(topic)
419428
} else if since.IsID() {
420429
return c.messagesSinceID(topic, since, scheduled)
421430
}
@@ -462,6 +471,14 @@ func (c *messageCache) messagesSinceID(topic string, since sinceMarker, schedule
462471
return readMessages(rows)
463472
}
464473

474+
func (c *messageCache) messagesLatest(topic string) ([]*message, error) {
475+
rows, err := c.db.Query(selectMessagesLatestQuery, topic)
476+
if err != nil {
477+
return nil, err
478+
}
479+
return readMessages(rows)
480+
}
481+
465482
func (c *messageCache) MessagesDue() ([]*message, error) {
466483
rows, err := c.db.Query(selectMessagesDueQuery, time.Now().Unix())
467484
if err != nil {

server/message_cache_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ func testCacheMessages(t *testing.T, c *messageCache) {
6666
require.Equal(t, 1, len(messages))
6767
require.Equal(t, "my other message", messages[0].Message)
6868

69+
// mytopic: latest
70+
messages, _ = c.Messages("mytopic", sinceLatestMessage, false)
71+
require.Equal(t, 1, len(messages))
72+
require.Equal(t, "my other message", messages[0].Message)
73+
6974
// example: count
7075
counts, err = c.MessageCounts()
7176
require.Nil(t, err)

server/server.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -1556,8 +1556,8 @@ func (s *Server) sendOldMessages(topics []*topic, since sinceMarker, scheduled b
15561556

15571557
// parseSince returns a timestamp identifying the time span from which cached messages should be received.
15581558
//
1559-
// Values in the "since=..." parameter can be either a unix timestamp or a duration (e.g. 12h), or
1560-
// "all" for all messages.
1559+
// Values in the "since=..." parameter can be either a unix timestamp or a duration (e.g. 12h),
1560+
// "all" for all messages, or "latest" for the most recent message for a topic
15611561
func parseSince(r *http.Request, poll bool) (sinceMarker, error) {
15621562
since := readParam(r, "x-since", "since", "si")
15631563

@@ -1569,6 +1569,8 @@ func parseSince(r *http.Request, poll bool) (sinceMarker, error) {
15691569
return sinceNoMessages, nil
15701570
} else if since == "all" {
15711571
return sinceAllMessages, nil
1572+
} else if since == "latest" {
1573+
return sinceLatestMessage, nil
15721574
} else if since == "none" {
15731575
return sinceNoMessages, nil
15741576
}

server/server_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,11 @@ func TestServer_PublishAndPollSince(t *testing.T) {
594594
require.Equal(t, 1, len(messages))
595595
require.Equal(t, "test 2", messages[0].Message)
596596

597+
response = request(t, s, "GET", "/mytopic/json?poll=1&since=latest", "", nil)
598+
messages = toMessages(t, response.Body.String())
599+
require.Equal(t, 1, len(messages))
600+
require.Equal(t, "test 2", messages[0].Message)
601+
597602
response = request(t, s, "GET", "/mytopic/json?poll=1&since=INVALID", "", nil)
598603
require.Equal(t, 40008, toHTTPError(t, response.Body.String()).Code)
599604
}

server/types.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,12 @@ func (t sinceMarker) IsNone() bool {
169169
return t == sinceNoMessages
170170
}
171171

172+
func (t sinceMarker) IsLatest() bool {
173+
return t == sinceLatestMessage
174+
}
175+
172176
func (t sinceMarker) IsID() bool {
173-
return t.id != ""
177+
return t.id != "" && t.id != "latest"
174178
}
175179

176180
func (t sinceMarker) Time() time.Time {
@@ -182,8 +186,9 @@ func (t sinceMarker) ID() string {
182186
}
183187

184188
var (
185-
sinceAllMessages = sinceMarker{time.Unix(0, 0), ""}
186-
sinceNoMessages = sinceMarker{time.Unix(1, 0), ""}
189+
sinceAllMessages = sinceMarker{time.Unix(0, 0), ""}
190+
sinceNoMessages = sinceMarker{time.Unix(1, 0), ""}
191+
sinceLatestMessage = sinceMarker{time.Unix(0, 0), "latest"}
187192
)
188193

189194
type queryFilter struct {

0 commit comments

Comments
 (0)