Skip to content

Latest commit

 

History

History
265 lines (196 loc) · 7.37 KB

File metadata and controls

265 lines (196 loc) · 7.37 KB

telegram-init-data

PyPI version Python versions PyPI Downloads License: MIT

Python library for parsing, validating, and signing Telegram Mini Apps initialization data on the server side. API-compatible in spirit with @tma.js/init-data-node.

Features

  • Validate init data signature and expiration
  • Parse URL-encoded init data into typed Python objects
  • Sign init data for testing and development
  • Full type hints
  • Optional FastAPI integration
  • Third-party validation (data signed by Telegram directly)

Installation

pip install telegram-init-data

With FastAPI support:

pip install telegram-init-data[fastapi]

Quick Start

Validation

from telegram_init_data import validate, parse

bot_token = "YOUR_BOT_TOKEN"
init_data = "query_id=AAHdF6IQAAAAAN0XohDhrOrc&user=%7B%22id%22%3A279058397%2C%22first_name%22%3A%22Vladislav%22%2C%22last_name%22%3A%22Kibenko%22%2C%22username%22%3A%22vdkfrost%22%2C%22language_code%22%3A%22ru%22%2C%22is_premium%22%3Atrue%7D&auth_date=1662771648&hash=c501b71e775f74ce10e377dea85a7ea24ecd640b223ea86dfe453e0eaed2e2b2"

try:
    validate(init_data, bot_token)
    parsed = parse(init_data)
    print(parsed["user"]["first_name"])
except Exception as e:
    print(f"Validation failed: {e}")

FastAPI

from fastapi import FastAPI, Depends, HTTPException
from telegram_init_data import validate, parse

app = FastAPI()

def verify_init_data(init_data: str) -> dict:
    bot_token = "YOUR_BOT_TOKEN"
    try:
        validate(init_data, bot_token)
        return parse(init_data)
    except Exception as e:
        raise HTTPException(status_code=401, detail=str(e))

@app.post("/user/profile")
async def get_profile(init_data: dict = Depends(verify_init_data)):
    user = init_data.get("user")
    if not user:
        raise HTTPException(status_code=400, detail="User data not found")
    return {"user_id": user["id"], "name": user["first_name"]}

Signing (tests / development)

from telegram_init_data import sign, is_valid
from datetime import datetime

bot_token = "YOUR_BOT_TOKEN"

test_data = {
    "query_id": "test_query_id",
    "user": {
        "id": 123456789,
        "first_name": "John",
        "last_name": "Doe",
        "username": "johndoe",
        "language_code": "en",
    },
    "auth_date": datetime.now(),
}

signed_data = sign(test_data, bot_token, datetime.now())

if is_valid(signed_data, bot_token):
    print("Valid")

API Reference

validate(value, token, options=None)

Validate Telegram Mini App init data.

Parameter Type Description
value `str dict`
token str Bot token from @BotFather
options dict, optional expires_in (seconds, default 86400)

Raises: SignatureMissingError, AuthDateInvalidError, ExpiredError, SignatureInvalidError

is_valid(value, token, options=None)

Same checks as validate, returns bool instead of raising.

parse(value)

Parse init data into a structured object. Returns InitData.

sign(data, token, auth_date, options=None)

Sign init data for testing. Returns a URL-encoded string.

Types

class InitData(TypedDict):
    query_id: Optional[str]
    user: Optional[User]
    receiver: Optional[User]
    chat: Optional[Chat]
    chat_type: Optional[ChatType]
    chat_instance: Optional[str]
    start_param: Optional[str]
    can_send_after: Optional[int]
    auth_date: int
    hash: str
    signature: Optional[str]

class User(TypedDict):
    id: int
    first_name: str
    last_name: Optional[str]
    username: Optional[str]
    language_code: Optional[str]
    is_bot: Optional[bool]
    is_premium: Optional[bool]
    added_to_attachment_menu: Optional[bool]
    allows_write_to_pm: Optional[bool]
    photo_url: Optional[str]

class Chat(TypedDict):
    id: int
    type: ChatType
    title: Optional[str]
    username: Optional[str]
    photo_url: Optional[str]

class ChatType(str, Enum):
    SENDER = "sender"
    PRIVATE = "private"
    GROUP = "group"
    SUPERGROUP = "supergroup"
    CHANNEL = "channel"

Exceptions

Exception When
TelegramInitDataError Base class
AuthDateInvalidError Invalid or missing auth_date
SignatureInvalidError Signature mismatch
SignatureMissingError Missing hash / signature
ExpiredError Init data expired

Options

# Custom TTL (1 hour)
validate(init_data, bot_token, {"expires_in": 3600})

# Disable expiration check
validate(init_data, bot_token, {"expires_in": 0})

Testing

pip install -e ".[dev]"
pytest
pytest --cov=telegram_init_data --cov-report=html

Examples

See [examples/](examples/) for basic usage and a FastAPI app.

FastAPI with Authorization header

from fastapi import FastAPI, Depends, HTTPException, Header
from telegram_init_data import parse, is_valid

app = FastAPI()

def get_init_data(authorization: str = Header(None)):
    if not authorization:
        raise HTTPException(status_code=401, detail="Authorization header missing")
    if not authorization.startswith("tma "):
        raise HTTPException(status_code=401, detail="Invalid authorization format")

    init_data = authorization[4:]
    bot_token = "YOUR_BOT_TOKEN"

    if not is_valid(init_data, bot_token):
        raise HTTPException(status_code=401, detail="Invalid init data")

    return parse(init_data)

@app.get("/me")
async def get_current_user(init_data: dict = Depends(get_init_data)):
    user = init_data.get("user")
    if not user:
        raise HTTPException(status_code=400, detail="User data not found")
    return {
        "id": user["id"],
        "name": user.get("first_name", ""),
        "username": user.get("username"),
        "is_premium": user.get("is_premium", False),
    }

Development

git clone https://github.com/iCodeCraft/telegram-init-data.git
cd telegram-init-data
python -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
pytest
black telegram_init_data tests
isort telegram_init_data tests
mypy telegram_init_data

License

MIT. See LICENSE.

Related

Changelog

See CHANGELOG.md.