diff --git a/docs/releases.md b/docs/releases.md index 5e76bdc..479e7c5 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -26,6 +26,7 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release **Features:** * Regularly send Firebase keepalive messages to ~poll topic to support self-hosted servers (no ticket) +* Add subscribe filter to query exact messages by ID (no ticket) **Bugs:** diff --git a/docs/subscribe/api.md b/docs/subscribe/api.md index 67d3458..7eb3a95 100644 --- a/docs/subscribe/api.md +++ b/docs/subscribe/api.md @@ -267,7 +267,7 @@ curl -s "ntfy.sh/mytopic/json?poll=1&sched=1" ``` ### Filter messages -You can filter which messages are returned based on the well-known message fields `message`, `title`, `priority` and +You can filter which messages are returned based on the well-known message fields `id`, `message`, `title`, `priority` and `tags`. Here's an example that only returns messages of high or urgent priority that contains the both tags "zfs-error" and "error". Note that the `priority` filter is a logical OR and the `tags` filter is a logical AND. @@ -280,12 +280,13 @@ $ curl "ntfy.sh/alerts/json?priority=high&tags=zfs-error" Available filters (all case-insensitive): -| Filter variable | Alias | Example | Description | -|-----------------|---------------------------|------------------------------------|-------------------------------------------------------------------------| -| `message` | `X-Message`, `m` | `ntfy.sh/mytopic?message=lalala` | Only return messages that match this exact message string | -| `title` | `X-Title`, `t` | `ntfy.sh/mytopic?title=some+title` | Only return messages that match this exact title string | -| `priority` | `X-Priority`, `prio`, `p` | `ntfy.sh/mytopic?p=high,urgent` | Only return messages that match *any priority listed* (comma-separated) | -| `tags` | `X-Tags`, `tag`, `ta` | `ntfy.sh/mytopic?tags=error,alert` | Only return messages that match *all listed tags* (comma-separated) | +| Filter variable | Alias | Example | Description | +|-----------------|---------------------------|-----------------------------------------------|-------------------------------------------------------------------------| +| `id` | `X-ID` | `ntfy.sh/mytopic/json?poll=1&id=pbkiz8SD7ZxG` | Only return messages that match this exact message ID | +| `message` | `X-Message`, `m` | `ntfy.sh/mytopic/json?message=lalala` | Only return messages that match this exact message string | +| `title` | `X-Title`, `t` | `ntfy.sh/mytopic/json?title=some+title` | Only return messages that match this exact title string | +| `priority` | `X-Priority`, `prio`, `p` | `ntfy.sh/mytopic/json?p=high,urgent` | Only return messages that match *any priority listed* (comma-separated) | +| `tags` | `X-Tags`, `tag`, `ta` | `ntfy.sh/mytopic?/jsontags=error,alert` | Only return messages that match *all listed tags* (comma-separated) | ### Subscribe to multiple topics It's possible to subscribe to multiple topics in one HTTP call by providing a comma-separated list of topics @@ -315,18 +316,19 @@ format of the message. It's very straight forward: **Message**: -| Field | Required | Type | Example | Description | -|--------------|----------|---------------------------------------------------|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------| -| `id` | ✔️ | *string* | `hwQ2YpKdmg` | Randomly chosen message identifier | -| `time` | ✔️ | *number* | `1635528741` | Message date time, as Unix time stamp | -| `event` | ✔️ | `open`, `keepalive`, `message`, or `poll_request` | `message` | Message type, typically you'd be only interested in `message` | -| `topic` | ✔️ | *string* | `topic1,topic2` | Comma-separated list of topics the message is associated with; only one for all `message` events, but may be a list in `open` events | -| `message` | - | *string* | `Some message` | Message body; always present in `message` events | -| `title` | - | *string* | `Some title` | Message [title](../publish.md#message-title); if not set defaults to `ntfy.sh/` | -| `tags` | - | *string array* | `["tag1","tag2"]` | List of [tags](../publish.md#tags-emojis) that may or not map to emojis | -| `priority` | - | *1, 2, 3, 4, or 5* | `4` | Message [priority](../publish.md#message-priority) with 1=min, 3=default and 5=max | -| `click` | - | *URL* | `https://example.com` | Website opened when notification is [clicked](../publish.md#click-action) | -| `attachment` | - | *JSON object* | *see below* | Details about an attachment (name, URL, size, ...) | +| Field | Required | Type | Example | Description | +|--------------|----------|---------------------------------------------------|-------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------| +| `id` | ✔️ | *string* | `hwQ2YpKdmg` | Randomly chosen message identifier | +| `time` | ✔️ | *number* | `1635528741` | Message date time, as Unix time stamp | +| `event` | ✔️ | `open`, `keepalive`, `message`, or `poll_request` | `message` | Message type, typically you'd be only interested in `message` | +| `topic` | ✔️ | *string* | `topic1,topic2` | Comma-separated list of topics the message is associated with; only one for all `message` events, but may be a list in `open` events | +| `message` | - | *string* | `Some message` | Message body; always present in `message` events | +| `title` | - | *string* | `Some title` | Message [title](../publish.md#message-title); if not set defaults to `ntfy.sh/` | +| `tags` | - | *string array* | `["tag1","tag2"]` | List of [tags](../publish.md#tags-emojis) that may or not map to emojis | +| `priority` | - | *1, 2, 3, 4, or 5* | `4` | Message [priority](../publish.md#message-priority) with 1=min, 3=default and 5=max | +| `click` | - | *URL* | `https://example.com` | Website opened when notification is [clicked](../publish.md#click-action) | +| `actions` | - | *JSON array* | *see [actions buttons](../publish.md#action-buttons)* | [Action buttons](../publish.md#action-buttons) that can be displayed in the notification | +| `attachment` | - | *JSON object* | *see below* | Details about an attachment (name, URL, size, ...) | **Attachment** (part of the message, see [attachments](../publish.md#attachments) for details): @@ -416,6 +418,7 @@ and can be passed as **HTTP headers** or **query parameters in the URL**. They a | `poll` | `X-Poll`, `po` | Return cached messages and close connection | | `since` | `X-Since`, `si` | Return cached messages since timestamp, duration or message ID | | `scheduled` | `X-Scheduled`, `sched` | Include scheduled/delayed messages in message list | +| `id` | `X-ID` | Filter: Only return messages that match this exact message ID | | `message` | `X-Message`, `m` | Filter: Only return messages that match this exact message string | | `title` | `X-Title`, `t` | Filter: Only return messages that match this exact title string | | `priority` | `X-Priority`, `prio`, `p` | Filter: Only return messages that match *any priority listed* (comma-separated) | diff --git a/server/types.go b/server/types.go index 8c4f125..3f6fcdb 100644 --- a/server/types.go +++ b/server/types.go @@ -153,6 +153,7 @@ var ( ) type queryFilter struct { + ID string Message string Title string Tags []string @@ -160,6 +161,7 @@ type queryFilter struct { } func parseQueryFilters(r *http.Request) (*queryFilter, error) { + idFilter := readParam(r, "x-id", "id") messageFilter := readParam(r, "x-message", "message", "m") titleFilter := readParam(r, "x-title", "title", "t") tagsFilter := util.SplitNoEmpty(readParam(r, "x-tags", "tags", "tag", "ta"), ",") @@ -172,6 +174,7 @@ func parseQueryFilters(r *http.Request) (*queryFilter, error) { priorityFilter = append(priorityFilter, priority) } return &queryFilter{ + ID: idFilter, Message: messageFilter, Title: titleFilter, Tags: tagsFilter, @@ -182,11 +185,11 @@ func parseQueryFilters(r *http.Request) (*queryFilter, error) { func (q *queryFilter) Pass(msg *message) bool { if msg.Event != messageEvent { return true // filters only apply to messages - } - if q.Message != "" && msg.Message != q.Message { + } else if q.ID != "" && msg.ID != q.ID { return false - } - if q.Title != "" && msg.Title != q.Title { + } else if q.Message != "" && msg.Message != q.Message { + return false + } else if q.Title != "" && msg.Title != q.Title { return false } messagePriority := msg.Priority