Revert "Add admin controls for traffic package visibility"

This commit is contained in:
Egor
2025-10-08 07:30:56 +03:00
committed by GitHub
parent 2e4317e457
commit 20855e5bfc
5 changed files with 28 additions and 268 deletions

View File

@@ -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:"),

View File

@@ -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."
}

View File

@@ -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": "Сообщение удалено"
}

View File

@@ -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",

View File

@@ -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": "Текущее значение",