from __future__ import annotations import logging import re from typing import Any logger = logging.getLogger(__name__) _COMMAND_NAME_RE = re.compile(r"^[a-z0-9_]{1,32}$") TELEGRAM_API_BASE = "https://api.telegram.org" def normalize_command_name(raw: str) -> str: cleaned = raw.lstrip("/").replace("-", "_").strip().lower() return cleaned if _COMMAND_NAME_RE.match(cleaned) else "" def build_command_list(custom_commands: list[dict]) -> list[dict]: commands: list[dict] = [] seen: set[str] = set() for cmd in custom_commands: command = normalize_command_name(cmd.get("command", "")) description = str(cmd.get("description", ""))[:256] if command and command not in seen: commands.append({"command": command, "description": description}) seen.add(command) return commands async def register_commands(token: str, commands: list[dict], scope: dict | None = None) -> bool: import httpx payload: dict = {"commands": [{"command": c["command"], "description": c["description"]} for c in commands]} if scope: payload["scope"] = scope try: async with httpx.AsyncClient(timeout=httpx.Timeout(10.0)) as client: resp = await client.post( f"{TELEGRAM_API_BASE}/bot{token}/setMyCommands", json=payload, ) data = resp.json() if resp.content else {} ok = resp.status_code == 200 and data.get("ok", False) if not ok: logger.warning("setMyCommands failed: %s", data.get("description", "")) return ok except Exception: logger.exception("setMyCommands network error") return False async def delete_commands(token: str, scope: dict | None = None) -> bool: import httpx payload: dict = {} if scope: payload["scope"] = scope try: async with httpx.AsyncClient(timeout=httpx.Timeout(10.0)) as client: resp = await client.post( f"{TELEGRAM_API_BASE}/bot{token}/deleteMyCommands", json=payload, ) data = resp.json() if resp.content else {} return resp.status_code == 200 and data.get("ok", False) except Exception: logger.exception("deleteMyCommands network error") return False async def get_commands( token: str, *, scope: dict | None = None, language_code: str | None = None, ) -> list[dict] | None: import httpx payload: dict = {} if scope: payload["scope"] = scope if language_code: payload["language_code"] = language_code try: async with httpx.AsyncClient(timeout=httpx.Timeout(10.0)) as client: resp = await client.get( f"{TELEGRAM_API_BASE}/bot{token}/getMyCommands", params=payload, ) data = resp.json() if resp.content else {} if resp.status_code == 200 and data.get("ok"): return data.get("result", []) except Exception: logger.exception("getMyCommands network error") return None async def get_default_administrator_rights(token: str) -> dict | None: import httpx try: async with httpx.AsyncClient(timeout=httpx.Timeout(10.0)) as client: resp = await client.get( f"{TELEGRAM_API_BASE}/bot{token}/getMyDefaultAdministratorRights", ) data = resp.json() if resp.content else {} if resp.status_code == 200 and data.get("ok"): return data.get("result", {}) except Exception: logger.exception("getMyDefaultAdministratorRights network error") return None async def set_default_administrator_rights( token: str, *, rights: dict | None = None, for_channels: bool = False, ) -> bool: import httpx payload: dict[str, Any] = {"for_channels": for_channels} if rights is not None: payload["rights"] = rights else: payload["rights"] = None try: async with httpx.AsyncClient(timeout=httpx.Timeout(10.0)) as client: resp = await client.post( f"{TELEGRAM_API_BASE}/bot{token}/setMyDefaultAdministratorRights", json=payload, ) data = resp.json() if resp.content else {} return resp.status_code == 200 and data.get("ok", False) except Exception: logger.exception("setMyDefaultAdministratorRights network error") return False