import hmac import hashlib import json import os import time import urllib.parse from fastapi import Request, HTTPException from config import TELEGRAM_BOT_TOKEN DEV_MODE = os.environ.get("DEV_MODE", "").lower() in ("true", "1", "yes") def verify_init_data(init_data: str) -> dict | None: if DEV_MODE and init_data.startswith("dev_"): parts = init_data.split("_") return { "id": int(parts[1]) if len(parts) > 1 and parts[1].isdigit() else 5703679073, "first_name": parts[2] if len(parts) > 2 else "Dev", "last_name": "", "username": parts[3] if len(parts) > 3 else "devuser", "language_code": "en", "is_premium": False, } try: parsed = dict(urllib.parse.parse_qsl(init_data)) except Exception: return None hash_val = parsed.pop("hash", None) if not hash_val: return None auth_date = parsed.get("auth_date") if not auth_date: return None try: if time.time() - int(auth_date) > 86400: return None except (ValueError, TypeError): return None sorted_items = sorted(parsed.items()) check_str = "\n".join(f"{k}={v}" for k, v in sorted_items) secret_key = hmac.new(b"WebAppData", TELEGRAM_BOT_TOKEN.encode(), hashlib.sha256).digest() computed_hash = hmac.new(secret_key, check_str.encode(), hashlib.sha256).hexdigest() if not hmac.compare_digest(computed_hash, hash_val): return None try: user_info = json.loads(parsed.get("user", "{}")) except (json.JSONDecodeError, TypeError): user_info = {} return { "id": user_info.get("id"), "first_name": user_info.get("first_name", ""), "last_name": user_info.get("last_name", ""), "username": user_info.get("username", ""), "language_code": user_info.get("language_code", "en"), "is_premium": user_info.get("is_premium", False), "photo_url": user_info.get("photo_url", ""), } async def get_current_user(request: Request) -> dict: auth_header = request.headers.get("Authorization", "") if not auth_header.startswith("tma "): raise HTTPException(status_code=401, detail="Missing or invalid Authorization header") user_data = verify_init_data(auth_header[4:]) if not user_data or not user_data.get("id"): raise HTTPException(status_code=401, detail="Invalid init data") return user_data async def optional_current_user(request: Request) -> dict | None: auth_header = request.headers.get("Authorization", "") if not auth_header.startswith("tma "): return None return verify_init_data(auth_header[4:])