Python > Получить содержимое поста, если упомянут

Я экспериментирую с Python. Если быть точным:

Я изучаю документацию к API:

Discourse API Docs / Discourse API Docs

Я новичок (учусь) в Python и пытаюсь просто получать сообщения, если @UserA где-то упоминается. Я думал, что мне нужно получить post_number из уведомлений, а затем запросить это сообщение, чтобы извлечь и сохранить его содержимое.

Далее я хочу сохранить это содержимое сообщения в переменную данных и экспериментировать с ним.

Часть моего кода выглядит так:

# поиск упоминаний
mention = client.notifications.json.get()

# вывод сообщений
for post_number in mention:
    print(post_number)

Текущий вывод этого кода:

notifications
total_rows_notifications
seen_notification_id
load_more_notifications

Извините, если вопрос глупый, я относительно новичок в работе с API и ещё более неопытен в Python. Я работаю над этим весь день без успеха; если кто-то сможет указать мне правильное направление, это было бы очень ценно.

Спасибо за ссылку на fluent-discourse!

Сейчас я активно изучаю Python, поэтому вполне возможно, что существуют более эффективные подходы к решению этой задачи, чем тот, который я опишу ниже.

mention = client.notifications.json.get()

Эта команда возвращает словарь уведомлений об упоминаниях для username, который вы указали при создании client. Я предполагаю, что вы установили username в значение UserA при создании client.

Чтобы убедиться, что возвращается словарь, выполните:

print(type(mention))

Это должно вернуть <class 'dict'>.

Вы можете увидеть ключи словаря, выполнив:

print(list(mention))

Это вернет ['notifications', 'total_rows_notifications', 'seen_notification_id', 'load_more_notifications'].

В вашем случае вам нужны значения, находящиеся под ключом notifications:

notifications = mention['notifications']

Если вы затем выполните type(notifications), то увидите, что это список. Чтобы получить уведомления, сработавшие из-за упоминания UserA, нужно пройтись по списку и найти записи, где notification_type установлен в 1. Самый простой способ увидеть значения типов уведомлений Discourse — выполнить Notification.types в консоли Rails вашего сайта Discourse:

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

Итак, чтобы получить уведомления типа «упомянут» для пользователя, чье имя вы указали в client, попробуйте что-то вроде этого (я тестирую это в интерпретаторе Python по мере набора текста):

data = client.notifications.json.get()
notifications = data['notifications']
# Создаем пустой список для хранения уведомлений типа «упомянут»
mentions = []
# Проходим по списку `notifications`, чтобы найти все записи, где `notification_type` равен `1`
for notification in notifications:
    if notification['notification_type'] == 1:
        mentions.append(notification)

Это создаст список уведомлений, сработавших из-за упоминаний. Каждый элемент списка будет словарем. Вы можете проверить это, выполнив что-то вроде:

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

Это здорово. Спасибо за информацию. В сети не так много материалов по Python x Discourse, поэтому я рад научиться новому, и, возможно, это поможет и другим. Я только что попробовал ваш код, и он, кажется, работает как ожидалось. Конечно, я не остановился на достигнутом, поэтому нашёл обходной путь, чтобы дойти до шага 5, а также другой способ получить нужные мне значения вплоть до сырого текста поста.

Вот что я хочу получить в итоге:

  1. Получать уведомления
  2. Получать номера постов (post_numbers)
  3. Получать содержимое поста
  4. Генерировать ответ с помощью OpenAI
  5. Отправлять ответ
  6. Помечать то же самое уведомление как прочитанное?
# Настройка бесконечного цикла для работы бота
while True:
    # Получение user_actions и извлечение id поста
    data = {
    'username': 'theuser',
    # Для упоминаний и ответов мне нужны фильтры 6 и 7, но по какой-то причине возвращается только один JSON-ответ.
    'filter': 6 | 7,
    'limit': 10,
    }
    
    unread_mentions = client.user_actions.json.get(data)
    print(unread_mentions)

    user_actions = unread_mentions['user_actions']

    # Перебор каждого непрочитанного упоминания
    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}')
    
        # Получение содержимого поста
        post = client.posts[post_id].json.get()

        # Вывод содержимого поста
        post_content = post['raw']

        # Генерация ответа на пост
        response = generate_response(post_content)
    
        # Отправка ответа на форум
        content = {
            'topic_id': topic_id,
            'raw': response + ' @' + str(username)
        }
        make_post = client.posts.json.post(content)

