- Escape app names, device names, and other_app_names in
handle_device_guide, handle_app_selection, handle_specific_app_guide
- Redact internal paths and exception details from cabinet API
error responses in _load_config, _save_config, and Remnawave
fetch endpoints
- Fix NameError: texts used before assignment in handle_single_device_reset
(crash on malformed callback_data)
- HTML-escape subscription_link in all <code> tag interpolations
(3 locations in devices.py)
- Add fallback else branch for subscriptionLink in blocks format
(prevents silent button drop when deep link resolution fails)
- Extract render_guide_blocks() helper to eliminate duplicated
block-rendering logic between handle_device_guide and
handle_specific_app_guide
- Add HTML escaping for admin-controlled config text in guide blocks
- Remove unused get_localized_value import from devices.py
- Add async Remnawave config loader with TTL cache and asyncio.Lock
- Normalize both legacy (steps) and Remnawave (blocks) formats to unified structure
- Build dynamic platform selection keyboard from config instead of hardcoded 6-device layout
- Add colored buttons via Bot API 9.4 (green for connect, blue for download)
- Add admin panel handler for selecting Remnawave subscription page config
- Add cache invalidation from both bot admin and cabinet API
- Fix callback data parsing for app IDs with underscores
- Add Linux platform support across all device mappings
- 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
Remove AUTO_ACTIVATE_AFTER_TOPUP and SHOW_ACTIVATION_PROMPT_AFTER_TOPUP
features from all payment providers, config, system settings, and tests.
Cart auto-purchase (AUTO_PURCHASE_AFTER_TOPUP) is preserved.
Bug fixes:
- fix KeyError 'months' in devices.py for custom locale overrides
- fix IntegrityError on trial subscription retry (update existing PENDING instead of INSERT)
- fix PendingRollbackError cascade by adding db.rollback() before recovery
- fix TelegramForbiddenError not caught in photo_message.py
- fix "query is too old" spam in required_sub_channel_check
- add missing trial locale keys (TRIAL_PAYMENT_DESCRIPTION, TRIAL_REFUND_DESCRIPTION, TRIAL_ACTIVATION_ERROR)
- 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
При уменьшении лимита устройств подключённые устройства не удалялись,
позволяя пользователю продолжать использовать их бесплатно.
Исправления:
- execute_change_devices: сброс всех устройств через API если
подключённых больше чем новый лимит
- confirm_change_devices: предупреждение пользователя о сбросе
устройств перед подтверждением
- Уведомление о количестве сброшенных устройств в результате