diff --git a/bot/handlers/admin/user_management.py b/bot/handlers/admin/user_management.py index 4ecf6e1..ebde73b 100644 --- a/bot/handlers/admin/user_management.py +++ b/bot/handlers/admin/user_management.py @@ -33,6 +33,26 @@ router = Router(name="admin_user_management_router") USERNAME_REGEX = re.compile(r"^[a-zA-Z0-9_]{5,32}$") +def _format_traffic_period(strategy: Optional[str], get_text: Callable[..., str]) -> Optional[str]: + if not strategy: + return None + strategy_upper = str(strategy).upper() + key_map = { + "MONTH": "traffic_period_month", + "WEEK": "traffic_period_week", + "DAY": "traffic_period_day", + "NO_RESET": "traffic_period_no_reset", + } + label_key = key_map.get(strategy_upper) + return get_text(label_key) if label_key else strategy_upper + + +def _format_used_with_period(get_text: Callable[..., str], used_display: str, period_label: Optional[str]) -> str: + if not period_label: + return used_display + return get_text("traffic_used_with_period", traffic_used=used_display, traffic_period=period_label) + + async def users_list_handler(callback: types.CallbackQuery, i18n_data: dict, settings: Settings, session: AsyncSession, page: int = 0): @@ -263,10 +283,22 @@ async def format_user_card(user: User, session: AsyncSession, traffic_limit = subscription_details.get('traffic_limit_bytes') traffic_used = subscription_details.get('traffic_used_bytes') - if traffic_limit and traffic_used is not None: - traffic_limit_gb = traffic_limit / (1024**3) - traffic_used_gb = traffic_used / (1024**3) - card_parts.append(f"{_('admin_user_traffic_label')} {hcode(f'{traffic_used_gb:.2f}GB / {traffic_limit_gb:.2f}GB')}") + traffic_strategy = subscription_details.get('traffic_limit_strategy') + period_label = _format_traffic_period(traffic_strategy, _) + if traffic_used is not None or traffic_limit is not None: + used_display = _("traffic_na") + if traffic_used is not None: + traffic_used_gb = traffic_used / (1024**3) + used_display = f"{traffic_used_gb:.2f}GB" + used_display = _format_used_with_period(_, used_display, period_label) + + if traffic_limit: + traffic_limit_gb = traffic_limit / (1024**3) + limit_display = f"{traffic_limit_gb:.2f}GB" + else: + limit_display = _("traffic_unlimited") + + card_parts.append(f"{_('admin_user_traffic_label')} {hcode(f'{used_display} / {limit_display}')}") else: card_parts.append(f"{_('admin_user_subscription_label')} {hcode(_('admin_user_subscription_none'))}") except Exception as e: diff --git a/bot/handlers/user/subscription/core.py b/bot/handlers/user/subscription/core.py index 60555ee..f887f15 100644 --- a/bot/handlers/user/subscription/core.py +++ b/bot/handlers/user/subscription/core.py @@ -172,10 +172,29 @@ async def my_subscription_command_handler( except Exception: pass return str(val) + def _format_traffic_period(strategy: Optional[str]) -> Optional[str]: + if not strategy: + return None + strategy_upper = str(strategy).upper() + key_map = { + "MONTH": "traffic_period_month", + "WEEK": "traffic_period_week", + "DAY": "traffic_period_day", + "NO_RESET": "traffic_period_no_reset", + } + label_key = key_map.get(strategy_upper) + return get_text(label_key) if label_key else strategy_upper + + def _format_used_with_period(used_display: str, period_label: Optional[str]) -> str: + if not period_label: + return used_display + return get_text("traffic_used_with_period", traffic_used=used_display, traffic_period=period_label) + + period_label = _format_traffic_period(active.get("traffic_limit_strategy")) if traffic_mode: limit_display = _fmt_gb(active.get("traffic_limit_bytes")) - used_display = _fmt_gb(active.get("traffic_used_bytes")) + used_display = _format_used_with_period(_fmt_gb(active.get("traffic_used_bytes")), period_label) remaining_display = get_text("traffic_na") try: limit_val = active.get("traffic_limit_bytes") or 0 @@ -202,7 +221,10 @@ async def my_subscription_command_handler( config_link=config_link_value, traffic_limit=(f"{active['traffic_limit_bytes'] / 2**30:.2f} GB" if active.get("traffic_limit_bytes") else get_text("traffic_unlimited")), traffic_used=( - f"{active['traffic_used_bytes'] / 2**30:.2f} GB" if active.get("traffic_used_bytes") is not None else get_text("traffic_na") + _format_used_with_period( + f"{active['traffic_used_bytes'] / 2**30:.2f} GB" if active.get("traffic_used_bytes") is not None else get_text("traffic_na"), + period_label, + ) ), ) diff --git a/bot/services/subscription_service.py b/bot/services/subscription_service.py index 6069228..7fc97ae 100644 --- a/bot/services/subscription_service.py +++ b/bot/services/subscription_service.py @@ -55,6 +55,19 @@ class SubscriptionService: except Exception: return False + def _extract_panel_traffic_details( + self, panel_user_data: Dict[str, Any] + ) -> Tuple[Optional[int], Optional[int], Optional[str]]: + traffic_stats = panel_user_data.get("userTraffic") or {} + used = traffic_stats.get("usedTrafficBytes") + if used is None: + used = panel_user_data.get("usedTrafficBytes") + limit = panel_user_data.get("trafficLimitBytes") + strategy = panel_user_data.get("trafficLimitStrategy") + if strategy is None: + strategy = traffic_stats.get("trafficLimitStrategy") + return used, limit, strategy + async def _notify_admin_panel_user_creation_failed(self, user_id: int): if not self.bot or not self.i18n or not self.settings.ADMIN_IDS: return @@ -444,9 +457,7 @@ class SubscriptionService: return None panel_user_data = await self.panel_service.get_user_by_uuid(panel_user_uuid) or {} - traffic_info = panel_user_data.get("userTraffic") or {} - current_limit = panel_user_data.get("trafficLimitBytes") - current_used = traffic_info.get("usedTrafficBytes") + current_used, current_limit, _ = self._extract_panel_traffic_details(panel_user_data) active_sub = await subscription_dal.get_active_subscription_by_user_id( session, user_id, panel_user_uuid @@ -846,9 +857,7 @@ class SubscriptionService: update_payload_local = {} panel_status = panel_user_data.get("status", "UNKNOWN").upper() panel_expire_at_str = panel_user_data.get("expireAt") - traffic_stats = panel_user_data.get("userTraffic") or {} - panel_traffic_used = traffic_stats.get("usedTrafficBytes") - panel_traffic_limit = panel_user_data.get("trafficLimitBytes") + panel_traffic_used, panel_traffic_limit, _ = self._extract_panel_traffic_details(panel_user_data) panel_sub_uuid_from_panel = panel_user_data.get( "subscriptionUuid" ) or panel_user_data.get("shortUuid") @@ -901,6 +910,7 @@ class SubscriptionService: if panel_user_data.get("expireAt") else None ) + panel_traffic_used, panel_traffic_limit, panel_traffic_strategy = self._extract_panel_traffic_details(panel_user_data) config_link_raw = panel_user_data.get("subscriptionUrl") display_link, connect_button_url = await prepare_config_links(self.settings, config_link_raw) hwid_limit = panel_user_data.get("hwidDeviceLimit") @@ -913,8 +923,9 @@ class SubscriptionService: "status_from_panel": panel_user_data.get("status", "UNKNOWN").upper(), "config_link": display_link, "connect_button_url": connect_button_url, - "traffic_limit_bytes": panel_user_data.get("trafficLimitBytes"), - "traffic_used_bytes": (panel_user_data.get("userTraffic") or {}).get("usedTrafficBytes"), + "traffic_limit_bytes": panel_traffic_limit, + "traffic_used_bytes": panel_traffic_used, + "traffic_limit_strategy": panel_traffic_strategy, "user_bot_username": db_user.username, "is_panel_data": True, "max_devices": hwid_limit, diff --git a/locales/en.json b/locales/en.json index 60d4622..10c450b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -76,6 +76,11 @@ "payment_failed": "❌ Payment failed or was cancelled. Please try again or contact support.", "config_link_not_available": "not available, contact support", "traffic_unlimited": "Unlimited", + "traffic_period_day": "per day", + "traffic_period_week": "per week", + "traffic_period_month": "per month", + "traffic_period_no_reset": "no reset", + "traffic_used_with_period": "{traffic_used} ({traffic_period})", "promo_code_prompt": "Please enter your promo code:", "promo_code_not_found": "Promo code {code} not found, expired, or already used the maximum number of times.", "promo_code_already_used_by_user": "You have already used promo code {code}.", diff --git a/locales/ru.json b/locales/ru.json index 318875f..1ca545f 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -76,6 +76,11 @@ "payment_failed": "❌ Оплата не удалась или была отменена. Пожалуйста, попробуйте еще раз или свяжитесь с поддержкой.", "config_link_not_available": "недоступна, обратитесь в поддержку", "traffic_unlimited": "Безлимитный", + "traffic_period_day": "за день", + "traffic_period_week": "за неделю", + "traffic_period_month": "за месяц", + "traffic_period_no_reset": "без сброса", + "traffic_used_with_period": "{traffic_used} ({traffic_period})", "promo_code_prompt": "Пожалуйста, введите ваш промокод:", "promo_code_not_found": "Промокод {code} не найден, истек или уже использован максимальное количество раз.", "promo_code_already_used_by_user": "Вы уже активировали промокод {code}.",