diff --git a/app/bot.py b/app/bot.py index c598bdca..36532569 100644 --- a/app/bot.py +++ b/app/bot.py @@ -30,7 +30,6 @@ from app.handlers.admin import ( remnawave as admin_remnawave, statistics as admin_statistics, servers as admin_servers, - pricing as admin_pricing, maintenance as admin_maintenance, promo_groups as admin_promo_groups, campaigns as admin_campaigns, @@ -127,8 +126,7 @@ async def setup_bot() -> tuple[Bot, Dispatcher]: admin_main.register_handlers(dp) admin_users.register_handlers(dp) admin_subscriptions.register_handlers(dp) - admin_servers.register_handlers(dp) - admin_pricing.register_handlers(dp) + admin_servers.register_handlers(dp) admin_promocodes.register_handlers(dp) admin_messages.register_handlers(dp) admin_monitoring.register_handlers(dp) diff --git a/app/handlers/admin/bot_configuration.py b/app/handlers/admin/bot_configuration.py index 5fa0381f..ad0bea38 100644 --- a/app/handlers/admin/bot_configuration.py +++ b/app/handlers/admin/bot_configuration.py @@ -142,28 +142,6 @@ for _group_key, _title, _category_keys in CATEGORY_GROUP_DEFINITIONS: CATEGORY_FALLBACK_KEY = "other" CATEGORY_FALLBACK_TITLE = "📦 Прочие настройки" -PRICING_SETTING_KEYS: set[str] = { - "BASE_SUBSCRIPTION_PRICE", - "PRICE_14_DAYS", - "PRICE_30_DAYS", - "PRICE_60_DAYS", - "PRICE_90_DAYS", - "PRICE_180_DAYS", - "PRICE_360_DAYS", - "PRICE_PER_DEVICE", - "PRICE_TRAFFIC_5GB", - "PRICE_TRAFFIC_10GB", - "PRICE_TRAFFIC_25GB", - "PRICE_TRAFFIC_50GB", - "PRICE_TRAFFIC_100GB", - "PRICE_TRAFFIC_250GB", - "PRICE_TRAFFIC_500GB", - "PRICE_TRAFFIC_1000GB", - "PRICE_TRAFFIC_UNLIMITED", -} - -PRICING_CATEGORY_KEYS: set[str] = {"SUBSCRIPTION_PRICES", "TRAFFIC_PACKAGES"} - PRESET_CONFIGS: Dict[str, Dict[str, object]] = { "recommended": { "ENABLE_NOTIFICATIONS": True, @@ -224,49 +202,6 @@ def _get_group_description(group_key: str) -> str: return str(meta.get("description", "")) -def _is_pricing_setting_key(key: str) -> bool: - return key in PRICING_SETTING_KEYS - - -def _filter_pricing_definitions(definitions): - return [definition for definition in definitions if not _is_pricing_setting_key(definition.key)] - - -def _get_visible_definitions(category_key: str): - definitions = bot_configuration_service.get_settings_for_category(category_key) - return _filter_pricing_definitions(definitions) - - -def _is_pricing_category_key(category_key: str) -> bool: - if category_key in PRICING_CATEGORY_KEYS: - return True - - definitions = bot_configuration_service.get_settings_for_category(category_key) - if not definitions: - return False - - return not _filter_pricing_definitions(definitions) - - -def _build_pricing_redirect_keyboard(texts) -> types.InlineKeyboardMarkup: - return types.InlineKeyboardMarkup( - inline_keyboard=[ - [ - types.InlineKeyboardButton( - text=texts.t("ADMIN_PRICING_GO_TO_SECTION", "Перейти к управлению ценами"), - callback_data="admin_pricing", - ) - ], - [ - types.InlineKeyboardButton( - text=texts.t("ADMIN_PRICING_BACK_TO_CONFIG", "⬅️ К разделам"), - callback_data="admin_bot_config", - ) - ], - ] - ) - - def _get_group_icon(group_key: str) -> str: meta = _get_group_meta(group_key) return str(meta.get("icon", "⚙️")) @@ -402,11 +337,9 @@ def _render_dashboard_overview() -> str: for group_key, _title, items in grouped: for category_key, _label, count in items: total_settings += count - definitions = _get_visible_definitions(category_key) + definitions = bot_configuration_service.get_settings_for_category(category_key) total_overrides += sum( - 1 - for definition in definitions - if bot_configuration_service.has_override(definition.key) + 1 for definition in definitions if bot_configuration_service.has_override(definition.key) ) lines: List[str] = [ @@ -458,13 +391,7 @@ def _perform_settings_search(query: str) -> List[Dict[str, object]]: else: category_page = 1 - visible_definitions = [ - definition - for definition in definitions - if not _is_pricing_setting_key(definition.key) - ] - - for definition_index, definition in enumerate(visible_definitions): + for definition_index, definition in enumerate(definitions): fields = [definition.key.lower(), definition.display_name.lower()] guidance = bot_configuration_service.get_setting_guidance(definition.key) fields.extend( @@ -1127,34 +1054,25 @@ def _parse_group_payload(payload: str) -> Tuple[str, int]: def _get_grouped_categories() -> List[Tuple[str, str, List[Tuple[str, str, int]]]]: categories = bot_configuration_service.get_categories() - categories_map: Dict[str, Tuple[str, int]] = {} + categories_map = {key: (label, count) for key, label, count in categories} used: set[str] = set() grouped: List[Tuple[str, str, List[Tuple[str, str, int]]]] = [] - for key, label, _count in categories: - if _is_pricing_category_key(key): - continue - visible = _get_visible_definitions(key) - if not visible: - continue - categories_map[key] = (label, len(visible)) - for group_key, title, category_keys in CATEGORY_GROUP_DEFINITIONS: items: List[Tuple[str, str, int]] = [] for category_key in category_keys: - if category_key not in categories_map: - continue - label, count = categories_map[category_key] - items.append((category_key, label, count)) - used.add(category_key) + if category_key in categories_map: + label, count = categories_map[category_key] + items.append((category_key, label, count)) + used.add(category_key) if items: grouped.append((group_key, title, items)) - remaining: List[Tuple[str, str, int]] = [] - for key, (label, count) in categories_map.items(): - if key in used: - continue - remaining.append((key, label, count)) + remaining = [ + (key, label, count) + for key, (label, count) in categories_map.items() + if key not in used + ] if remaining: remaining.sort(key=lambda item: item[1]) @@ -1261,14 +1179,10 @@ def _build_categories_keyboard( buttons: List[types.InlineKeyboardButton] = [] for category_key, label, count in sliced: - definitions = _get_visible_definitions(category_key) - if not definitions: - continue - overrides = sum( - 1 - for definition in definitions - if bot_configuration_service.has_override(definition.key) - ) + overrides = 0 + for definition in bot_configuration_service.get_settings_for_category(category_key): + if bot_configuration_service.has_override(definition.key): + overrides += 1 badge = "✳️" if overrides else "•" button_text = f"{badge} {label} ({count})" buttons.append( @@ -1324,26 +1238,7 @@ def _build_settings_keyboard( language: str, page: int = 1, ) -> types.InlineKeyboardMarkup: - definitions = _get_visible_definitions(category_key) - if not definitions: - texts = get_texts(language) - return types.InlineKeyboardMarkup( - inline_keyboard=[ - [ - types.InlineKeyboardButton( - text=texts.t("ADMIN_PRICING_GO_TO_SECTION", "Перейти к управлению ценами"), - callback_data="admin_pricing", - ) - ], - [ - types.InlineKeyboardButton( - text=texts.t("ADMIN_PRICING_BACK_TO_CONFIG", "⬅️ К разделам"), - callback_data="admin_bot_config", - ) - ], - ] - ) - + definitions = bot_configuration_service.get_settings_for_category(category_key) total_pages = max(1, math.ceil(len(definitions) / SETTINGS_PAGE_SIZE)) page = max(1, min(page, total_pages)) @@ -1633,17 +1528,10 @@ async def show_bot_config_category( group_key, category_key, category_page, settings_page = _parse_category_payload( callback.data ) - definitions = _get_visible_definitions(category_key) + definitions = bot_configuration_service.get_settings_for_category(category_key) if not definitions: - texts = get_texts(db_user.language) - message = texts.t( - "ADMIN_PRICING_MOVED_MESSAGE", - "💡 Управление ценами переехало в раздел «Цены» на главной странице админ-панели.", - ) - keyboard = _build_pricing_redirect_keyboard(texts) - await callback.message.edit_text(message, reply_markup=keyboard, parse_mode="HTML") - await callback.answer() + await callback.answer("В этой категории пока нет настроек", show_alert=True) return category_label = definitions[0].category_label @@ -2141,19 +2029,6 @@ async def show_bot_config_setting( except KeyError: await callback.answer("Эта настройка больше недоступна", show_alert=True) return - if _is_pricing_setting_key(key): - texts = get_texts(db_user.language) - message = texts.t( - "ADMIN_PRICING_MOVED_MESSAGE", - "💡 Управление ценами переехало в раздел «Цены» на главной странице админ-панели.", - ) - await callback.message.edit_text( - message, - reply_markup=_build_pricing_redirect_keyboard(texts), - parse_mode="HTML", - ) - await callback.answer() - return text = _render_setting_text(key) keyboard = _build_setting_keyboard(key, group_key, category_page, settings_page) await callback.message.edit_text(text, reply_markup=keyboard) @@ -2191,19 +2066,6 @@ async def start_edit_setting( except KeyError: await callback.answer("Эта настройка больше недоступна", show_alert=True) return - if _is_pricing_setting_key(key): - texts = get_texts(db_user.language) - message = texts.t( - "ADMIN_PRICING_MOVED_MESSAGE", - "💡 Управление ценами переехало в раздел «Цены» на главной странице админ-панели.", - ) - await callback.message.edit_text( - message, - reply_markup=_build_pricing_redirect_keyboard(texts), - parse_mode="HTML", - ) - await callback.answer() - return definition = bot_configuration_service.get_definition(key) summary = bot_configuration_service.get_setting_summary(key) @@ -2358,19 +2220,6 @@ async def reset_setting( except KeyError: await callback.answer("Эта настройка больше недоступна", show_alert=True) return - if _is_pricing_setting_key(key): - texts = get_texts(db_user.language) - message = texts.t( - "ADMIN_PRICING_MOVED_MESSAGE", - "💡 Управление ценами переехало в раздел «Цены» на главной странице админ-панели.", - ) - await callback.message.edit_text( - message, - reply_markup=_build_pricing_redirect_keyboard(texts), - parse_mode="HTML", - ) - await callback.answer() - return await bot_configuration_service.reset_value(db, key) await db.commit() @@ -2411,19 +2260,6 @@ async def toggle_setting( except KeyError: await callback.answer("Эта настройка больше недоступна", show_alert=True) return - if _is_pricing_setting_key(key): - texts = get_texts(db_user.language) - message = texts.t( - "ADMIN_PRICING_MOVED_MESSAGE", - "💡 Управление ценами переехало в раздел «Цены» на главной странице админ-панели.", - ) - await callback.message.edit_text( - message, - reply_markup=_build_pricing_redirect_keyboard(texts), - parse_mode="HTML", - ) - await callback.answer() - return current = bot_configuration_service.get_current_value(key) new_value = not bool(current) await bot_configuration_service.set_value(db, key, new_value) @@ -2468,19 +2304,6 @@ async def apply_setting_choice( except KeyError: await callback.answer("Эта настройка больше недоступна", show_alert=True) return - if _is_pricing_setting_key(key): - texts = get_texts(db_user.language) - message = texts.t( - "ADMIN_PRICING_MOVED_MESSAGE", - "💡 Управление ценами переехало в раздел «Цены» на главной странице админ-панели.", - ) - await callback.message.edit_text( - message, - reply_markup=_build_pricing_redirect_keyboard(texts), - parse_mode="HTML", - ) - await callback.answer() - return try: value = bot_configuration_service.resolve_choice_token(key, choice_token) diff --git a/app/handlers/admin/pricing.py b/app/handlers/admin/pricing.py deleted file mode 100644 index b2d37278..00000000 --- a/app/handlers/admin/pricing.py +++ /dev/null @@ -1,470 +0,0 @@ -import logging -from typing import Iterable, List, Tuple - -from aiogram import Dispatcher, F, types -from aiogram.fsm.context import FSMContext -from sqlalchemy.ext.asyncio import AsyncSession - -from app.config import settings -from app.database.models import User -from app.keyboards.admin import get_admin_pricing_keyboard -from app.localization.texts import get_texts -from app.services.system_settings_service import bot_configuration_service -from app.states import PricingStates -from app.utils.decorators import admin_required, error_handler - - -logger = logging.getLogger(__name__) - -SUBSCRIPTION_PRICE_ENTRIES: List[Tuple[str, str, str]] = [ - ("base", "BASE_SUBSCRIPTION_PRICE", "Базовая цена"), - ("14", "PRICE_14_DAYS", "14 дней"), - ("30", "PRICE_30_DAYS", "30 дней"), - ("60", "PRICE_60_DAYS", "60 дней"), - ("90", "PRICE_90_DAYS", "90 дней"), - ("180", "PRICE_180_DAYS", "180 дней"), - ("360", "PRICE_360_DAYS", "360 дней"), -] - -TRAFFIC_PRICE_ENTRIES: List[Tuple[str, str, str]] = [ - ("5", "PRICE_TRAFFIC_5GB", "5 ГБ"), - ("10", "PRICE_TRAFFIC_10GB", "10 ГБ"), - ("25", "PRICE_TRAFFIC_25GB", "25 ГБ"), - ("50", "PRICE_TRAFFIC_50GB", "50 ГБ"), - ("100", "PRICE_TRAFFIC_100GB", "100 ГБ"), - ("250", "PRICE_TRAFFIC_250GB", "250 ГБ"), - ("500", "PRICE_TRAFFIC_500GB", "500 ГБ"), - ("1000", "PRICE_TRAFFIC_1000GB", "1000 ГБ"), - ("unlimited", "PRICE_TRAFFIC_UNLIMITED", "Безлимит"), -] - -DEVICE_PRICE_ENTRY: Tuple[str, str, str] = ( - "devices", - "PRICE_PER_DEVICE", - "Дополнительное устройство", -) - -MAX_PRICE_RUBLES = 1_000_000 - - -def _format_price(value: int) -> str: - return settings.format_price(int(value)) - - -def _build_price_buttons( - entries: Iterable[Tuple[str, str, str]], - prefix: str, -) -> List[List[types.InlineKeyboardButton]]: - buttons: List[List[types.InlineKeyboardButton]] = [] - row: List[types.InlineKeyboardButton] = [] - - for token, key, label in entries: - current_value = bot_configuration_service.get_current_value(key) - button = types.InlineKeyboardButton( - text=f"{label} — {_format_price(current_value)}", - callback_data=f"admin_pricing_edit_{prefix}_{token}", - ) - row.append(button) - if len(row) == 2: - buttons.append(row) - row = [] - - if row: - buttons.append(row) - - return buttons - - -async def _prompt_price_input( - callback: types.CallbackQuery, - state: FSMContext, - key: str, - label: str, - return_callback: str, - language: str, -) -> None: - texts = get_texts(language) - current_value = bot_configuration_service.get_current_value(key) - current_price = _format_price(current_value) - - await state.set_state(PricingStates.waiting_for_value) - await state.update_data( - target_key=key, - target_label=label, - return_callback=return_callback, - ) - - prompt_lines = [ - texts.t("ADMIN_PRICING_EDIT_TITLE", "💰 Изменение цены"), - "", - texts.t("ADMIN_PRICING_CURRENT_PRICE", "Текущая цена: {price}").format( - price=current_price - ), - texts.t("ADMIN_PRICING_PROMPT", "Отправьте новую цену для {name}:").format( - name=label - ), - texts.t( - "ADMIN_PRICING_ENTER_PRICE", - "Введите новую цену в рублях (можно использовать копейки через точку).", - ), - texts.t("ADMIN_PRICING_CANCEL_HINT", "Для отмены воспользуйтесь кнопкой ниже."), - ] - - keyboard = types.InlineKeyboardMarkup( - inline_keyboard=[ - [ - types.InlineKeyboardButton( - text=texts.t("ADMIN_PRICING_CANCEL", "❌ Отмена"), - callback_data=return_callback, - ) - ] - ] - ) - - await callback.message.edit_text( - "\n".join(prompt_lines), - reply_markup=keyboard, - parse_mode="HTML", - ) - await callback.answer() - - -@admin_required -@error_handler -async def show_pricing_menu( - callback: types.CallbackQuery, - state: FSMContext, - db_user: User, - db: AsyncSession, -): - await state.clear() - texts = get_texts(db_user.language) - - summary_lines = [ - texts.t("ADMIN_PRICING_TITLE", "💰 Управление ценами"), - "", - texts.t( - "ADMIN_PRICING_DESCRIPTION", - "Выберите раздел для редактирования стоимости подписок, трафика и устройств.", - ), - "", - ] - - summary_lines.append(texts.t("ADMIN_PRICING_OVERVIEW", "Ключевые значения:")) - summary_lines.append( - f"• 30 дней: {_format_price(settings.PRICE_30_DAYS)}" - ) - summary_lines.append( - f"• 90 дней: {_format_price(settings.PRICE_90_DAYS)}" - ) - summary_lines.append( - f"• {_format_price(settings.PRICE_PER_DEVICE)} — {texts.t('ADMIN_PRICING_DEVICE_SHORT', 'дополнительное устройство')}" - ) - - await callback.message.edit_text( - "\n".join(summary_lines), - reply_markup=get_admin_pricing_keyboard(db_user.language), - parse_mode="HTML", - ) - await callback.answer() - - -@admin_required -@error_handler -async def show_subscription_prices( - callback: types.CallbackQuery, - state: FSMContext, - db_user: User, - db: AsyncSession, -): - await state.clear() - texts = get_texts(db_user.language) - - lines = [ - texts.t("ADMIN_PRICING_SUBSCRIPTIONS_TITLE", "📅 Стоимость подписок"), - "", - texts.t( - "ADMIN_PRICING_SUBSCRIPTIONS_HELP", - "Нажмите на период, чтобы изменить цену. Значения указаны в месяцах.", - ), - "", - ] - - for _token, key, label in SUBSCRIPTION_PRICE_ENTRIES: - lines.append(f"• {label}: {_format_price(bot_configuration_service.get_current_value(key))}") - - keyboard_rows = _build_price_buttons(SUBSCRIPTION_PRICE_ENTRIES, "subscription") - keyboard_rows.append( - [types.InlineKeyboardButton(text=texts.BACK, callback_data="admin_pricing")] - ) - - await callback.message.edit_text( - "\n".join(lines), - reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard_rows), - parse_mode="HTML", - ) - await callback.answer() - - -@admin_required -@error_handler -async def show_traffic_prices( - callback: types.CallbackQuery, - state: FSMContext, - db_user: User, - db: AsyncSession, -): - await state.clear() - texts = get_texts(db_user.language) - - lines = [ - texts.t("ADMIN_PRICING_TRAFFIC_TITLE", "📦 Стоимость пакетов трафика"), - "", - texts.t( - "ADMIN_PRICING_TRAFFIC_HELP", - "Нажмите на пакет, чтобы обновить цену. Цена применяется за выбранный объём трафика.", - ), - "", - ] - - for _token, key, label in TRAFFIC_PRICE_ENTRIES: - lines.append(f"• {label}: {_format_price(bot_configuration_service.get_current_value(key))}") - - keyboard_rows = _build_price_buttons(TRAFFIC_PRICE_ENTRIES, "traffic") - keyboard_rows.append( - [types.InlineKeyboardButton(text=texts.BACK, callback_data="admin_pricing")] - ) - - await callback.message.edit_text( - "\n".join(lines), - reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard_rows), - parse_mode="HTML", - ) - await callback.answer() - - -@admin_required -@error_handler -async def show_device_price( - callback: types.CallbackQuery, - state: FSMContext, - db_user: User, - db: AsyncSession, -): - await state.clear() - texts = get_texts(db_user.language) - - _token, key, label = DEVICE_PRICE_ENTRY - current_value = bot_configuration_service.get_current_value(key) - - lines = [ - texts.t("ADMIN_PRICING_DEVICES_TITLE", "📱 Стоимость дополнительных устройств"), - "", - texts.t( - "ADMIN_PRICING_DEVICES_HELP", - "Цена применяется за каждое устройство сверх базового лимита подписки.", - ), - "", - f"{label}: {_format_price(current_value)}", - ] - - keyboard = types.InlineKeyboardMarkup( - inline_keyboard=[ - [ - types.InlineKeyboardButton( - text=texts.t("ADMIN_PRICING_EDIT_BUTTON", "✏️ Изменить цену"), - callback_data="admin_pricing_edit_devices", - ) - ], - [types.InlineKeyboardButton(text=texts.BACK, callback_data="admin_pricing")], - ] - ) - - await callback.message.edit_text( - "\n".join(lines), - reply_markup=keyboard, - parse_mode="HTML", - ) - await callback.answer() - - -@admin_required -@error_handler -async def start_subscription_price_edit( - callback: types.CallbackQuery, - state: FSMContext, - db_user: User, - db: AsyncSession, -): - token = callback.data.split("_")[-1] - for entry_token, key, label in SUBSCRIPTION_PRICE_ENTRIES: - if entry_token == token: - await _prompt_price_input( - callback, - state, - key, - label, - "admin_pricing_subscriptions", - db_user.language, - ) - return - - await callback.answer("❌ Значение недоступно", show_alert=True) - - -@admin_required -@error_handler -async def start_traffic_price_edit( - callback: types.CallbackQuery, - state: FSMContext, - db_user: User, - db: AsyncSession, -): - token = callback.data.split("_")[-1] - for entry_token, key, label in TRAFFIC_PRICE_ENTRIES: - if entry_token == token: - await _prompt_price_input( - callback, - state, - key, - label, - "admin_pricing_traffic", - db_user.language, - ) - return - - await callback.answer("❌ Значение недоступно", show_alert=True) - - -@admin_required -@error_handler -async def start_device_price_edit( - callback: types.CallbackQuery, - state: FSMContext, - db_user: User, - db: AsyncSession, -): - _token, key, label = DEVICE_PRICE_ENTRY - await _prompt_price_input( - callback, - state, - key, - label, - "admin_pricing_devices", - db_user.language, - ) - - -@admin_required -@error_handler -async def process_price_input( - message: types.Message, - state: FSMContext, - db_user: User, - db: AsyncSession, -): - data = await state.get_data() - key = data.get("target_key") - label = data.get("target_label", "") - return_callback = data.get("return_callback", "admin_pricing") - texts = get_texts(db_user.language) - - if not key: - await message.answer("❌ Не удалось определить настройку цены. Попробуйте снова из меню цен.") - await state.clear() - return - - raw_text = (message.text or "").strip() - if raw_text.lower() in {"cancel", "отмена"}: - await state.clear() - await message.answer( - texts.t("ADMIN_PRICING_CANCELLED", "Отменено."), - reply_markup=types.InlineKeyboardMarkup( - inline_keyboard=[ - [ - types.InlineKeyboardButton( - text=texts.BACK, - callback_data=return_callback, - ) - ] - ] - ), - ) - return - - try: - price_rubles = float(raw_text.replace(",", ".")) - except ValueError: - await message.answer( - texts.t( - "ADMIN_PRICING_INVALID_PRICE", - "❌ Неверный формат цены. Используйте число, например 199.90", - ) - ) - return - - if price_rubles < 0: - await message.answer( - texts.t("ADMIN_PRICING_INVALID_PRICE", "❌ Неверный формат цены. Используйте число, например 199.90"), - ) - return - - if price_rubles > MAX_PRICE_RUBLES: - await message.answer( - texts.t( - "ADMIN_PRICING_TOO_HIGH", - "❌ Слишком высокая цена. Укажите значение до 1 000 000 ₽.", - ) - ) - return - - price_kopeks = int(round(price_rubles * 100)) - - await bot_configuration_service.set_value(db, key, price_kopeks) - await state.clear() - - logger.info("✅ Обновлена цена %s: %s ₽", key, price_rubles) - - confirmation = texts.t( - "ADMIN_PRICING_PRICE_UPDATED", - "✅ Цена обновлена: {name} — {price}", - ).format(name=label or key, price=_format_price(price_kopeks)) - - keyboard = types.InlineKeyboardMarkup( - inline_keyboard=[ - [ - types.InlineKeyboardButton( - text=texts.t("ADMIN_PRICING_BACK_TO_SECTION", "⬅️ Назад к разделу"), - callback_data=return_callback, - ) - ] - ] - ) - - await message.answer(confirmation, reply_markup=keyboard, parse_mode="HTML") - - -def register_handlers(dp: Dispatcher) -> None: - dp.callback_query.register(show_pricing_menu, F.data == "admin_pricing") - dp.callback_query.register( - show_subscription_prices, F.data == "admin_pricing_subscriptions" - ) - dp.callback_query.register( - show_traffic_prices, F.data == "admin_pricing_traffic" - ) - dp.callback_query.register(show_device_price, F.data == "admin_pricing_devices") - dp.callback_query.register( - start_subscription_price_edit, - F.data.startswith("admin_pricing_edit_subscription_"), - ) - dp.callback_query.register( - start_traffic_price_edit, F.data.startswith("admin_pricing_edit_traffic_"), - ) - dp.callback_query.register( - start_device_price_edit, F.data == "admin_pricing_edit_devices" - ) - dp.message.register( - process_price_input, - PricingStates.waiting_for_value, - ) - diff --git a/app/keyboards/admin.py b/app/keyboards/admin.py index c9142a8a..ee7729af 100644 --- a/app/keyboards/admin.py +++ b/app/keyboards/admin.py @@ -14,8 +14,6 @@ def get_admin_main_keyboard(language: str = "ru") -> InlineKeyboardMarkup: return InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text=_t(texts, "ADMIN_MAIN_USERS_SUBSCRIPTIONS", "👥 Юзеры/Подписки"), callback_data="admin_submenu_users")], - [InlineKeyboardButton(text=_t(texts, "ADMIN_MAIN_SERVERS", "🌐 Сервера"), callback_data="admin_servers")], - [InlineKeyboardButton(text=_t(texts, "ADMIN_MAIN_PRICING", "💰 Цены"), callback_data="admin_pricing")], [InlineKeyboardButton(text=_t(texts, "ADMIN_MAIN_PROMO_STATS", "💰 Промокоды/Статистика"), callback_data="admin_submenu_promo")], [InlineKeyboardButton(text=_t(texts, "ADMIN_MAIN_SUPPORT", "🛟 Поддержка"), callback_data="admin_submenu_support")], [InlineKeyboardButton(text=_t(texts, "ADMIN_MAIN_MESSAGES", "📨 Сообщения"), callback_data="admin_submenu_communications")], @@ -173,32 +171,6 @@ def get_admin_system_submenu_keyboard(language: str = "ru") -> InlineKeyboardMar ]) -def get_admin_pricing_keyboard(language: str = "ru") -> InlineKeyboardMarkup: - texts = get_texts(language) - - return InlineKeyboardMarkup(inline_keyboard=[ - [ - InlineKeyboardButton( - text=_t(texts, "ADMIN_PRICING_SUBSCRIPTIONS_BUTTON", "📅 Подписки"), - callback_data="admin_pricing_subscriptions", - ) - ], - [ - InlineKeyboardButton( - text=_t(texts, "ADMIN_PRICING_TRAFFIC_BUTTON", "📦 Трафик"), - callback_data="admin_pricing_traffic", - ) - ], - [ - InlineKeyboardButton( - text=_t(texts, "ADMIN_PRICING_DEVICES_BUTTON", "📱 Устройства"), - callback_data="admin_pricing_devices", - ) - ], - [InlineKeyboardButton(text=texts.BACK, callback_data="admin_panel")], - ]) - - def get_admin_reports_keyboard(language: str = "ru") -> InlineKeyboardMarkup: texts = get_texts(language) diff --git a/app/localization/locales/ru.json b/app/localization/locales/ru.json index dbb778d6..4ca51d14 100644 --- a/app/localization/locales/ru.json +++ b/app/localization/locales/ru.json @@ -20,8 +20,6 @@ "ADMIN_MESSAGES": "📨 Рассылки", "ADMIN_MONITORING": "🔍 Мониторинг", "ADMIN_PANEL": "\n⚙️ Административная панель\n\nВыберите раздел для управления:\n", - "ADMIN_MAIN_SERVERS": "🌐 Сервера", - "ADMIN_MAIN_PRICING": "💰 Цены", "ADMIN_PROMOCODES": "🎫 Промокоды", "ADMIN_REFERRALS": "🤝 Партнерка", "ADMIN_REMNAWAVE": "🖥️ Remnawave", @@ -40,34 +38,6 @@ "ADMIN_PROMO_GROUP_TOGGLE_ADDON_DISCOUNT_DISABLE": "🧩 Отключить скидки на доп. услуги", "ADMIN_PROMO_GROUP_ADDON_DISCOUNT_UPDATED_ENABLED": "🧩 Скидки на докупку доп. услуг включены.", "ADMIN_PROMO_GROUP_ADDON_DISCOUNT_UPDATED_DISABLED": "🧩 Скидки на докупку доп. услуг отключены.", - "ADMIN_PRICING_SUBSCRIPTIONS_BUTTON": "📅 Подписки", - "ADMIN_PRICING_TRAFFIC_BUTTON": "📦 Трафик", - "ADMIN_PRICING_DEVICES_BUTTON": "📱 Устройства", - "ADMIN_PRICING_TITLE": "💰 Управление ценами", - "ADMIN_PRICING_DESCRIPTION": "Выберите раздел для редактирования стоимости подписок, трафика и устройств.", - "ADMIN_PRICING_OVERVIEW": "Ключевые значения:", - "ADMIN_PRICING_DEVICE_SHORT": "дополнительное устройство", - "ADMIN_PRICING_SUBSCRIPTIONS_TITLE": "📅 Стоимость подписок", - "ADMIN_PRICING_SUBSCRIPTIONS_HELP": "Нажмите на период, чтобы изменить цену. Значения указаны в месяцах.", - "ADMIN_PRICING_TRAFFIC_TITLE": "📦 Стоимость пакетов трафика", - "ADMIN_PRICING_TRAFFIC_HELP": "Нажмите на пакет, чтобы обновить цену. Цена применяется за выбранный объём трафика.", - "ADMIN_PRICING_DEVICES_TITLE": "📱 Стоимость дополнительных устройств", - "ADMIN_PRICING_DEVICES_HELP": "Цена применяется за каждое устройство сверх базового лимита подписки.", - "ADMIN_PRICING_EDIT_BUTTON": "✏️ Изменить цену", - "ADMIN_PRICING_EDIT_TITLE": "💰 Изменение цены", - "ADMIN_PRICING_CURRENT_PRICE": "Текущая цена: {price}", - "ADMIN_PRICING_PROMPT": "Отправьте новую цену для {name}:", - "ADMIN_PRICING_ENTER_PRICE": "Введите новую цену в рублях (можно использовать копейки через точку).", - "ADMIN_PRICING_CANCEL_HINT": "Для отмены воспользуйтесь кнопкой ниже.", - "ADMIN_PRICING_CANCEL": "❌ Отмена", - "ADMIN_PRICING_CANCELLED": "Отменено.", - "ADMIN_PRICING_INVALID_PRICE": "❌ Неверный формат цены. Используйте число, например 199.90", - "ADMIN_PRICING_TOO_HIGH": "❌ Слишком высокая цена. Укажите значение до 1 000 000 ₽.", - "ADMIN_PRICING_PRICE_UPDATED": "✅ Цена обновлена: {name} — {price}", - "ADMIN_PRICING_BACK_TO_SECTION": "⬅️ Назад к разделу", - "ADMIN_PRICING_GO_TO_SECTION": "Перейти к управлению ценами", - "ADMIN_PRICING_BACK_TO_CONFIG": "⬅️ К разделам", - "ADMIN_PRICING_MOVED_MESSAGE": "💡 Управление ценами переехало в раздел «Цены» на главной странице админ-панели.", "ADMIN_PROMO_GROUPS_DEFAULT_LABEL": " (базовая)", "ADMIN_PROMO_GROUPS_MEMBERS_COUNT": "Участников: {count}", "ADMIN_PROMO_GROUPS_EMPTY": "Промогруппы не найдены.", diff --git a/app/states.py b/app/states.py index 25ceccd1..61015d48 100644 --- a/app/states.py +++ b/app/states.py @@ -135,10 +135,6 @@ class BotConfigStates(StatesGroup): waiting_for_search_query = State() waiting_for_import_file = State() - -class PricingStates(StatesGroup): - waiting_for_value = State() - class AutoPayStates(StatesGroup): setting_autopay_days = State() confirming_autopay_toggle = State()