binwiederhier / ntfy

Send push notifications to your phone or desktop using PUT/POST
https://ntfy.sh
Apache License 2.0
17.25k stars 663 forks source link

Update/delete notifications #303

Open blacklight opened 2 years ago

blacklight commented 2 years ago

A similar issue was open a while ago, but it didn't get traction for some reason.

The ability to update notifications is so far the only feature that forces me to still use AutoNotification and the Tasker environment instead of ntfy.

I have a multi-host and multi-room music setup, and whenever a new track is playing I receive an actionable notification that allows me to easily control the playback.

AutoNotification allows editing a notification given its ID (even a total overwrite/replace is fine, so we don't have to mess with the complexity of partial updates). And that's exactly the feature I need: if every music device created a new notification for each track that is played the notification bar will quickly get crammed, especially if there is no programmatic way to dismiss stale notifications. Notifications that report a progress status are another quite interesting use case that isn't possible right now.

binwiederhier commented 2 years ago

I absolutely love the idea of updating notifications, and I've attempted to implement it twice. It's a little tricky to implement but doable.

The reason it didn't get traction was because I hated the API I came up with, and on Discord nobody had any better ideas. I want the API to be simple and ideally the request to update a message is the same that it is to initially publish it. That's what's tricky about it.

Related: https://github.com/binwiederhier/ntfy/pull/259

254

187

43

binwiederhier commented 2 years ago

There's also #44 for progress. That is trivial to implement once messages can be updated.

blacklight commented 2 years ago

I want the API to be simple and ideally the request to update a message is the same that it is to initially publish it.

Something like POST/PUT without an id to create a notification and PUT with an id to upsert? id could then be passed either on the query string or the JSON payload.

KawaiiZapic commented 2 years ago

It's great to have this feature.
we can set an id(or type) for a message, and replace the old message when new message come if id is same.

Or can we support conversation type message on Android?
conversations

It is useful to push IRC message to phone in a website only IRC channel.

mrherman commented 1 year ago

For the API, we could consider something similar to Home Assistants companion app's notification replace/clear system. https://companion.home-assistant.io/docs/notifications/notifications-basic/#replacing For that, the user/client provides a tag/id name for the notifications. On the receiver side, if no notification with that tag exists then it creates it, otherwise it deletes/replaces it. It is up to the user to make the tag super unique or generic depending on their use case.

Just to add, I would really like this feature too. I pop up an image of the outdoor cam when doorbell is rang, but it is only relevant until no motion is detected (for me to decide to answer it or not), after that I would like to clear the notification. Also, sticky notification (if we get them) that has some data that is periodically updated, I can update the notification when the data is changed.

julianlam commented 1 year ago

Another +1 for this suggestion, if I may :smile:

We would use this in NodeBB for replacing notifications with updated text or to remove stale notifications. For example, a notification that a post needs review is no longer relevant if another admin has approved/denied it, and so the notification automatically gets rescinded in NodeBB.

We also replace notification text saying "X has posted a reply" to some variants like "X and 2 others have posted a reply", etc.

We use nid, similar to @BlackLight's suggestion. We also use mergeId to combine notifications, but I wouldn't expect that one to land in ntfy any time soon, if ever :wink:

courtarro commented 1 year ago

+1

As discussed in previous comments, it seems like the standard in other notification architectures is to use an optional ID when creating a notification. If the ID of a new notification matches a previous notification, the previous notification is replaced with the new one. If we support an integer ID, the clients should be able to handle them also. If there is an errant client that can't handle it, it would just show all of the notifications without replacement.

Is there a reason we can't use an integer id field for this? What are the API concerns you have, @binwiederhier?

Edit: I see that there's an internal id field already that is a random string. So then let's call ours external_id and it's an int.

spraot commented 1 year ago

If this feature is implemented I would happily switch over from Pushover.

chovyy commented 9 months ago

I would realy love to have this. I often send status updates from my home automation, and it is quite annoying to have this stack of messages on the same issue.

pnxs commented 8 months ago

I also really like this feature, I tried to understand what you hate about your API for updating messages but I had trouble to find what the API really looks like (I looked into #259 and #187). You said, you want the API "to be simple and ideally the request to update a message is the same that it is to initially publish it.". This makes sense to me, because removing the message is more like adding the information, that the message is remove to an existing message. In some cases one would like to add an information or timestamp why this message was deleted. Naively spoken: I just expect the message id to be returned from the POST, which enabled the publisher to update it's message if it want to. To update the message it just do the same as publishing it originally, with the difference, that the message id is added to the POST.

E.g.:

First POST:

POST / HTTP/1.1
Host: ntfy.sh

{
    "topic": "myhome",
    "message": "Garage door has been open for 15 minutes. Close it?",
    "actions": [
      {
        "action": "http",
        "label": "Close door",
        "url": "https://api.mygarage.lan/",
        "method": "PUT",
        "headers": {
          "Authorization": "Bearer zAzsx1sk.."
        },
        "body": "{\"action\": \"close\"}"
      }
    ]
}

The Post will return the message Id "cdssFSfwt4fw"

Update of the message:

POST / HTTP/1.1
Host: ntfy.sh

{
    "topic": "myhome",
    "message": "Garage door has been open for 20 minutes. Close it?",
    "actions": [
      {
        "action": "http",
        "label": "Close door",
        "url": "https://api.mygarage.lan/",
        "method": "PUT",
        "headers": {
          "Authorization": "Bearer zAzsx1sk.."
        },
        "body": "{\"action\": \"close\"}"
      }
    ]
}

Remove the message:

POST / HTTP/1.1
Host: ntfy.sh

{
    "topic": "myhome",
    "id": "cdssFSfwt4fw",
    "message": "Garage door was open for 25 minutes.",
    "removed": "<timestamp>"
}

The presenting application has not to be aware of updating messages. In the simplest case, every message is shown consecutively. But if the presenting application is aware it could replace the message on it's display, change colors or do whatever. Maybe also show a log of recently remove messages.

What do you think @binwiederhier ?

genofire commented 7 months ago

i am for POST for create, PUT for update and DELETE for delete an message as http method (and not everything POST)

i also believe everything should be equal like before the POST (just the path contains the id and topic like DELETE /topic/id), i think it is an bad idea to put any logic into the BODY of an request.

pnxs commented 7 months ago

@genofire How to you handle the information about the time when the reason for generating the original message was omitted (e.g. the real deletion date)? What do you mean with " i think it is an bad idea to put any logic into the BODY of an request"? I do not see any logic... And sure you can use different Verbs, but I tried to make the API very simple, as wished by the author...

mikebakke commented 7 months ago

+1 - driven by the use case where I have set a scheduled message but need to delete it before reaching the scheduled delivery time, to cancel it as not required or correct my speeiling mistakes...

julianlam commented 7 months ago

@pnxs, @genofire means you should not just have a single POST endpoint and pass in an action parameter to denote whether to create, delete, or update... because those are already available if you use the proper http methods.

smashah commented 3 months ago

i thought this + progress bar notification would already be built in to ntfy. I think it should be implemented even if the api is awkward

wunter8 commented 1 week ago

Copying my thoughts from Discord to here so they don't get lost:

(a) I propose adding a new field to the message object called pid (persistent id). It will be a user-defined string ([A-Za-z0-9-_]+). You can include the pid in a header (-H "pid: garage") or in the JSON body ({"topic":"home","message":"The garage has been open for 10 minutes","pid":"garage"}).

(b) Each revision to a notification (e.g., sending another message with "pid: garage") will be its own, individual message. It will have a different message id, db row id, timestamp, etc.. I think this will help maintain backwards compatibility since old clients can just receive each individual message, ignore the pid field, and show a notification for each revision. All the messages/revisions can be sorted based on timestamp, and ?since= will work just as before. Updated clients can choose to combine/update notifications that have the same pid in their respective UIs.

(c) Having each revision be its own message (with its own message id, etc.) makes it easy for a client to show the history of a message over time. And you don't run into issues with reinserting a modified message into the db to get a new id.

(d) I prefer this approach over updating a message based on its message id (e.g., POST /topic/$id) because this way you do not need to remember/keep track of a randomly generated message id; it would be nice to update a message using a key I define.

(e) One of the use cases for this feature that was discussed in a Github issue was implementing a "progress" feature:

for progress in 10 20 30 40 50 60 70 80 90 100; do 
  curl -d "Build process running, currently ${progress}%" ntfy.sh/mytopic
  sleep 10
done

(e) Imagine this being replaced with actual progress updates of any long running command (e.g., file downloads, machine updates, machine learning model training, etc.). I could send progress updates right now using the current API (using the above example code), but I'd receive 10 different notifications. It'd be nice to be able to simply include a pid to the above code and receive 1 updating notification instead of 10 individual notifications. (And old clients that haven't been updated yet (e.g., iOS 😬) would simply "fallback" to receiving 10 individual notifications).

(f) Since every message update is its own message, rate limiting would be applied as if we were using the current/"old" system: every message or revision counts as 1 publish toward your limit; you cannot circumvent the limits by sending 1 message to a topic and "updating" the contents of that message 10 (20, 30, 100, etc.) times.

g) You mentioned that you weren't sure about anonymous users being able to update the text of any previous message. I think we can either add a new user permission to control "update" permissions (not my preference), or we can let any user that can publish to a topic update a notification with a "pid" in that topic. If we go with my approach of having each revision as its own message (and leaving it to the client to combine/update the messages in the UI), the history of edits will be tracked, so no information could be lost/overwritten. And it continues to "fallback" gracefully, since old clients will just depend on normal publish permissions instead of trying to act based on a new "update" permission. And any old, already cached notifications cannot be updated using the new feature since the old messages don't have a pid

(h) In general, this is mostly a proposal to update the clients. A new field will be added to the message object on the server, but that's it there. We don't need to add new endpoints (e.g., /topic/$id), rate-limiting code, permissions, etc.. We just add a user-specified identifier (pid) to messages (on the backend), and then update each client to deal with the new field and use it to make their respective UI/notification systems a little cleaner/more convenient.

(i) I know Android, iOS, and web allow updating the contents of a notification/replacing an existing notification with a new notification with different contents.