Spaces:
Running
Running
| # core/tools/telegram_bot.py | |
| from __future__ import annotations | |
| import os | |
| import requests | |
| from typing import Any, Dict, Optional | |
| def _env(name: str) -> str: | |
| return os.getenv(name, "").strip() | |
| def tg_token() -> str: | |
| return _env("TELEGRAM_BOT_TOKEN") | |
| def tg_api(method: str) -> str: | |
| return f"https://api.telegram.org/bot{tg_token()}/{method}" | |
| def send_message( | |
| chat_id: int, | |
| text: str, | |
| reply_to_message_id: Optional[int] = None, | |
| reply_markup: Optional[Dict[str, Any]] = None, | |
| disable_web_page_preview: Optional[bool] = None, | |
| ) -> Dict[str, Any]: | |
| """ | |
| sendMessage wrapper. | |
| - reply_markup: e.g. {"inline_keyboard": [[{"text":"...", "callback_data":"..."}]]} | |
| """ | |
| if not tg_token(): | |
| return {"ok": False, "error": "missing_token"} | |
| payload: Dict[str, Any] = {"chat_id": chat_id, "text": text} | |
| # Optional: reply to the triggering message | |
| if reply_to_message_id is not None: | |
| payload["reply_to_message_id"] = reply_to_message_id | |
| if reply_markup is not None: | |
| payload["reply_markup"] = reply_markup | |
| if disable_web_page_preview is not None: | |
| payload["disable_web_page_preview"] = bool(disable_web_page_preview) | |
| r = requests.post(tg_api("sendMessage"), json=payload, timeout=12) | |
| try: | |
| return r.json() | |
| except Exception: | |
| return {"ok": False, "status": r.status_code, "text": r.text} | |
| def edit_message_text( | |
| chat_id: int, | |
| message_id: int, | |
| text: str, | |
| reply_markup: Optional[Dict[str, Any]] = None, | |
| disable_web_page_preview: Optional[bool] = None, | |
| ) -> Dict[str, Any]: | |
| """ | |
| editMessageText wrapper (useful to update status messages after button click) | |
| """ | |
| if not tg_token(): | |
| return {"ok": False, "error": "missing_token"} | |
| payload: Dict[str, Any] = {"chat_id": chat_id, "message_id": message_id, "text": text} | |
| if reply_markup is not None: | |
| payload["reply_markup"] = reply_markup | |
| if disable_web_page_preview is not None: | |
| payload["disable_web_page_preview"] = bool(disable_web_page_preview) | |
| r = requests.post(tg_api("editMessageText"), json=payload, timeout=12) | |
| try: | |
| return r.json() | |
| except Exception: | |
| return {"ok": False, "status": r.status_code, "text": r.text} | |
| def answer_callback_query( | |
| callback_query_id: str, | |
| text: Optional[str] = None, | |
| show_alert: bool = False, | |
| ) -> Dict[str, Any]: | |
| """ | |
| answerCallbackQuery wrapper: stops the Telegram button spinner. | |
| """ | |
| if not tg_token(): | |
| return {"ok": False, "error": "missing_token"} | |
| payload: Dict[str, Any] = {"callback_query_id": callback_query_id, "show_alert": bool(show_alert)} | |
| if text: | |
| payload["text"] = str(text) | |
| r = requests.post(tg_api("answerCallbackQuery"), json=payload, timeout=10) | |
| try: | |
| return r.json() | |
| except Exception: | |
| return {"ok": False, "status": r.status_code, "text": r.text} | |
| def send_chat_action(chat_id: int, action: str = "typing") -> None: | |
| if not tg_token(): | |
| return | |
| try: | |
| requests.post(tg_api("sendChatAction"), json={"chat_id": chat_id, "action": action}, timeout=8) | |
| except Exception: | |
| pass | |
| def is_allowed(chat_id: int) -> bool: | |
| allow = _env("TELEGRAM_ALLOWED_CHAT_IDS") | |
| if not allow: | |
| return True | |
| allowed = {c.strip() for c in allow.split(",") if c.strip()} | |
| return str(chat_id) in allowed | |