diff --git a/app/localization/locales/en.json b/app/localization/locales/en.json
index 905fbc8b..88a7af66 100644
--- a/app/localization/locales/en.json
+++ b/app/localization/locales/en.json
@@ -75,7 +75,7 @@
"LOADING": "⏳ Loading...",
"MAIN_MENU": "👤 {user_name}\n\n📱 Subscription: {subscription_status}\n\nChoose an option:\n",
"MAIN_MENU_ACTION_PROMPT": "Choose an option:",
- "MAIN_MENU_TEST_ACCESS_HEADER": "🧪 Test servers active: {count}",
+ "MAIN_MENU_TEST_ACCESS_HEADER": "🧪 Test servers active: {servers}",
"MAIN_MENU_TEST_ACCESS_TIMER": "⏳ Access active for {time_left}\n{bar}",
"MAIN_MENU_BUTTON": "🏠 Main menu",
"MANAGE_DEVICES_BUTTON": "🔧 Manage devices",
diff --git a/app/localization/locales/ru.json b/app/localization/locales/ru.json
index 3071c652..6f0277b1 100644
--- a/app/localization/locales/ru.json
+++ b/app/localization/locales/ru.json
@@ -202,7 +202,7 @@
"MAINTENANCE_MODE_API_ERROR": "\n🔧 Технические работы!\n\nСервис временно недоступен из-за проблем с подключением к серверам.\n\n⏰ Мы работаем над восстановлением. Попробуйте через несколько минут.\n\n🔄 Последняя проверка: {last_check}\n",
"MAIN_MENU": "👤 {user_name}\n \n📱 Подписка: {subscription_status}\n\nВыберите действие:\n",
"MAIN_MENU_ACTION_PROMPT": "Выберите действие:",
- "MAIN_MENU_TEST_ACCESS_HEADER": "🧪 Тестовые сервера активны: {count}",
+ "MAIN_MENU_TEST_ACCESS_HEADER": "🧪 Тестовые сервера активны: {servers}",
"MAIN_MENU_TEST_ACCESS_TIMER": "⏳ Доступ действует ещё: {time_left}\n{bar}",
"MAIN_MENU_BUTTON": "🏠 Главное меню",
"MANAGE_DEVICES_BUTTON": "🔧 Управление устройствами",
diff --git a/app/utils/promo_offer.py b/app/utils/promo_offer.py
index 1d672912..c7f62cb4 100644
--- a/app/utils/promo_offer.py
+++ b/app/utils/promo_offer.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+import html
import math
from datetime import datetime
from typing import Optional, Sequence
@@ -9,7 +10,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.database.crud.discount_offer import get_latest_claimed_offer_for_user
-from app.database.models import SubscriptionTemporaryAccess, User
+from app.database.models import ServerSquad, SubscriptionTemporaryAccess, User
def get_user_active_promo_discount_percent(user: Optional[User]) -> int:
@@ -197,21 +198,49 @@ async def build_test_access_hint(
bar = _build_progress_bar(seconds_left, total_seconds)
time_left_text = _format_time_left(seconds_left, getattr(texts, "language", "ru"))
- unique_squads = {
- entry.squad_uuid for entry in active_entries if getattr(entry, "squad_uuid", None)
- }
- count = len(unique_squads) or len(active_entries)
+ unique_squad_uuids: list[str] = []
+ seen_squads: set[str] = set()
+ for entry in active_entries:
+ squad_uuid = getattr(entry, "squad_uuid", None)
+ if squad_uuid and squad_uuid not in seen_squads:
+ seen_squads.add(squad_uuid)
+ unique_squad_uuids.append(squad_uuid)
+
+ squad_display_names: list[str] = []
+ if unique_squad_uuids:
+ squads_result = await db.execute(
+ select(ServerSquad.squad_uuid, ServerSquad.display_name).where(
+ ServerSquad.squad_uuid.in_(unique_squad_uuids)
+ )
+ )
+ names_map = {
+ squad_uuid: html.escape(display_name)
+ for squad_uuid, display_name in squads_result.all()
+ if display_name
+ }
+ for squad_uuid in unique_squad_uuids:
+ if squad_uuid in names_map:
+ squad_display_names.append(names_map[squad_uuid])
+ else:
+ squad_display_names.append(html.escape(squad_uuid))
+
+ if squad_display_names:
+ servers_display = ", ".join(squad_display_names)
+ elif unique_squad_uuids:
+ servers_display = ", ".join(html.escape(squad_uuid) for squad_uuid in unique_squad_uuids)
+ else:
+ servers_display = str(len(active_entries))
header_template = texts.t(
"MAIN_MENU_TEST_ACCESS_HEADER",
- "🧪 Test servers active: {count}",
+ "🧪 Test servers active: {servers}",
)
timer_template = texts.t(
"MAIN_MENU_TEST_ACCESS_TIMER",
"⏳ Access active for {time_left}\n{bar}",
)
- header = header_template.format(count=count)
+ header = header_template.format(servers=servers_display)
timer_line = timer_template.format(time_left=time_left_text, bar=bar)
return f"{header}\n{timer_line}"
diff --git a/locales/en.json b/locales/en.json
index f4df7a86..a771e73e 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -55,7 +55,7 @@
"LOADING": "⏳ Loading...",
"MAIN_MENU": "👤 {user_name}\n\n📱 Subscription: {subscription_status}\n\nChoose an option:\n",
"MAIN_MENU_ACTION_PROMPT": "Choose an option:",
- "MAIN_MENU_TEST_ACCESS_HEADER": "🧪 Test servers active: {count}",
+ "MAIN_MENU_TEST_ACCESS_HEADER": "🧪 Test servers active: {servers}",
"MAIN_MENU_TEST_ACCESS_TIMER": "⏳ Access active for {time_left}\n{bar}",
"MAIN_MENU_BUTTON": "🏠 Main menu",
"MANAGE_DEVICES_BUTTON": "🔧 Manage devices",
diff --git a/locales/ru.json b/locales/ru.json
index 0c2f6ac9..18f0690c 100644
--- a/locales/ru.json
+++ b/locales/ru.json
@@ -235,7 +235,7 @@
"MAINTENANCE_MODE_API_ERROR": "\n🔧 Технические работы!\n\nСервис временно недоступен из-за проблем с подключением к серверам.\n\n⏰ Мы работаем над восстановлением. Попробуйте через несколько минут.\n\n🔄 Последняя проверка: {last_check}\n",
"MAIN_MENU": "👤 {user_name}\n \n📱 Подписка: {subscription_status}\n\nВыберите действие:\n",
"MAIN_MENU_ACTION_PROMPT": "Выберите действие:",
- "MAIN_MENU_TEST_ACCESS_HEADER": "🧪 Тестовые сервера активны: {count}",
+ "MAIN_MENU_TEST_ACCESS_HEADER": "🧪 Тестовые сервера активны: {servers}",
"MAIN_MENU_TEST_ACCESS_TIMER": "⏳ Доступ действует ещё: {time_left}\n{bar}",
"MAIN_MENU_BUTTON": "🏠 Главное меню",
"MANAGE_DEVICES_BUTTON": "🔧 Управление устройствами",