Python > Get post content if mentioned

I am playing around with python. To be specific:

I am looking this API docs:

Discourse API Docs / Discourse API Docs

I am new (learning) with Python, and am trying to simply receive the posts if @UserA gets mentioned somewhere. I was thinking I need to get the post_number from the notifications, and then query that post, to retrieve and store the post content.

From there I want to store that post content, as a data variable and from there experiment with some things.

A part of my code is this:

# lookup mentions
mention = client.notifications.json.get()

# print the posts
for post_number in mention:
    print(post_number)

The current output of this code:

notifications
total_rows_notifications
seen_notification_id
load_more_notifications

Sorry if this is a stupid question, I am relatively new with API’s and even newer with Python. I have been working with this all day without success, if anyone can point me in the right direction, that would be highly appreciated.

3 Likes

Thanks for posting the link to fluent-discourse!

I’m currently trying to get up to speed with Python, so it’s possible there are better ways of approaching this than what I’m outlining here.

mention = client.notifications.json.get()

This is returning a dictionary of mentions for the username you set when you created the client. I am assuming that you set the username to UserA when you created the client.

To confirm that a dictionary is being returned, you can run:

print(type(mention))

That should return <class 'dict'>

You can see the dictionary’s keys by running:

print(list(mention))

That will return ['notifications', 'total_rows_notifications', 'seen_notification_id', 'load_more_notifications']

For your case, you want the values that are found under the notifications key:

notifications = mention['notifications']

If you then run type(notifications), you’ll see that it is a list. To get notifications that were triggered by mentions of UserA, you need to iterate through the list to find entries where the notification_type is set to 1. The easiest way to see the values for the Discourse notification types is to run Notification.types from your Discourse site’s rails console:

 pry(main)> Notification.types
