# -*- coding: utf-8 -*- """Telegram getUpdates long polling 与按钮回调分发。""" from __future__ import annotations import time from typing import Any, Mapping class TelegramPoller: def __init__(self, bot, actions, *, logger=None, sleeper=time.sleep) -> None: self.bot = bot self.actions = actions self.logger = logger self._sleep = sleeper def _log(self, level: str, event: str, **fields) -> None: if self.logger is not None: getattr(self.logger, level)(event, **fields) def run_once(self, offset: int | None = None) -> int | None: updates = self.bot.get_updates(offset) for update in updates: update_id = update.get("update_id") if isinstance(update_id, int) and not isinstance(update_id, bool): offset = max(offset or 0, update_id + 1) callback = update.get("callback_query") if isinstance(callback, Mapping): self.handle_callback(callback) return offset def handle_callback(self, callback: Mapping[str, Any]) -> None: callback_id = str(callback.get("id") or "") message = callback.get("message") or {} chat_id = (message.get("chat") or {}).get("id") from_id = (callback.get("from") or {}).get("id") if chat_id != self.bot.chat_id or from_id != self.bot.chat_id: self._log( "warning", "telegram.unauthorized", chat_id=chat_id, from_id=from_id, ) self._answer(callback_id, "无权限") return action, _, raw_aid = str(callback.get("data") or "").partition(":") try: aid = int(raw_aid) except (TypeError, ValueError): self._answer(callback_id, "未知操作") return if action == "fav": result = self.actions.favorite(aid) elif action == "ign": result = self.actions.ignore(aid) elif action == "blk": result = self.actions.blacklist(aid) else: self._answer(callback_id, "未知操作") return text = result.message if result.ok and result.position is not None: text = f"{text}(位置 #{result.position})" self._log( "info", "telegram.action", action=action, aid=aid, ok=result.ok, state=result.state, position=result.position, ) self._answer(callback_id, text) def _answer(self, callback_id: str, text: str) -> None: try: self.bot.answer_callback(callback_id, text) except Exception as exc: # noqa: BLE001 self._log("error", "telegram.answer_failed", error=str(exc)) def run_forever(self, stop_event=None) -> None: offset: int | None = None while stop_event is None or not stop_event.is_set(): try: offset = self.run_once(offset) except Exception as exc: # noqa: BLE001 长轮询失败后退避重试 self._log("error", "telegram.poll_failed", error=str(exc)) self._sleep(3)