- Added checks for safe filename to prevent directory traversal attacks.
- Updated file type validation to use the sanitized filename.
- Implemented path resolution to ensure uploaded files are within the backup directory.
- Updated .env.example to include BACKUP_ARCHIVE_PASSWORD variable.
- Added pyzipper to requirements.txt for ZIP file encryption.
- Modified Settings class in config.py to handle BACKUP_ARCHIVE_PASSWORD.
- Enhanced BackupService to create and send password-protected ZIP archives if a password is provided.
Добавлена функция умной автоактивации подписки после пополнения баланса:
- Новая настройка 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: применение новой логики
- Рекламные кампании теперь выдают триальную подписку (is_trial=True),
а не платную — пользователь становится платным только после оплаты
- Добавлена настройка CHANNEL_REQUIRED_FOR_ALL для проверки подписки
на канал для ВСЕХ пользователей (платных и триальных)
- Добавлен параметр is_trial в create_paid_subscription для гибкости
При наличии докупленного трафика (например 250 + 10 ГБ = 260 ГБ)
система округляла текущий пакет до ближайшего (500 ГБ) и позволяла
бесплатно переключиться на него.
Исправления:
- confirm_switch_traffic: используется базовый трафик для расчёта цены
- get_traffic_switch_keyboard: добавлен параметр base_traffic_gb
- handle_switch_traffic: показывает информацию о докупленном трафике
- execute_switch_traffic: сбрасывает purchased_traffic_gb при переключении
Добавлена возможность просмотра топа рефереров за неделю/месяц
с сортировкой по количеству приглашённых или по заработку:
- get_top_referrers_by_period() в crud/referral.py
- Интерактивные кнопки выбора периода и критерия сортировки
- Топ-20 рефереров с медалями для первых трёх мест
При уменьшении лимита устройств подключённые устройства не удалялись,
позволяя пользователю продолжать использовать их бесплатно.
Исправления:
- execute_change_devices: сброс всех устройств через API если
подключённых больше чем новый лимит
- confirm_change_devices: предупреждение пользователя о сбросе
устройств перед подтверждением
- Уведомление о количестве сброшенных устройств в результате
Добавлена поддержка указания способа оплаты при пополнении баланса:
- add_user_balance(): новый параметр payment_method для передачи в транзакцию
- add_user_balance_by_id(): поддержка payment_method
- UserService: ручные пополнения админом пом
feat(tickets): добавлены уведомления админам об ответах пользователей на тикеты
Реализована функция notify_admins_about_ticket_reply() для оповещения администраторов:
- Уведомление отправляется после успешного добавления ответа пользователя
- Формат уведомления включает ID тикета, заголовок
Реализована система платного триала с гибким выбором способа оплаты:
- Автоопределение платности: если TRIAL_ACTIVATION_PRICE > 0, триал автоматически платный
- TRIAL_PAYMENT_ENABLED теперь опционален (для обратной совместимости)
- Добавлена функция create
Реализована отказоустойчивая система отправки чеков в налоговую:
- Добавлен NalogoQueueService для фоновой обработки очереди чеков
- При недоступности nalog.ru (503) чеки сохраняются в Redis
- Автоматическая повторная отправка с настраиваемым интервалом
- Защита от DDoS: задержка между чеками (NALOGO_QUEUE_RECEIPT_DELAY)
- Уведомления админам в топик при проблемах и успешной разгрузке
Изменения в файлах:
- app/services/nalogo_queue_service.py: новый фоновый сервис
- app/services/nalogo_service.py: методы очереди, определение 503
- app/utils/cache.py: lpush/rpop/llen/lrange для Redis List
- app/handlers/admin/monitoring.py: статистика чеков в админке
- app/config.py: NALOGO_QUEUE_* и ADMIN_NOTIFICATIONS_NALOG_TOPIC_ID
- main.py: интеграция запуска/остановки сервиса
Новые ENV переменные:
- ADMIN_NOTIFICATIONS_NALOG_TOPIC_ID
- NALOGO_QUEUE_CHECK_INTERVAL (300с)
- NALOGO_QUEUE_RECEIPT_DELAY (3с)
- NALOGO_QUEUE_MAX_ATTEMPTS (10)
Рефакторинг архитектуры управления модемом:
- Создан сервис app/services/modem_service.py:
- ModemService с бизнес-логикой подключения/отключения
- ModemError enum для типизации ошибок
- ModemPriceInfo, ModemOperationResult dataclass'ы
- Константы MODEM_WARNING_DAYS_* для уровней предупреждений
Рефакторинг архитектуры ежедневных конкурсов:
- Создан модуль app/services/contests/ с новой архитектурой:
- enums.py: GameType, RoundStatus, PrizeType enum классы
- games.py: паттерн Стратегия для 7 типов игр
- attempt_service.py: ContestAttemptService для атомарных операций
- Упрощён handlers/contests.py:
- Удалены отдельные _render_* функции (заменены на стратегии)
- Логика обработки попыток вынесена в ContestAttemptService
- Уменьшено с 523 до 342 строк (-35%)
- Обновлён contest_rotation_service.py:
- Заменена if-elif цепочка на get_game_strategy().build_payload()
- Используются enum классы вместо магических строк
- Исправлен handlers/admin/daily_contests.py:
- prize_days → prize_type/prize_value (соответствие модели БД)
- Обновлены EDITABLE_FIELDS и отображение приза
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Создание попытки сразу при показе вопроса (cipher/emoji/anagram)
- Проверка attempt.answer is not None для блокировки повторного ответа
- Обновление существующей попытки вместо создания новой
- Добавлена функция update_attempt() в CRUD
- Добавлен 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 с обработчиками подключения/отключения модема
- Добавлено управ