В настоящее время у меня две проблемы:

  • Запрашивается только одно упоминание. Должно быть, я что-то упустил, но пока не понял, почему.
  • Конечно, мы хотим отвечать на пост только один раз, и поскольку у нас цикл, нам нужно что-то, чтобы предотвратить повторные ответы.
    • Мы можем хранить post_id в файле .txt (не уверен, сколько данных он может вместить, но, думаю, вы сможете обработать довольно много упоминаний).
    • Идеальное решение для меня — помечать уведомление как прочитанное. Но я понял, что ни в одном из наших кодов мы не запрашиваем непрочитанные упоминания (хотя я сначала пытался получить непрочитанные уведомления, но не смог извлечь/сохранить переменную post_id, которая мне нужна для вызова /posts/get.json и получения содержимого “raw”.

Моя конечная цель — использовать ответ с API OpenAI. Но сначала мне нужно решить вышеуказанные проблемы.

Полагаю, вы хотите, чтобы при упоминании определённого имени пользователя (например, UserA) в сообщении генерировался ответ от OpenAI. Если это так, вместо того чтобы делать API-запросы к Discourse для получения уведомлений, вы можете настроить вебхук события уведомлений Discourse на ваш сервер с Python-приложением. Приложению на Python потребуется анализировать полезную нагрузку вебхука. Если поле notification_type в полезной нагрузке установлено в 1, а user_id совпадает с ID пользователя, для которого вы хотите получать уведомления, вы сделаете API-запрос к Discourse, чтобы получить сообщение, указанное в поле original_post_id полезной нагрузки. Затем отправьте содержимое сообщения, полученное в результате этого запроса, в OpenAI.

Вот пример полезной нагрузки для уведомления об упоминании:

{
  "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"
    }
  }
}

Преимущество этого подхода заключается в том, что он позволяет получать уведомления в реальном времени, не выполняя множество API-запросов к вашему сайту Discourse.

Если вы выберете подход с вебхуком, рекомендуется установить поле «Секрет» вебхука в Discourse, а затем проверять запрос в вашем Python-приложении, чтобы убедиться, что секретный ключ установлен правильно. Я ещё не пробовал настраивать маршрут в Python-приложении для этого. Подробная информация о том, как проверить секрет вебхука в WordPress, доступна здесь: wp-discourse-custom-webhook-request/wp-discourse-custom-webhook-request.php at master · scossar/wp-discourse-custom-webhook-request · GitHub.

Спасибо за ваш ответ, я очень это ценю.

Я никогда не изучал вебхуки Discourse. Я займусь этим.

РЕДАКТИРОВАНИЕ: Я, кажется, ошибался. Похоже, мне нужны типы 1 и 2. Тип 2 срабатывает, если есть ответ пользователю. Это делает этот пост практически нерелевантным. Пока я экспериментировал с этим, у меня возник следующий вопрос:

mentioned (упомянут) звучит ясно: тип == 1, если есть явное @упомяновение.

Но с replied (ответ) я немного запутался.

Когда я отвечаю на тему через одну из общих кнопок, это приходит как ответ. Это логично.

Но если нажать кнопку ответа на конкретный пост от пользователя, можно подумать, что это упоминание, но это всё равно уведомление типа 2.

Это должно быть уведомление типа упоминания

Разве уведомление «ответ пользователю» не должно быть типа 1? Потому что это по сути то же самое, что и упоминание, просто без @упоминания в посте.

Для справки, поскольку нигде больше на meta этого нет, публикую здесь типы уведомлений. Возможно, таким уведомлениям можно присвоить новый тип или просто тип 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}

Я так не думаю. Уведомления об упоминаниях (“notification_type”: 1) предназначены для конкретного случая, когда в сообщение добавляется @username. Использование того же типа уведомления для прямых ответов вызвало бы путаницу.

Существует пограничный случай, о котором следует знать. Если имя пользователя добавляется в ответ на тему пользователя, уведомление, отправляемое пользователю, будет иметь “notification_type”: 1, а не “notification_type”: 2. Например, упомянув здесь ваше имя пользователя, это сообщение должно вызвать уведомление о том, что я упомянул вас, а не уведомление о том, что я ответил вам: @MarcP.

Обратите внимание, что если бы эту тему отслеживал кто-то другой, кроме вас, они получили бы уведомление с типом, установленным в 9 (опубликовано).