From be28e9bec40a67d006eb9519f3baaf6e1dcaa21a Mon Sep 17 00:00:00 2001 From: Egor Date: Mon, 6 Oct 2025 01:15:42 +0300 Subject: [PATCH] Escape server names in promo hint --- app/localization/locales/en.json | 2 +- app/localization/locales/ru.json | 2 +- app/utils/promo_offer.py | 43 ++++++++++++++++++++++++++------ locales/en.json | 2 +- locales/ru.json | 2 +- 5 files changed, 40 insertions(+), 11 deletions(-) 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": "🔧 Управление устройствами",