Fix poll handler for kurigram compatibility

Changes:
- Replace PollAnswerHandler with RawUpdateHandler
- Handle UpdateMessagePollVote instead of PollAnswer
- Parse options as Vector<bytes> per Telegram API spec
- Extract user_id from PeerUser properly
- Add test_kurigram_compat.py for validation
- Update STATISTICS_GUIDE.md with correct handler info

Fixes ImportError with kurigram's handler structure.
Now compatible with Telegram MTProto API Layer 222.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-05 12:33:52 +02:00
parent ab8e129277
commit 6c6ef263be
3 changed files with 152 additions and 43 deletions

View File

@@ -2,9 +2,8 @@ from __future__ import annotations
import logging
from pyrogram import Client
from pyrogram.handlers import PollAnswerHandler
from pyrogram.types import PollAnswer
from pyrogram import Client, raw
from pyrogram.handlers import RawUpdateHandler
from game import SQLiteWouldYouRatherRepository
@@ -19,45 +18,70 @@ class PollStatisticsHandler:
def register(self, client: Client) -> None:
"""Регистрирует обработчик в клиенте Pyrogram."""
client.add_handler(PollAnswerHandler(self._on_poll_answer))
client.add_handler(RawUpdateHandler(self._on_raw_update))
logger.info("Poll statistics handler registered.")
async def _on_poll_answer(self, _: Client, poll_answer: PollAnswer) -> None:
"""Обрабатывает ответ пользователя на опрос."""
async def _on_raw_update(self, client: Client, update, users, chats) -> None:
"""Обрабатывает raw update от Telegram."""
# Проверяем, является ли это обновлением голосования в опросе
if not isinstance(update, raw.types.UpdateMessagePollVote):
return
try:
poll_id = poll_answer.poll_id
if not poll_id:
return
# Получаем question_id по poll_id
question_id = self.repository.get_question_id_by_poll(poll_id)
if question_id is None:
logger.debug("Poll %s not found in mappings, skipping.", poll_id)
return
# Обрабатываем выбранные опции
option_ids = poll_answer.option_ids or []
for option_index in option_ids:
# Получаем answer_id по индексу опции
answer_id = self.repository.get_answer_id_by_option_index(
question_id, option_index
)
if answer_id is None:
logger.warning(
"Answer not found for question %s option %s",
question_id,
option_index,
)
continue
# Записываем статистику
self.repository.record_answer(question_id, answer_id, increment=1)
logger.debug(
"Recorded answer: question=%s, answer=%s, user=%s",
question_id,
answer_id,
poll_answer.user.id if poll_answer.user else "unknown",
)
await self._process_poll_vote(update)
except Exception:
logger.exception("Failed to process poll answer: %s", poll_answer)
logger.exception("Failed to process poll vote: %s", update)
async def _process_poll_vote(self, update: raw.types.UpdateMessagePollVote) -> None:
"""Обрабатывает голос пользователя в опросе."""
poll_id = str(update.poll_id)
# Получаем question_id по poll_id
question_id = self.repository.get_question_id_by_poll(poll_id)
if question_id is None:
logger.debug("Poll %s not found in mappings, skipping.", poll_id)
return
# Обрабатываем выбранные опции (каждая опция - это bytes объект)
option_bytes_list = update.options or []
# Получаем user_id из peer
peer = update.peer
if isinstance(peer, raw.types.PeerUser):
user_id = peer.user_id
else:
user_id = "unknown"
for option_data in option_bytes_list:
# В Telegram API каждая опция - это один байт с индексом
# option_data это bytes объект, берём первый байт как индекс
try:
if isinstance(option_data, bytes):
option_index = option_data[0] if len(option_data) > 0 else 0
else:
option_index = int(option_data)
except (ValueError, TypeError, IndexError) as e:
logger.warning("Invalid option data: %s (%s)", option_data, e)
continue
# Получаем answer_id по индексу опции
answer_id = self.repository.get_answer_id_by_option_index(
question_id, option_index
)
if answer_id is None:
logger.warning(
"Answer not found for question %s option %s",
question_id,
option_index,
)
continue
# Записываем статистику
self.repository.record_answer(question_id, answer_id, increment=1)
logger.debug(
"Recorded answer: question=%s, answer=%s, option=%s, user=%s",
question_id,
answer_id,
option_index,
user_id,
)