How to get unread notification / topic count via API?

I’m running a self-hosted Discourse instance running on pretty basic Docker installation. The forum runs on a subdomain of my main site and it uses main site as SSO host. I’d like to encourage traffic between the main site and forum by showing some kind of unread forum notifications/topics number for a given user on the main site. Problem is, I haven’t figured a good way to get the data.

I tried relaxing same site cookies setting to disabled, but if I query for example /notifications.json?recent=true with JavaScript from the main site (when logged in on the forum), I still get not_logged_in error. The query works fine from browser window (when logged in).

Is there a way to change the Discourse session cookie for the top-level domain, or some other way I could query this data?

There is also possibility to query this from the backend with a Python script and API key if it helps. In any case I would still need at least a semi-decent way of syncing this data so if a user sees 10 notifications on the main site, then goes to the forum and reads/resets them, she should see the updated number on the main site, too.

I just realised that notifications are totally different than unread/new topics. Preferably I would show both for the users on the main site. Say Alice has 2 new topics, 3 unread posts and 2 unread notifications. I’d preferably show a number “7” on the main site notification tab (like on Facebook, for example)

Any tips or ideas would be appreciated :slight_smile:

I do think we should offer a clean howto on this … perhaps @blake could take it?

5 Likes

I’m happy to help in writing one if I can be of any assistance :slight_smile:

2 Likes

A clean howto on how to use those api endpoints? or how to use cookie/session to make authenticated JavaScript CORS requests (doable, but way more involved)?

I think we should always encourage people to do such requests from the backend.

Maybe a simple Rails/Sinatra example app where:

  • Discourse users authenticate using SSO with the example application

  • Example application has a simple header that shows DIscourse notifications

3 Likes

Totally agree.

@Uninen I think this is where you should start focusing your efforts.

I’ll look into this. I don’t think there is an api endpoint for this like there is for /notifications.json.

I’m thinking you will have to do polling. So make an API request at a set interval to check for new notifications. The problem is that you will have to do polling for each user separately. How many users do you have? This could get out of hand.

3 Likes

As a user, doing this in the backend would make lots of sense to me :+1:

I think this is missing the cruicial part, tho; how does one fetch the necessary data (using the api? which endpoints? what kind of api key / authentication we need?) and what would be a smart way of doing these queries (in realtime? periodically?) and how would the updating/dismissing work if we store something in cache or db.

Thanks for helping!

Sorry, I totally missed the previous post. Thanks @Falco and @blake , I will get very far with this help already!

Let me know if I gan help in writing the documentation or some other way. Don’t know Rails/Sinatra (but Django instead) but I’m generally familiar with both back- and frontend stack.

Of course, please do! We love help from the community :slight_smile:.

2 Likes

Using the API.

The endpoint https://example.com/notifications?username=Falco which can be discovered using How to reverse engineer the Discourse API and navigating to a user profile Notification page.

You will need to pass an Admin API Key to be able to query notifications from any user.

Depends on your app.

A good default is:

  • When you are rendering a new page in your main app, you will know your current user.

  • Translate your app user id to a Discourse user id (and cache this for a long period)

  • Query the notifications endpoint in Discourse (cache for a short period)

  • Render your app page with the Discourse Notifications

For Dismissing, a click to dismiss will need to hit your backend and then you issue a PUT to https:/example.com/notifications/mark-read. That needs to clear your app notifications cache for the user.

Advanced stuff would include listening to new notifications in a Discourse plugin (or a PR to core that adds a Notification webhook. That would allow your main app to keep an websocket or server-sent events and update notifications in real time.

4 Likes

Okay there doesn’t appear to be an API endpoint for getting the unread or new topic counts for a user. It would be possible to write a plugin that creates a new endpoint that provides those stats. Those counts are coming from this file.

3 Likes

I managed to do this by using two API endpoints and some backend magic. My first implementation fetches number of unread topics, number of unread notifications and number of unread private messages and then combines these numbers with some simple logic to yield a one meaningful number to show the user on the main site.

I use the following query for fetching the number of unread topics:
/unread.json?api_key=%s&api_username=%s

and this for unread notifications and private messages:
/session/current.json?api_key=%s&api_username=%s.

There might be a better way but this works for me now.

I then cache these values and refresh when needed. I also changed the forum link on the main site navigation to a simple redirect function that marks the notifications read and clears the cache every time user navigates to the forum from the main site. This is was the simplest brute force way that I came up with to easily clear the notifications. (If the user doesn’t actually reset the notifications on the forum, they will be updated again on next page load on the main site.)

Here’s my actual Django code for that view:

def backstage_redirect(request):
    if request.user.is_authenticated():
        try:
            profile = request.user.userprofile
            profile.refresh_backstage_notifications(mark_as_read=True)
        except Exception:
            pass

    return HttpResponseRedirect('https://backstage.slipmat.io')

There is lots of improvements to do here, but for now, this works for me as first implementation. Thanks everyone for really swift and helpful advice!

3 Likes

Nice! Looks good.

I need to update the docs, but you can now use headers for the api authentication instead of query params. You can change your request to be

curl -i -sS -X GET "https://site/unread.json" \
-H "Api-Key: api-key" \
-H "Api-Username: api-username"
5 Likes

This is very good to know – thanks!

1 Like

I updated the api docs for the /notifications endpoint and started a howto for this that we can build upon:

5 Likes