Route all nalog.ru API traffic through SOCKS proxy. Uses NALOGO_PROXY_URL
env var (falls back to PROXY_URL if not set). Adds httpx[socks] dependency.
- Thread proxy_url through Client → AuthProviderImpl + AsyncHTTPClient
- Extract mask_proxy_url() and sanitize_proxy_error() utilities
- Add socks5h:// scheme support for remote DNS resolution
- Sanitize proxy credentials in error messages
- Log masked proxy URL at startup and service init
Replace all ~45 direct Bot() calls across the codebase with a centralized
create_bot() factory function that automatically configures SOCKS5 proxy
session when PROXY_URL is set. This ensures proxy support applies uniformly
to all Telegram API traffic.
Key changes:
- Add app/bot_factory.py with create_bot() factory
- Replace direct Bot() instantiation in 33 files
- Fix session leaks in cloudpayments.py and auth.py (async with)
- Replace 2 direct httpx calls to api.telegram.org with
bot.create_invoice_link() (balance.py, wheel.py)
- Remove now-unused imports (Bot, DefaultBotProperties, ParseMode, httpx)
Route bot traffic through SOCKS5 proxy when PROXY_URL env var is set.
Validates scheme to reject HTTP proxies (would expose bot token).
Credentials are masked in logs.
- Add menu_layout_cache.py for CABINET_MENU_LAYOUT in-process cache
- Add admin_menu_layout.py routes (GET/PUT/POST reset) with merged view
- Rewrite _build_cabinet_main_menu_keyboard to use cached row layout
- Support custom URL buttons with style, emoji, labels, enabled toggle
- Atomic dual-key DB writes for layout + button styles
- Add language button to default layout and DEFAULT_BUTTON_STYLES
- Pydantic validation with Literal types, max_length, duplicate ID checks
- Register routes and cache loading in bot startup
- New gift_activation handler for gift_activate:{id} callback buttons
- Send Telegram notification to gift recipients with activate button
- Add skip_notification param to activate_purchase to prevent duplicates
- Fix telegram username regex minimum length (4→5 chars) in landing routes
- Add BOT_TOKEN guard in telegram gift notification sender
- Pre-resolve notification params before commit to avoid DetachedInstanceError
Drop all messages and callback queries from non-private chats
(groups, supergroups with forum topics, channels) before they
reach any handler or heavy middleware (DB, throttle, blacklist).
- Registered after ContextVarsMiddleware, before GlobalErrorMiddleware
- chat_member events intentionally excluded (needed for channel tracking)
- pre_checkout_query excluded (no chat context, always private)
- Uses ChatType.PRIVATE enum for type safety
- Debug logging on dropped events for observability
- Multi-channel subscription enforcement via middleware, events, and cabinet API
- 3-layer cache architecture: Redis -> PostgreSQL -> rate-limited Telegram API
- ChatMemberUpdated event-driven tracking with automatic VPN access control
- Admin management via bot FSM handler and REST API with full CRUD
- Channel ID normalization: @username resolved to numeric ID at creation time
- Fail-closed error handling: API errors deny access (security-first)
- Background reconciliation with keyset pagination (100 per batch)
- Per-user rate limiting on subscription check button (5s cooldown)
- Redis connection pooling via cache singleton (no per-request connections)
- Database: channel_id index, multi-row upsert optimization
- Localization: en, ru, zh, fa, ua translations for all new strings
- Frontend blocking UI with channel list and subscription status
- Admin channel management page with toggle, delete, and create
- Add ContextVarsMiddleware for automatic user_id/chat_id/username binding
via structlog contextvars (aiogram) and http_method/http_path (FastAPI)
- Use bound_contextvars() context manager instead of clear_contextvars()
to safely restore previous state instead of wiping all context
- Register ContextVarsMiddleware as outermost middleware (before GlobalError)
so all error logs include user context
- Replace structlog.get_logger() with structlog.get_logger(__name__) across
270 calls in 265 files for meaningful logger names
- Switch wrapper_class from BoundLogger to make_filtering_bound_logger()
for pre-processor level filtering (performance optimization)
- Migrate 1411 %-style positional arg logger calls to structlog kwargs
style across 161 files via AST script
- Migrate log_rotation_service.py from stdlib logging to structlog
- Add payment module prefixes to TelegramNotifierProcessor.IGNORED_LOGGER_PREFIXES
and ExcludePaymentFilter.PAYMENT_MODULES to prevent payment data leaking
to Telegram notifications and general log files
- Fix LoggingMiddleware: add from_user null-safety for channel posts,
switch time.time() to time.monotonic() for duration measurement
- Remove duplicate logger assignments in purchase.py, config.py,
inline.py, and admin/payments.py
Add cabinet admin API for configuring button colors (primary/success/danger)
and custom emoji IDs per menu section (home, subscription, balance, referral,
support, info, admin). Styles are stored as JSON in system_settings and cached
in-process for fast resolution.
Style resolution chain: explicit param > per-section DB > global config > defaults.
- Rename mode from 'text' to 'cabinet' (text/text_only/minimal kept as aliases)
- Add build_cabinet_url() for joining MINIAPP_CUSTOM_URL with section paths
- Cabinet main menu now has section-specific buttons: subscription, balance,
referral, support, info — each opens the corresponding cabinet page
- Add CALLBACK_TO_CABINET_PATH mapping for automatic deep-linking from
callback_data to cabinet routes (/subscription, /balance, /referral, etc.)
- Unmapped callback_data gracefully falls back to regular Telegram callbacks
- Add startup validation warning when cabinet mode is active without MINIAPP_CUSTOM_URL
- Update admin broadcast buttons with section-specific routing
- Backward compatible: is_text_main_menu_mode() kept as alias for is_cabinet_mode()
Add BlacklistMiddleware for aiogram that blocks all message/callback/pre_checkout
from blacklisted users globally. Add blacklist check to cabinet API dependency.
Fix case-insensitive username matching. Remove 10 redundant manual checks from handlers.
- Add pyproject.toml with uv and ruff configuration
- Pin Python version to 3.13 via .python-version
- Add Makefile commands: lint, format, fix
- Apply ruff formatting to entire codebase
- Remove unused imports (base64 in yookassa/simple_subscription)
- Update .gitignore for new config files