🛡️ Скрепа

Реакции в приватном диалоге

Что получится

Бот поставит 👍 на сообщение пользователя, а изменения реакции пользователя придут приложению как durable message_reaction updates с прежним и новым состоянием.

Что понадобится

message_reaction исключён из default-набора Telegram updates. Его требуется явно перечислить в allowed_updates; без этого обработчик не увидит реакцию.

Запускаем бота

python3 -m venv .venv
.venv/bin/python -m pip install 'python-telegram-bot==22.8'

Сохраните как reaction_bot.py:

import os

from telegram import ReactionTypeEmoji, Update
from telegram.ext import (
    Application,
    ContextTypes,
    MessageHandler,
    MessageReactionHandler,
    filters,
)


async def react(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    message = update.effective_message
    await context.bot.set_message_reaction(
        chat_id=message.chat_id,
        message_id=message.message_id,
        reaction=[ReactionTypeEmoji("👍")],
    )
    await message.reply_text("Поставил 👍. Теперь поставьте свою реакцию на этот ответ.")


async def changed(update: Update, _context: ContextTypes.DEFAULT_TYPE) -> None:
    change = update.message_reaction
    old = [reaction.to_dict() for reaction in change.old_reaction]
    new = [reaction.to_dict() for reaction in change.new_reaction]
    print(
        f"chat_id={change.chat.id} message_id={change.message_id} "
        f"old={old} new={new}",
        flush=True,
    )


application = (
    Application.builder()
    .token(os.environ["SKREPA_BOT_TOKEN"])
    .base_url("http://127.0.0.1:8081/bot")
    .base_file_url("http://127.0.0.1:8081/file/bot")
    .build()
)
application.add_handler(
    MessageHandler(filters.TEXT & filters.ChatType.PRIVATE, react)
)
application.add_handler(MessageReactionHandler(changed))
application.run_polling(
    allowed_updates=["message", "message_reaction"]
)

Запустите .venv/bin/python reaction_bot.py.

Действия в клиенте Скрепы

  1. Отправьте боту любое текстовое сообщение.
  2. Убедитесь, что на сообщении появилась 👍.
  3. Поставьте или смените реакцию на сообщении бота.
  4. Снимите эту реакцию.

Для каждого фактического изменения терминал покажет old=[...] new=[...]. При снятии реакции new будет пустым. Повторная установка уже выбранного emoji и повторное снятие являются идемпотентными и второго update не создают.

Снять реакцию программно можно пустым списком:

await context.bot.set_message_reaction(
    chat_id=chat_id,
    message_id=message_id,
    reaction=[],
)

Проверяем через curl

Подставьте chat_id и message_id из входящего message update:

set -o pipefail
curl --fail-with-body -sS \
  -H 'content-type: application/json' \
  -d '{
    "chat_id": 217020518514230,
    "message_id": 391284761928,
    "reaction": [{"type": "emoji", "emoji": "👍"}]
  }' \
  "http://127.0.0.1:8081/bot${SKREPA_BOT_TOKEN}/setMessageReaction" | jq .

Ответ {"ok":true,"result":true} означает, что новое состояние принято.

Типичные ошибки

Границы поддержки

Бот устанавливает одну обычную emoji-реакцию или снимает её в private E2E-DM. Входящая реакция пользователя может содержать ReactionTypeCustomEmoji, хотя бот сам такую реакцию установить пока не может. Реакции групп и каналов, несколько одновременных реакций, paid reactions, anonymous counts и message_reaction_count в текущий профиль не входят.