- 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
- Keyboard now shows "Возобновить" for disabled/expired daily tariffs
instead of useless "Приостановить"
- resume_daily_subscription handles EXPIRED→ACTIVE (not only DISABLED)
- Pause handler detects inactive status and calls resume directly
- subscription_extend redirects daily tariffs to subscription info
(daily tariffs have no period_prices, so extend page was empty)
- Add enabled flag to hide/show each button section in main menu
- Add per-locale custom labels (ru, en, ua, zh, fa) for button text
- Deep-copy nested labels dict in cache to prevent reference leaks
- Validate label entries from DB (type + locale key checks)
- Use selective merge in PATCH handler instead of blind .update()
Allow admins to set buttons to Telegram's default style with no color
override. Refactors style resolution from or-chain to explicit if/elif/else
so that 'default' does not fall through to global config or hardcoded defaults.
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()
Remove all modem purchase/management code:
- Delete modem handler, service, and tests
- Remove modem button from keyboards and admin panel
- Remove modem pricing from calculations
- Remove modem REST API endpoint and schemas
- Remove modem decorator, config settings, and notification formatting
- Keep DB column and migration for backwards compatibility
Transaction created_at and completed_at showed identical timestamps
because webhook handlers created transactions with is_completed=True
in a single step. Now all 10 payment providers pass payment.created_at
to the transaction so created_at reflects when the user initiated
the payment, not when the webhook processed it.
Also: remove duplicate datetime import in inline.py, upgrade button
stats DB error logging from debug to warning, add index on
button_click_logs.button_type for analytics queries.
- 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
Новая функциональность вывода средств:
- config.py: добавлены настройки вывода (минимальная сумма, кулдаун, анализ подозрительности, тестовый режим)
- models.py: добавлена модель WithdrawalRequest с полями для заявок, анализа рисков и обработки админ
1. app/keyboards/inline.py
- Добавлен параметр callback_data: str = "back_to_menu" в get_back_keyboard()
- Позволяет использовать кнопку "Назад" с разными callback'ами
2. app/services/admin_notification_service.py
- Добавлен тип "modem" в update_types с заголовком "📡 ИЗМЕНЕНИЕ МОД
Добавлена функция умной автоактивации подписки после пополнения баланса:
- Новая настройка AUTO_ACTIVATE_AFTER_TOPUP_ENABLED в .env
- Функция auto_activate_subscription_after_topup() в subscription_auto_purchase_service.py:
- Автоматически продлевает истёкшую подписку с теми же параметрами
- Создаёт новую подписку с дефолтными параметрами если подписки нет
- Проверяет достаточность баланса перед активацией
- Интеграция с RemnaWave API
- Уведомления пользователю и админам
- Интеграция во все 9 платёжных провайдеров:
- Stars, CryptoBot, YooKassa, CloudPayments
- WATA, Platega, Pal24, MulenPay, Tribute
- Исправлен handle_activate_button в menu.py:
- Полная переработка с интеграцией RemnaWave
- Корректная работа с балансом и транзакциями
- Использование SubscriptionRenewalService
Добавлено округление цен при отображении:
- Новая настройка PRICE_ROUNDING_ENABLED в .env
- Логика: ≤50 коп → вниз, >50 коп → вверх
- Применяется везде: пополнения, партнёрки, скидки, промогруппы
- Кнопки устройств теперь в один столбец (вместо 2 колонок)
- Автоматический предвыбор бесплатных серверов (price_kopeks == 0)
- Вывод описания сквадов в тексте сообщения над кнопками
Изменённые файлы:
- keyboards/inline.py: get_devices_keyboard в 1 столбец
- handlers/subscription/countries.py: хелперы _get_preselected_free_countries и _build_countries_selection_text
- handlers/subscription/purchase.py, traffic.py, autopay.py: применение новой логики
- Добавлен ENV переключатель TRAFFIC_TOPUP_ENABLED для вкл/выкл докупки
- Добавлена отдельная конфигурация пакетов TRAFFIC_TOPUP_PACKAGES_CONFIG
- Добавлено поле purchased_traffic_gb для отслеживания докупленного трафика
- Добавлены режимы расчета цены сброса (period/traffic/traffic_with_purchased)
- Исправлен абьюз: цена сброса теперь учитывает докупленный трафик
- Сброс purchased_traffic_gb при продлении/покупке подписки
- UX: меню сброса теперь показывает цену и баланс вместо alert
- UX: кнопка пополнения если не хватает средств на сброс
- Добавлена миграция для нового поля purchased_traffic_gb
- Добавлена локализация TRAFFIC_TOPUP_DISABLED (ru/en/ua/zh)
Изменения:
- Добавлены настройки модема в .env.example и config.py (MODEM_ENABLED, MODEM_PRICE_PER_MONTH, MODEM_PERIOD_DISCOUNTS)
- Добавлено поле modem_enabled в модель Subscription
- Реализован модуль handlers/subscription/modem.py с обработчиками подключения/отключения модема
- Добавлено управ