""" Telegram Mini App — валидация initData. Telegram передаёт подписанные данные при открытии WebApp. Мы проверяем HMAC-SHA256 подпись через BOT_TOKEN, чтобы убедиться что запрос пришёл от реального пользователя. Документация: https://core.telegram.org/bots/webapps#validating-data """ import hashlib import hmac import json import os import time from urllib.parse import parse_qs BOT_TOKEN = os.getenv("BOT_TOKEN", "") def validate_init_data(init_data: str, max_age_seconds: int = 86400) -> dict | None: """ Валидирует initData из Telegram WebApp. Возвращает dict с данными пользователя при успехе, None при ошибке. """ if not init_data or not BOT_TOKEN: return None try: parsed = parse_qs(init_data, keep_blank_values=True) # Извлекаем hash received_hash = parsed.get("hash", [None])[0] if not received_hash: return None # Собираем data-check-string (все поля кроме hash, отсортированные) data_pairs = [] for key, values in parsed.items(): if key == "hash": continue data_pairs.append(f"{key}={values[0]}") data_pairs.sort() data_check_string = "\n".join(data_pairs) # Вычисляем секретный ключ secret_key = hmac.new( b"WebAppData", BOT_TOKEN.encode("utf-8"), hashlib.sha256 ).digest() # Вычисляем HMAC computed_hash = hmac.new( secret_key, data_check_string.encode("utf-8"), hashlib.sha256 ).hexdigest() if not hmac.compare_digest(computed_hash, received_hash): return None # Проверяем auth_date (не старше max_age_seconds) auth_date_str = parsed.get("auth_date", [None])[0] if auth_date_str: auth_date = int(auth_date_str) if time.time() - auth_date > max_age_seconds: return None # Парсим user user_str = parsed.get("user", [None])[0] if not user_str: return None user_data = json.loads(user_str) return { "user_id": user_data.get("id"), "first_name": user_data.get("first_name", ""), "last_name": user_data.get("last_name", ""), "username": user_data.get("username", ""), "language_code": user_data.get("language_code", "ru"), } except Exception: return None