Initial release
Build and Push Docker Images / build (push) Successful in 17s

This commit is contained in:
Stardream
2026-06-02 10:58:24 +10:00
commit eace48732d
88 changed files with 11140 additions and 0 deletions
View File
+27
View File
@@ -0,0 +1,27 @@
from functools import lru_cache
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
bot_token: str = ""
webhook_url: str = ""
webhook_path: str = "/webhook/telegram"
webhook_secret: str = ""
database_url: str = "postgresql+asyncpg://postgres:postgres@postgres:5432/tglotterybot"
mini_app_url: str = ""
avatar_cache_ttl: int = 3600
debug: bool = False
@lru_cache()
def get_settings() -> Settings:
return Settings()
settings = get_settings()
+10
View File
@@ -0,0 +1,10 @@
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from app.core.config import settings
engine = create_async_engine(settings.database_url, echo=settings.debug)
AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False)
async def get_db():
async with AsyncSessionLocal() as session:
yield session
+59
View File
@@ -0,0 +1,59 @@
import hmac
import hashlib
import time
from urllib.parse import parse_qsl
from app.core.config import settings
# Reject initData older than this many seconds (24 hours)
_MAX_AGE_SECONDS = 86_400
def validate_init_data(init_data: str) -> dict | None:
"""Validate Telegram WebApp initData using HMAC-SHA256.
Also rejects payloads whose auth_date is older than _MAX_AGE_SECONDS to
prevent indefinite replay of leaked initData strings.
"""
try:
# parse_qsl already URL-decodes each value. Decoding the whole query
# string first can turn escaped separators inside JSON values into real
# separators and make Telegram's HMAC check fail.
parsed = dict(parse_qsl(init_data, keep_blank_values=True))
hash_value = parsed.pop("hash", "")
if not hash_value:
return None
# Validate auth_date freshness before doing any crypto work
auth_date_str = parsed.get("auth_date")
if not auth_date_str:
return None
try:
age = time.time() - int(auth_date_str)
if age > _MAX_AGE_SECONDS:
return None
except ValueError:
return None
data_check_string = "\n".join(
f"{k}={v}" for k, v in sorted(parsed.items())
)
secret_key = hmac.new(
b"WebAppData",
settings.bot_token.encode(),
hashlib.sha256,
).digest()
expected = hmac.new(
secret_key,
data_check_string.encode(),
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(expected, hash_value):
return None
return parsed
except Exception:
return None