From 20855e5bfc8aebd2d6647dc8333256b0499fae47 Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 8 Oct 2025 07:30:56 +0300 Subject: [PATCH] Revert "Add admin controls for traffic package visibility" --- app/handlers/admin/pricing.py | 260 ++++--------------------------- app/localization/locales/en.json | 10 +- app/localization/locales/ru.json | 10 +- locales/en.json | 8 - locales/ru.json | 8 - 5 files changed, 28 insertions(+), 268 deletions(-) diff --git a/app/handlers/admin/pricing.py b/app/handlers/admin/pricing.py index ce1e6674..e505b9fe 100644 --- a/app/handlers/admin/pricing.py +++ b/app/handlers/admin/pricing.py @@ -21,25 +21,6 @@ logger = logging.getLogger(__name__) PriceItem = Tuple[str, str, int] -TRAFFIC_PACKAGE_FIELDS: Tuple[Tuple[int, str], ...] = ( - (5, "PRICE_TRAFFIC_5GB"), - (10, "PRICE_TRAFFIC_10GB"), - (25, "PRICE_TRAFFIC_25GB"), - (50, "PRICE_TRAFFIC_50GB"), - (100, "PRICE_TRAFFIC_100GB"), - (250, "PRICE_TRAFFIC_250GB"), - (500, "PRICE_TRAFFIC_500GB"), - (1000, "PRICE_TRAFFIC_1000GB"), - (0, "PRICE_TRAFFIC_UNLIMITED"), -) - -TRAFFIC_PACKAGE_FIELD_MAP: Dict[int, str] = {gb: field for gb, field in TRAFFIC_PACKAGE_FIELDS} -TRAFFIC_PACKAGE_ORDER: Tuple[int, ...] = tuple(gb for gb, _ in TRAFFIC_PACKAGE_FIELDS) -TRAFFIC_PACKAGE_ORDER_INDEX: Dict[int, int] = { - gb: index for index, gb in enumerate(TRAFFIC_PACKAGE_ORDER) -} - - @dataclass(slots=True) class ChoiceOption: value: Any @@ -197,71 +178,6 @@ SETTING_ENTRY_BY_KEY: Dict[str, SettingEntry] = { } -def _traffic_package_sort_key(package: Dict[str, Any]) -> Tuple[int, int]: - order_index = TRAFFIC_PACKAGE_ORDER_INDEX.get(package["gb"]) - if order_index is not None: - return (0, order_index) - return (1, package["gb"]) - - -def _collect_traffic_packages() -> List[Dict[str, Any]]: - raw_packages = settings.get_traffic_packages() - - packages_map: Dict[int, Dict[str, Any]] = {} - for package in raw_packages: - gb = int(package.get("gb", 0)) - packages_map[gb] = { - "gb": gb, - "price": int(package.get("price") or 0), - "enabled": bool(package.get("enabled", True)), - "field": TRAFFIC_PACKAGE_FIELD_MAP.get(gb), - } - - for gb, field in TRAFFIC_PACKAGE_FIELDS: - if not hasattr(settings, field): - continue - - price = getattr(settings, field) - existing = packages_map.get(gb) - enabled = existing["enabled"] if existing is not None else True - - packages_map[gb] = { - "gb": gb, - "price": int(price), - "enabled": enabled, - "field": field, - } - - packages = list(packages_map.values()) - packages.sort(key=_traffic_package_sort_key) - return packages - - -def _serialize_traffic_packages(packages: Iterable[Dict[str, Any]]) -> str: - parts = [] - for package in packages: - enabled_flag = "true" if package.get("enabled") else "false" - parts.append(f"{int(package['gb'])}:{int(package['price'])}:{enabled_flag}") - return ",".join(parts) - - -async def _save_traffic_packages( - db: AsyncSession, - packages: Iterable[Dict[str, Any]], - *, - skip_if_same: bool = False, -) -> bool: - new_value = _serialize_traffic_packages(packages) - current_value = bot_configuration_service.get_current_value("TRAFFIC_PACKAGES_CONFIG") or "" - - if skip_if_same and current_value == new_value: - return False - - await bot_configuration_service.set_value(db, "TRAFFIC_PACKAGES_CONFIG", new_value) - await db.commit() - return True - - def _language_code(language: str | None) -> str: return (language or "ru").split("-")[0].lower() @@ -320,17 +236,23 @@ def _get_period_items(lang_code: str) -> List[PriceItem]: def _get_traffic_items(lang_code: str) -> List[PriceItem]: - packages = _collect_traffic_packages() + traffic_keys: Tuple[Tuple[int, str], ...] = ( + (5, "PRICE_TRAFFIC_5GB"), + (10, "PRICE_TRAFFIC_10GB"), + (25, "PRICE_TRAFFIC_25GB"), + (50, "PRICE_TRAFFIC_50GB"), + (100, "PRICE_TRAFFIC_100GB"), + (250, "PRICE_TRAFFIC_250GB"), + (500, "PRICE_TRAFFIC_500GB"), + (1000, "PRICE_TRAFFIC_1000GB"), + (0, "PRICE_TRAFFIC_UNLIMITED"), + ) items: List[PriceItem] = [] - for package in packages: - field = package.get("field") - if not field: - continue - - label = _format_traffic_label(package["gb"], lang_code) - icon = "✅" if package["enabled"] else "⚪️" - items.append((field, f"{icon} {label}", int(package["price"]))) + for gb, key in traffic_keys: + if hasattr(settings, key): + price = getattr(settings, key) + items.append((key, _format_traffic_label(gb, lang_code), price)) return items @@ -363,17 +285,17 @@ def _build_period_summary(items: Iterable[PriceItem], lang_code: str, fallback: return ", ".join(parts) if parts else fallback -def _build_traffic_summary(lang_code: str, fallback: str) -> str: - packages = _collect_traffic_packages() - enabled_packages = [package for package in packages if package["enabled"]] - - if not enabled_packages: - return fallback - +def _build_traffic_summary(items: Iterable[PriceItem], lang_code: str, fallback: str) -> str: parts: List[str] = [] - for package in enabled_packages: - short_label = _format_traffic_label(package["gb"], lang_code, short=True) - parts.append(f"{short_label}: {settings.format_price(int(package['price']))}") + for key, label, price in items: + if key.endswith("UNLIMITED"): + short_label = "∞" + else: + digits = ''.join(ch for ch in key if ch.isdigit()) + unit = "ГБ" if lang_code == "ru" else "GB" + short_label = f"{digits}{unit}" if digits else label + + parts.append(f"{short_label}: {settings.format_price(price)}") return ", ".join(parts) if parts else fallback @@ -485,68 +407,6 @@ def _build_settings_section( return "\n".join(lines).strip(), keyboard -def _build_traffic_options_section(language: str) -> Tuple[str, types.InlineKeyboardMarkup]: - texts = get_texts(language) - lang_code = _language_code(language) - packages = _collect_traffic_packages() - - title = texts.t( - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_TITLE", - "🚦 Отображение пакетов трафика", - ) - - lines: List[str] = [title, ""] - - enabled_labels = [ - _format_traffic_label(package["gb"], lang_code, short=True) - for package in packages - if package["enabled"] - ] - - if enabled_labels: - lines.append( - texts.t( - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_ACTIVE", - "Активные пакеты: {items}", - ).format(items=", ".join(enabled_labels)) - ) - else: - lines.append( - texts.t( - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_NONE", - "Активных пакетов нет.", - ) - ) - - lines.append("") - lines.append( - texts.t( - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_PROMPT", - "Нажмите на пакет, чтобы включить или выключить его отображение.", - ) - ) - - keyboard_rows: List[List[types.InlineKeyboardButton]] = [] - buttons: List[types.InlineKeyboardButton] = [] - - for package in packages: - icon = "✅" if package["enabled"] else "⚪️" - label = _format_traffic_label(package["gb"], lang_code, short=True) - buttons.append( - types.InlineKeyboardButton( - text=f"{icon} {label}", - callback_data=f"admin_pricing_toggle_traffic:{package['gb']}", - ) - ) - - for i in range(0, len(buttons), 3): - keyboard_rows.append(buttons[i : i + 3]) - - keyboard_rows.append([types.InlineKeyboardButton(text=texts.BACK, callback_data="admin_pricing")]) - keyboard = types.InlineKeyboardMarkup(inline_keyboard=keyboard_rows) - return "\n".join(lines), keyboard - - def _build_period_options_section(language: str) -> Tuple[str, types.InlineKeyboardMarkup]: texts = get_texts(language) lang_code = _language_code(language) @@ -625,7 +485,7 @@ def _build_overview(language: str) -> Tuple[str, types.InlineKeyboardMarkup]: fallback = texts.t("ADMIN_PRICING_SUMMARY_EMPTY", "—") summary_periods = _build_period_summary(period_items, lang_code, fallback) - summary_traffic = _build_traffic_summary(lang_code, fallback) + summary_traffic = _build_traffic_summary(traffic_items, lang_code, fallback) summary_extra = _build_extra_summary(extra_items, fallback) summary_trial = _format_trial_summary(lang_code) summary_core = _format_core_summary(lang_code) @@ -676,13 +536,6 @@ def _build_overview(language: str) -> Tuple[str, types.InlineKeyboardMarkup]: text=texts.t("ADMIN_PRICING_BUTTON_TRAFFIC", "📦 Пакеты трафика"), callback_data="admin_pricing_section:traffic", ), - types.InlineKeyboardButton( - text=texts.t( - "ADMIN_PRICING_BUTTON_TRAFFIC_OPTIONS", - "🚦 Отображение пакетов", - ), - callback_data="admin_pricing_section:traffic_options", - ), types.InlineKeyboardButton( text=texts.t("ADMIN_PRICING_BUTTON_EXTRA", "➕ Дополнительно"), callback_data="admin_pricing_section:extra", @@ -711,8 +564,6 @@ def _build_section( elif section == "extra": items = _get_extra_items(lang_code) title = texts.t("ADMIN_PRICING_SECTION_EXTRA_TITLE", "➕ Дополнительные опции") - elif section == "traffic_options": - return _build_traffic_options_section(language) elif section in SETTING_ENTRIES_BY_SECTION: return _build_settings_section(section, language) elif section == "period_options": @@ -1071,10 +922,6 @@ async def process_pricing_input( await bot_configuration_service.set_value(db, key, new_value) await db.commit() - if key.startswith("PRICE_TRAFFIC_"): - packages = _collect_traffic_packages() - await _save_traffic_packages(db, packages, skip_if_same=True) - if mode == "price": label = _resolve_label(section, key, db_user.language) await message.answer( @@ -1196,57 +1043,6 @@ async def select_setting_choice( await _render_message(callback.message, text, keyboard) -@admin_required -@error_handler -async def toggle_traffic_package( - callback: types.CallbackQuery, - db_user: User, - db: AsyncSession, - state: FSMContext, -) -> None: - try: - _, gb_raw = callback.data.split(":", 1) - gb_value = int(gb_raw) - except (ValueError, TypeError): - await callback.answer() - return - - texts = get_texts(db_user.language) - packages = _collect_traffic_packages() - - target_index = next((index for index, pkg in enumerate(packages) if pkg["gb"] == gb_value), None) - if target_index is None: - await callback.answer() - return - - enabled_count = sum(1 for pkg in packages if pkg["enabled"]) - target_package = packages[target_index] - - if target_package["enabled"] and enabled_count <= 1: - await callback.answer( - texts.t( - "ADMIN_PRICING_TRAFFIC_PACKAGE_MIN", - "Должен оставаться хотя бы один пакет.", - ), - show_alert=True, - ) - return - - target_package["enabled"] = not target_package["enabled"] - - await _save_traffic_packages(db, packages) - - status_text = ( - texts.t("ADMIN_PRICING_TRAFFIC_PACKAGE_ENABLED", "Пакет включен.") - if target_package["enabled"] - else texts.t("ADMIN_PRICING_TRAFFIC_PACKAGE_DISABLED", "Пакет отключен.") - ) - await callback.answer(status_text) - - text, keyboard = _build_traffic_options_section(db_user.language) - await _render_message(callback.message, text, keyboard) - - @admin_required @error_handler async def toggle_period_option( @@ -1331,10 +1127,6 @@ def register_handlers(dp: Dispatcher) -> None: select_setting_choice, F.data.startswith("admin_pricing_choice:"), ) - dp.callback_query.register( - toggle_traffic_package, - F.data.startswith("admin_pricing_toggle_traffic:"), - ) dp.callback_query.register( toggle_period_option, F.data.startswith("admin_pricing_toggle_period:"), diff --git a/app/localization/locales/en.json b/app/localization/locales/en.json index c3aa686a..12d7e637 100644 --- a/app/localization/locales/en.json +++ b/app/localization/locales/en.json @@ -769,13 +769,5 @@ "ADMIN_PUBLIC_OFFER_ENABLED": "✅ Offer enabled", "ADMIN_PUBLIC_OFFER_DISABLED": "🚫 Offer disabled", "ADMIN_PUBLIC_OFFER_RETURN_TO_EDIT": "⬅️ Back to editing", - "ADMIN_PUBLIC_OFFER_EDIT_CANCELLED": "Offer editing cancelled.", - "ADMIN_PRICING_BUTTON_TRAFFIC_OPTIONS": "🚦 Package visibility", - "ADMIN_PRICING_TRAFFIC_PACKAGE_ENABLED": "Package enabled.", - "ADMIN_PRICING_TRAFFIC_PACKAGE_DISABLED": "Package disabled.", - "ADMIN_PRICING_TRAFFIC_PACKAGE_MIN": "At least one package must remain.", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_TITLE": "🚦 Traffic package visibility", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_ACTIVE": "Active packages: {items}", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_NONE": "No active packages.", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_PROMPT": "Tap a package to toggle its visibility." + "ADMIN_PUBLIC_OFFER_EDIT_CANCELLED": "Offer editing cancelled." } diff --git a/app/localization/locales/ru.json b/app/localization/locales/ru.json index e305d18b..b1513ddb 100644 --- a/app/localization/locales/ru.json +++ b/app/localization/locales/ru.json @@ -803,13 +803,5 @@ "ADMIN_SUPPORT_EDIT_DESCRIPTION_CONTACT_HINT": "Добавьте в описание при необходимости.", "ADMIN_SUPPORT_DESCRIPTION_UPDATED": "✅ Описание обновлено.", "ADMIN_SUPPORT_DESCRIPTION_SENT": "Текст отправлен ниже", - "ADMIN_SUPPORT_MESSAGE_DELETED": "Сообщение удалено", - "ADMIN_PRICING_BUTTON_TRAFFIC_OPTIONS": "🚦 Отображение пакетов", - "ADMIN_PRICING_TRAFFIC_PACKAGE_ENABLED": "Пакет включен.", - "ADMIN_PRICING_TRAFFIC_PACKAGE_DISABLED": "Пакет отключен.", - "ADMIN_PRICING_TRAFFIC_PACKAGE_MIN": "Должен оставаться хотя бы один пакет.", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_TITLE": "🚦 Отображение пакетов трафика", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_ACTIVE": "Активные пакеты: {items}", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_NONE": "Активных пакетов нет.", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_PROMPT": "Нажмите на пакет, чтобы включить или выключить его отображение." + "ADMIN_SUPPORT_MESSAGE_DELETED": "Сообщение удалено" } diff --git a/locales/en.json b/locales/en.json index 3444c456..c5e60530 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1032,7 +1032,6 @@ "ADMIN_PRICING_BUTTON_PERIOD_OPTIONS": "🗓 Available periods", "ADMIN_PRICING_BUTTON_PERIODS": "🗓 Subscription periods", "ADMIN_PRICING_BUTTON_TRAFFIC": "📦 Traffic packages", - "ADMIN_PRICING_BUTTON_TRAFFIC_OPTIONS": "🚦 Package visibility", "ADMIN_PRICING_BUTTON_TRIAL": "🎁 Trial period", "ADMIN_PRICING_CHOICE_ALREADY": "This option is already active.", "ADMIN_PRICING_CHOICE_UPDATED": "Selected: {label}", @@ -1049,9 +1048,6 @@ "ADMIN_PRICING_PERIOD_DISABLED": "Period disabled.", "ADMIN_PRICING_PERIOD_ENABLED": "Period enabled.", "ADMIN_PRICING_PERIOD_MIN": "At least one period must remain.", - "ADMIN_PRICING_TRAFFIC_PACKAGE_ENABLED": "Package enabled.", - "ADMIN_PRICING_TRAFFIC_PACKAGE_DISABLED": "Package disabled.", - "ADMIN_PRICING_TRAFFIC_PACKAGE_MIN": "At least one package must remain.", "ADMIN_PRICING_SECTION_CORE_TITLE": "⚙️ Core limits", "ADMIN_PRICING_SECTION_CURRENT": "Current values:", "ADMIN_PRICING_SECTION_EMPTY": "No values available.", @@ -1064,10 +1060,6 @@ "ADMIN_PRICING_SECTION_PROMPT": "Select what to update:", "ADMIN_PRICING_SECTION_SETTINGS_GENERIC": "⚙️ Settings", "ADMIN_PRICING_SECTION_TRAFFIC_TITLE": "📦 Traffic packages", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_TITLE": "🚦 Traffic package visibility", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_ACTIVE": "Active packages: {items}", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_NONE": "No active packages.", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_PROMPT": "Tap a package to toggle its visibility.", "ADMIN_PRICING_SECTION_TRIAL_TITLE": "🎁 Trial period", "ADMIN_PRICING_SETTING_CANCEL_HINT": "Reply \"Cancel\" to go back without changes.", "ADMIN_PRICING_SETTING_CURRENT": "Current value", diff --git a/locales/ru.json b/locales/ru.json index 47242a4d..cfe33d77 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1034,7 +1034,6 @@ "ADMIN_PRICING_BUTTON_PERIOD_OPTIONS": "🗓 Доступные периоды", "ADMIN_PRICING_BUTTON_PERIODS": "🗓 Периоды подписки", "ADMIN_PRICING_BUTTON_TRAFFIC": "📦 Пакеты трафика", - "ADMIN_PRICING_BUTTON_TRAFFIC_OPTIONS": "🚦 Отображение пакетов", "ADMIN_PRICING_BUTTON_TRIAL": "🎁 Пробный период", "ADMIN_PRICING_CHOICE_ALREADY": "Это значение уже активно.", "ADMIN_PRICING_CHOICE_UPDATED": "Выбрано: {label}", @@ -1051,9 +1050,6 @@ "ADMIN_PRICING_PERIOD_DISABLED": "Период отключен.", "ADMIN_PRICING_PERIOD_ENABLED": "Период включен.", "ADMIN_PRICING_PERIOD_MIN": "Должен оставаться хотя бы один период.", - "ADMIN_PRICING_TRAFFIC_PACKAGE_ENABLED": "Пакет включен.", - "ADMIN_PRICING_TRAFFIC_PACKAGE_DISABLED": "Пакет отключен.", - "ADMIN_PRICING_TRAFFIC_PACKAGE_MIN": "Должен оставаться хотя бы один пакет.", "ADMIN_PRICING_SECTION_CORE_TITLE": "⚙️ Базовые лимиты", "ADMIN_PRICING_SECTION_CURRENT": "Текущие значения:", "ADMIN_PRICING_SECTION_EMPTY": "Нет доступных значений.", @@ -1066,10 +1062,6 @@ "ADMIN_PRICING_SECTION_PROMPT": "Выберите что изменить:", "ADMIN_PRICING_SECTION_SETTINGS_GENERIC": "⚙️ Настройки", "ADMIN_PRICING_SECTION_TRAFFIC_TITLE": "📦 Пакеты трафика", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_TITLE": "🚦 Отображение пакетов трафика", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_ACTIVE": "Активные пакеты: {items}", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_NONE": "Активных пакетов нет.", - "ADMIN_PRICING_SECTION_TRAFFIC_OPTIONS_PROMPT": "Нажмите на пакет, чтобы включить или выключить его отображение.", "ADMIN_PRICING_SECTION_TRIAL_TITLE": "🎁 Пробный период", "ADMIN_PRICING_SETTING_CANCEL_HINT": "Чтобы вернуться без изменений, ответьте «Отмена».", "ADMIN_PRICING_SETTING_CURRENT": "Текущее значение",