=> {:mentioned=>1,
 :replied=>2,
 :quoted=>3,
 :edited=>4,
 :liked=>5,
 ...

So to get “mention” notifications for the user whose username you’ve set for the client, try something like this (I’m testing this in the Python interpreter as I type it):

data = client.notifications.json.get()
notifications = data['notifications']
# Create an empty list object to store "mention" notifications
mentions = []
# Iterate through the `notifications` list to find all entries where the `notification_type` is set to `1`
for notification in notifications:
    if notification['notification_type'] == 1:
        mentions.append(notification)

That will create a list of notifications that were triggered by mentions. Each item in the list will be a dictionary. You can check it with something like:

for mention in mentions:
    print(mention['topic_id'], mention['post_number'])    
3 Likes

This is neat. Thanks for the info. There is not many information on Python x Discourse available out here, so happy to learn, and perhaps this will help others. I just tried your code, and it seems to work as expected. Of course, I didn’t stop playing, so I’ve found a workaround to get up until step 5, and a different way to get the values I needed all the way up to a raw paste of the post content.

What I want is basically this.

  1. Get notifications
  2. Get post_numbers
  3. Get post content
  4. Generate reply with OpenAI
  5. Post reply
  6. Mark that same notification as read?
# Set up a loop to run the bot indefinitely
while True:
    # Get user_actions and fetch post id    
    data = {
    'username': 'theuser',
    # For mentions and replies, I need to get filters 6 + 7, but for some reason only 1 json response is given.
    'filter': 6 | 7,
    'limit': 10,
    }
    
    unread_mentions = client.user_actions.json.get(data)
    print(unread_mentions)

    user_actions = unread_mentions['user_actions']

    # Loop over each unread mention
    for user_action in unread_mentions['user_actions']:
        topic_id = user_action['topic_id']
        post_number = user_action['post_number']
        post_id = user_action['post_id']
        username = user_action['username']

        print(f'Topic ID: {topic_id}, Post Number: {post_number}, Post ID: {post_id}, Reply to: {username}')
    
        # Get the post content   
        post = client.posts[post_id].json.get()

        # Print the post content
        post_content = post['raw']

        # Generate a response to the post
        response = generate_response(post_content)
    
        # Post a response to the forum
        content = {
            'topic_id': topic_id,
            'raw': response + ' @' + str(username)
        }
        make_post = client.posts.json.post(content)

Currently I’m having two problems:

  • Only 1 mention is fetched. I must’ve forgot something, but haven’t found out why yet.
  • Ofcourse we want to reply to a post only one time, and since we are looping we need something here to prevent this.
    • We can store the post_id in a .txt file (not sure how many data this can store, I think you can handle quite a few mentions here.
    • My ideal solution, was to mark the notification as read. But I came to realize in none of our code, we are fetching unread mentions (well, I tried at first, fetching unread notifications, but I was unable to extract/store the post_id variable which I needed to use /posts/get.json and fetch the “raw” content.

My end goal is to feed the response with OpenAI API. But I first need to find a way to sort out the problems above.

I’m assuming that what you want to do is generate a response from OpenAI when a given username (for example, UserA) is mentioned in a post. If that’s correct, instead of making API requests to Discourse to get the notifications, you could point a Discourse Notification Event webhook to your Python application’s server. The Python application would need to look at the webhook’s payload. If the payload’s notification_type was set to 1, and its user_id matched the ID of the user you are interested in getting notifications for, you’d make an API request to Discourse to get the post that’s set in the payload’s original_post_id field. Then send the post content that’s returned from that request on to OpenAI.

Here’s an example of what the payload for a mention notification looks like:

{
  "notification": {
    "id": 55267,
    "user_id": 2,
    "notification_type": 1,
    "read": false,
    "high_priority": false,
    "created_at": "2022-12-08T23:23:36.379Z",
    "post_number": 5,
    "topic_id": 277,
    "fancy_title": "I should be able to call this topic anything",
    "slug": "i-should-be-able-to-call-this-topic-anything",
    "data": {
      "topic_title": "I should be able to call this topic anything",
      "original_post_id": 10999,
      "original_post_type": 1,
      "original_username": "scossar",
      "revision_number": null,
      "display_username": "scossar"
    }
  }
}

The benefit of this approach is that it would let you get notifications in real time - without having to make large numbers of API requests to your Discourse site.

If you go with the webhook approach, it would be a good idea to set the webhook’s “Secret” field on Discourse, then validate the request on your Python application to make sure that the secret key is correctly set. I haven’t tried setting up a route on a Python application to do this yet. Details about how to validate a webhook’s Secret on WordPress are here: wp-discourse-custom-webhook-request/wp-discourse-custom-webhook-request.php at master · scossar/wp-discourse-custom-webhook-request · GitHub.

1 Like

Thanks for your reply, I highly appreciate this.

I never looked into Discourse webhooks. I will look into this.

1 Like

EDIT: I think I am mistaken. It looks like I am looking for type 1 and 2. Type 2 will trigger if there is a reply to the user. This makes this post basically irrelevant.While playing with this, the following question came to mind:

mentioned is loud and clear, type == 1 if there is a clear @Mention

But for replied I am kinda confused.

When I reply to the topic, through one of the general buttons, it comes in as a reply. Makes sense.

But if you click the reply button to a specific post from a user, one would think it’s a mention, but it is still a type 2 notification.

Shouldn’t a “reply to user” notification get type 1? Because it’s basically the exact same as a mention, just without the @Mention in the post.

For reference, since it is nowhere else on meta, posting the notification types here. Perhaps these types of notifications can get a new type, or just type 1?

[1] pry(main)> Notification.types
=> {:mentioned=>1,
 :replied=>2,
 :quoted=>3,
 :edited=>4,
 :liked=>5,
 :private_message=>6,
 :invited_to_private_message=>7,
 :invitee_accepted=>8,
 :posted=>9,
 :moved_post=>10,
 :linked=>11,
 :granted_badge=>12,
 :invited_to_topic=>13,
 :custom=>14,
 :group_mentioned=>15,
 :group_message_summary=>16,
 :watching_first_post=>17,
 :topic_reminder=>18,
 :liked_consolidated=>19,
 :post_approved=>20,
 :code_review_commit_approved=>21,
 :membership_request_accepted=>22,
 :membership_request_consolidated=>23,
 :bookmark_reminder=>24,
 :reaction=>25,
 :votes_released=>26,
 :event_reminder=>27,
 :event_invitation=>28,
 :chat_mention=>29,
 :chat_message=>30,
 :chat_invitation=>31,
 :chat_group_mention=>32,
 :chat_quoted=>33,
 :assigned=>34,
 :question_answer_user_commented=>35,
 :watching_category_or_tag=>36}
1 Like

I don’t think so. Mention notifications ("notification_type": 1) are meant for the specific case of when an @username is added to a post. Using the same notification type for direct replies would cause confusion.

There’s an edge case to be aware of. If a username is added to a reply to a user’s topic, the notification that’s sent to the user will be "notification_type": 1, not "notification_type": 2. For example, by mentioning your username here, this post should trigger a notification telling you that I’ve mentioned you, and not a notification saying that I replied to you: @MarcP.

Note that if a user other than yourself was watching this topic, they would get a notification with its type set to 9 (posted.)

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.