diff --git a/.env.example b/.env.example index f2647f8b..4ccc38b0 100644 --- a/.env.example +++ b/.env.example @@ -91,6 +91,8 @@ DEFAULT_TRAFFIC_LIMIT_GB=100 # ===== ГЛОБАЛЬНЫЙ ПАРАМЕТР ДЛЯ ВСЕХ ПОДПИСОК ===== DEFAULT_TRAFFIC_RESET_STRATEGY=MONTH +# Сбрасывать трафик при каждой оплате подписки +RESET_TRAFFIC_ON_PAYMENT=false # ===== НАСТРОЙКИ ТРАФИКА ===== # Режим выбора трафика: diff --git a/README.md b/README.md index 6f4f973e..2a23b022 100644 --- a/README.md +++ b/README.md @@ -335,6 +335,8 @@ DEFAULT_TRAFFIC_LIMIT_GB=100 # ===== ГЛОБАЛЬНЫЙ ПАРАМЕТР ДЛЯ ВСЕХ ПОДПИСОК ===== DEFAULT_TRAFFIC_RESET_STRATEGY=MONTH +# Сбрасывать трафик при каждой оплате подписки +RESET_TRAFFIC_ON_PAYMENT=false # ===== НАСТРОЙКИ ТРАФИКА ===== # Режим выбора трафика: diff --git a/app/config.py b/app/config.py index 878dcc0f..b098201b 100644 --- a/app/config.py +++ b/app/config.py @@ -71,6 +71,7 @@ class Settings(BaseSettings): DEFAULT_DEVICE_LIMIT: int = 1 TRIAL_SQUAD_UUID: str = "" DEFAULT_TRAFFIC_RESET_STRATEGY: str = "MONTH" + RESET_TRAFFIC_ON_PAYMENT: bool = False MAX_DEVICES_LIMIT: int = 20 TRIAL_WARNING_HOURS: int = 2 diff --git a/app/database/crud/subscription.py b/app/database/crud/subscription.py index 91b79375..de20b006 100644 --- a/app/database/crud/subscription.py +++ b/app/database/crud/subscription.py @@ -120,9 +120,13 @@ async def extend_subscription( if subscription.status == SubscriptionStatus.EXPIRED.value: subscription.status = SubscriptionStatus.ACTIVE.value logger.info(f"🔄 Статус изменён с EXPIRED на ACTIVE") - + + if settings.RESET_TRAFFIC_ON_PAYMENT: + subscription.traffic_used_gb = 0.0 + logger.info("🔄 Сбрасываем использованный трафик согласно настройке RESET_TRAFFIC_ON_PAYMENT") + subscription.updated_at = current_time - + await db.commit() await db.refresh(subscription) await clear_notifications(db, subscription.id) diff --git a/app/handlers/subscription.py b/app/handlers/subscription.py index 649e8415..1eb66101 100644 --- a/app/handlers/subscription.py +++ b/app/handlers/subscription.py @@ -2625,7 +2625,12 @@ async def confirm_extend_subscription( await add_subscription_servers(db, subscription, server_ids, server_prices_for_period) try: - remnawave_result = await subscription_service.update_remnawave_user(db, subscription) + remnawave_result = await subscription_service.update_remnawave_user( + db, + subscription, + reset_traffic=settings.RESET_TRAFFIC_ON_PAYMENT, + reset_reason="продление подписки", + ) if remnawave_result: logger.info("✅ RemnaWave обновлен успешно") else: @@ -3516,13 +3521,28 @@ async def confirm_purchase( subscription_service = SubscriptionService() if db_user.remnawave_uuid: - remnawave_user = await subscription_service.update_remnawave_user(db, subscription) + remnawave_user = await subscription_service.update_remnawave_user( + db, + subscription, + reset_traffic=settings.RESET_TRAFFIC_ON_PAYMENT, + reset_reason="покупка подписки", + ) else: - remnawave_user = await subscription_service.create_remnawave_user(db, subscription) + remnawave_user = await subscription_service.create_remnawave_user( + db, + subscription, + reset_traffic=settings.RESET_TRAFFIC_ON_PAYMENT, + reset_reason="покупка подписки", + ) if not remnawave_user: logger.error(f"Не удалось создать/обновить RemnaWave пользователя для {db_user.telegram_id}") - remnawave_user = await subscription_service.create_remnawave_user(db, subscription) + remnawave_user = await subscription_service.create_remnawave_user( + db, + subscription, + reset_traffic=settings.RESET_TRAFFIC_ON_PAYMENT, + reset_reason="покупка подписки (повторная попытка)", + ) transaction = await create_transaction( db=db, diff --git a/app/services/monitoring_service.py b/app/services/monitoring_service.py index d5cc02bb..a87638c3 100644 --- a/app/services/monitoring_service.py +++ b/app/services/monitoring_service.py @@ -795,7 +795,12 @@ class MonitoringService: if success: await extend_subscription(db, subscription, 30) - await self.subscription_service.update_remnawave_user(db, subscription) + await self.subscription_service.update_remnawave_user( + db, + subscription, + reset_traffic=settings.RESET_TRAFFIC_ON_PAYMENT, + reset_reason="автопродление подписки", + ) if self.bot: await self._send_autopay_success_notification(user, renewal_cost, 30) diff --git a/app/services/subscription_service.py b/app/services/subscription_service.py index 54ba6720..e398b286 100644 --- a/app/services/subscription_service.py +++ b/app/services/subscription_service.py @@ -87,9 +87,12 @@ class SubscriptionService: ) async def create_remnawave_user( - self, - db: AsyncSession, - subscription: Subscription + self, + db: AsyncSession, + subscription: Subscription, + *, + reset_traffic: bool = False, + reset_reason: Optional[str] = None, ) -> Optional[RemnaWaveUser]: try: @@ -130,6 +133,14 @@ class SubscriptionService: active_internal_squads=subscription.connected_squads ) + if reset_traffic: + await self._reset_user_traffic( + api, + updated_user.uuid, + user.telegram_id, + reset_reason, + ) + else: logger.info(f"🆕 Создаем нового пользователя в панели для {user.telegram_id}") username = f"user_{user.telegram_id}" @@ -148,7 +159,15 @@ class SubscriptionService: ), active_internal_squads=subscription.connected_squads ) - + + if reset_traffic: + await self._reset_user_traffic( + api, + updated_user.uuid, + user.telegram_id, + reset_reason, + ) + subscription.remnawave_short_uuid = updated_user.short_uuid subscription.subscription_url = updated_user.subscription_url subscription.subscription_crypto_link = updated_user.happ_crypto_link @@ -172,7 +191,10 @@ class SubscriptionService: async def update_remnawave_user( self, db: AsyncSession, - subscription: Subscription + subscription: Subscription, + *, + reset_traffic: bool = False, + reset_reason: Optional[str] = None, ) -> Optional[RemnaWaveUser]: try: @@ -210,6 +232,14 @@ class SubscriptionService: active_internal_squads=subscription.connected_squads ) + if reset_traffic: + await self._reset_user_traffic( + api, + user.remnawave_uuid, + user.telegram_id, + reset_reason, + ) + subscription.subscription_url = updated_user.subscription_url subscription.subscription_crypto_link = updated_user.happ_crypto_link await db.commit() @@ -219,16 +249,37 @@ class SubscriptionService: strategy_name = settings.DEFAULT_TRAFFIC_RESET_STRATEGY logger.info(f"📊 Стратегия сброса трафика: {strategy_name}") return updated_user - + except RemnaWaveAPIError as e: logger.error(f"Ошибка RemnaWave API: {e}") return None except Exception as e: logger.error(f"Ошибка обновления RemnaWave пользователя: {e}") return None - + + async def _reset_user_traffic( + self, + api: RemnaWaveAPI, + user_uuid: str, + telegram_id: int, + reset_reason: Optional[str] = None, + ) -> None: + if not user_uuid: + return + + try: + await api.reset_user_traffic(user_uuid) + reason_text = f" ({reset_reason})" if reset_reason else "" + logger.info( + f"🔄 Сброшен трафик RemnaWave для пользователя {telegram_id}{reason_text}" + ) + except Exception as exc: + logger.warning( + f"⚠️ Не удалось сбросить трафик RemnaWave для пользователя {telegram_id}: {exc}" + ) + async def disable_remnawave_user(self, user_uuid: str) -> bool: - + try: async with self.api as api: await api.disable_user(user_uuid) diff --git a/app/services/system_settings_service.py b/app/services/system_settings_service.py index cde93e4c..9f2bec98 100644 --- a/app/services/system_settings_service.py +++ b/app/services/system_settings_service.py @@ -113,6 +113,7 @@ class BotConfigurationService: "MAX_DEVICES_LIMIT": "PAID_SUBSCRIPTION", "PRICE_PER_DEVICE": "PAID_SUBSCRIPTION", "DEFAULT_TRAFFIC_RESET_STRATEGY": "SUBSCRIPTIONS_GLOBAL", + "RESET_TRAFFIC_ON_PAYMENT": "SUBSCRIPTIONS_GLOBAL", "TRAFFIC_SELECTION_MODE": "TRAFFIC", "FIXED_TRAFFIC_LIMIT_GB": "TRAFFIC", "AVAILABLE_SUBSCRIPTION_PERIODS": "PERIODS",