Enhance Telegram bot functionality and improve error handling

- Introduced a new function to set the default menu button for the Telegram bot's Web App.
- Updated the initData validation process to provide detailed error messages for authorization failures.
- Refactored the validate_init_data function to return both username and reason for validation failure.
- Enhanced the web application to handle access denial more gracefully, providing users with hints on how to access the calendar.
- Improved the README with additional instructions for configuring the bot's menu button and Web App URL.
- Updated tests to reflect changes in the validation process and error handling.
This commit is contained in:
2026-02-17 19:08:14 +03:00
parent 1948618394
commit dd960dc5cc
11 changed files with 171 additions and 59 deletions

33
main.py
View File

@@ -1,8 +1,10 @@
"""Single entry point: build Application, run HTTP server + polling. Migrations run in Docker entrypoint."""
import asyncio
import json
import logging
import threading
import urllib.request
import config
from telegram.ext import ApplicationBuilder
@@ -16,6 +18,36 @@ logging.basicConfig(
logger = logging.getLogger(__name__)
def _set_default_menu_button_webapp() -> None:
"""Set the bot's default menu button to Web App so Telegram sends tgWebAppData when users open the app from the menu."""
if not (config.MINI_APP_BASE_URL and config.BOT_TOKEN):
return
menu_url = (config.MINI_APP_BASE_URL.rstrip("/") + "/app/").strip()
if not menu_url.startswith("https://"):
return
payload = {
"menu_button": {
"type": "web_app",
"text": "Календарь",
"web_app": {"url": menu_url},
}
}
req = urllib.request.Request(
f"https://api.telegram.org/bot{config.BOT_TOKEN}/setChatMenuButton",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=10) as resp:
if resp.status == 200:
logger.info("Default menu button set to Web App: %s", menu_url)
else:
logger.warning("setChatMenuButton returned %s", resp.status)
except Exception as e:
logger.warning("Could not set menu button: %s", e)
def _run_uvicorn(web_app, port: int) -> None:
"""Run uvicorn in a dedicated thread with its own event loop."""
import uvicorn
@@ -29,6 +61,7 @@ def _run_uvicorn(web_app, port: int) -> None:
def main() -> None:
_set_default_menu_button_webapp()
app = ApplicationBuilder().token(config.BOT_TOKEN).build()
register_handlers(app)