diff --git a/app/bot.py b/app/bot.py
index bdb21e50..36532569 100644
--- a/app/bot.py
+++ b/app/bot.py
@@ -40,7 +40,6 @@ from app.handlers.admin import (
welcome_text as admin_welcome_text,
tickets as admin_tickets,
reports as admin_reports,
- pricing as admin_pricing,
bot_configuration as admin_bot_configuration,
)
from app.handlers.stars_payments import register_stars_handlers
@@ -145,7 +144,6 @@ async def setup_bot() -> tuple[Bot, Dispatcher]:
admin_welcome_text.register_welcome_text_handlers(dp)
admin_tickets.register_handlers(dp)
admin_reports.register_handlers(dp)
- admin_pricing.register_handlers(dp)
admin_bot_configuration.register_handlers(dp)
common.register_handlers(dp)
register_stars_handlers(dp)
diff --git a/app/handlers/admin/pricing.py b/app/handlers/admin/pricing.py
deleted file mode 100644
index 26d3d2a9..00000000
--- a/app/handlers/admin/pricing.py
+++ /dev/null
@@ -1,566 +0,0 @@
-import logging
-from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
-from typing import Optional, Tuple
-
-from aiogram import Dispatcher, F, types
-from aiogram.fsm.context import FSMContext
-from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
-from sqlalchemy.ext.asyncio import AsyncSession
-
-from app.config import settings
-from app.database.models import User
-from app.localization.texts import get_texts
-from app.services.system_settings_service import bot_configuration_service
-from app.states import AdminPricingStates
-from app.utils.decorators import admin_required, error_handler
-
-logger = logging.getLogger(__name__)
-
-PERIOD_SETTINGS: Tuple[Tuple[int, str], ...] = (
- (14, "PRICE_14_DAYS"),
- (30, "PRICE_30_DAYS"),
- (60, "PRICE_60_DAYS"),
- (90, "PRICE_90_DAYS"),
- (180, "PRICE_180_DAYS"),
- (360, "PRICE_360_DAYS"),
-)
-
-TRAFFIC_SETTINGS: 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"),
-)
-
-EXTRA_SETTINGS: Tuple[Tuple[str, str], ...] = (
- ("device", "PRICE_PER_DEVICE"),
-)
-
-MAX_PRICE_RUBLES = Decimal("100000")
-
-
-def _format_price(value: Optional[int]) -> str:
- return settings.format_price(int(value or 0))
-
-
-def _get_current_value(key: str) -> int:
- current = bot_configuration_service.get_current_value(key)
- if current is None:
- original = bot_configuration_service.get_original_value(key)
- return int(original or 0)
- return int(current)
-
-
-def _get_period_label(texts, days: int) -> str:
- return texts.t("ADMIN_PRICING_DAYS_LABEL", "{days} дн.").format(days=days)
-
-
-def _get_traffic_label(texts, amount: int) -> str:
- if amount == 0:
- return texts.t("ADMIN_PRICING_UNLIMITED_LABEL", "Безлимит")
- return texts.t("ADMIN_PRICING_TRAFFIC_GB", "{gb} ГБ").format(gb=amount)
-
-
-def _build_main_summary(texts) -> str:
- period_lines = [
- texts.t(
- f"PERIOD_{days}_DAYS",
- f"📅 {days} дней - {_format_price(_get_current_value(key))}",
- )
- for days, key in PERIOD_SETTINGS
- ]
-
- traffic_lines = []
- for amount, key in TRAFFIC_SETTINGS:
- traffic_key = "UNLIMITED" if amount == 0 else f"{amount}GB"
- default_label = (
- f"📊 {_get_traffic_label(texts, amount)} - {_format_price(_get_current_value(key))}"
- )
- traffic_lines.append(
- texts.t(f"TRAFFIC_{traffic_key}", default_label)
- )
-
- device_line = texts.t(
- "ADMIN_PRICING_DEVICE_LABEL",
- "📱 Дополнительное устройство — {price}",
- ).format(price=_format_price(_get_current_value("PRICE_PER_DEVICE")))
-
- parts = [
- texts.t("ADMIN_PRICING_TITLE", "💰 Управление ценами"),
- "",
- texts.t(
- "ADMIN_PRICING_DESCRIPTION",
- "Настройте стоимость подписок, пакетов трафика и дополнительных опций.",
- ),
- "",
- texts.t("ADMIN_PRICING_SECTION_PERIODS", "📅 Периоды подписки"),
- "\n".join(period_lines),
- "",
- texts.t("ADMIN_PRICING_SECTION_TRAFFIC", "📦 Пакеты трафика"),
- "\n".join(traffic_lines),
- "",
- texts.t("ADMIN_PRICING_SECTION_EXTRAS", "🔧 Дополнительные опции"),
- device_line,
- ]
-
- return "\n".join(part for part in parts if part)
-
-
-def _period_keyboard(texts) -> InlineKeyboardMarkup:
- rows = [
- [
- InlineKeyboardButton(
- text=texts.t("ADMIN_PRICING_EDIT_PERIOD", "✏️ {label}").format(
- label=_get_period_label(texts, days)
- ),
- callback_data=f"admin_pricing_edit_period_{days}",
- )
- ]
- for days, _ in PERIOD_SETTINGS
- ]
- rows.append([InlineKeyboardButton(text=texts.BACK, callback_data="admin_pricing")])
- return InlineKeyboardMarkup(inline_keyboard=rows)
-
-
-def _traffic_keyboard(texts) -> InlineKeyboardMarkup:
- rows = [
- [
- InlineKeyboardButton(
- text=texts.t("ADMIN_PRICING_EDIT_TRAFFIC", "✏️ {label}").format(
- label=_get_traffic_label(texts, amount)
- ),
- callback_data=(
- f"admin_pricing_edit_traffic_{'unlimited' if amount == 0 else amount}"
- ),
- )
- ]
- for amount, _ in TRAFFIC_SETTINGS
- ]
- rows.append([InlineKeyboardButton(text=texts.BACK, callback_data="admin_pricing")])
- return InlineKeyboardMarkup(inline_keyboard=rows)
-
-
-def _extras_keyboard(texts) -> InlineKeyboardMarkup:
- return InlineKeyboardMarkup(inline_keyboard=[
- [
- InlineKeyboardButton(
- text=texts.t("ADMIN_PRICING_EDIT_DEVICE", "✏️ Цена за устройство"),
- callback_data="admin_pricing_edit_extra_device",
- )
- ],
- [InlineKeyboardButton(text=texts.BACK, callback_data="admin_pricing")],
- ])
-
-
-def _pricing_main_keyboard(texts) -> InlineKeyboardMarkup:
- return InlineKeyboardMarkup(inline_keyboard=[
- [InlineKeyboardButton(text=texts.t("ADMIN_PRICING_PERIODS", "📅 Периоды подписки"), callback_data="admin_pricing_periods")],
- [InlineKeyboardButton(text=texts.t("ADMIN_PRICING_TRAFFIC", "📦 Трафик-пакеты"), callback_data="admin_pricing_traffic")],
- [InlineKeyboardButton(text=texts.t("ADMIN_PRICING_EXTRAS", "🔧 Доп. опции"), callback_data="admin_pricing_extras")],
- [InlineKeyboardButton(text=texts.BACK, callback_data="admin_panel")],
- ])
-
-
-def _find_period_setting(days: int) -> Optional[str]:
- for item_days, key in PERIOD_SETTINGS:
- if item_days == days:
- return key
- return None
-
-
-def _find_traffic_setting(identifier: str) -> Optional[Tuple[int, str]]:
- if identifier == "unlimited":
- identifier_value = 0
- else:
- try:
- identifier_value = int(identifier)
- except ValueError:
- return None
- for amount, key in TRAFFIC_SETTINGS:
- if amount == identifier_value:
- return amount, key
- return None
-
-
-def _find_extra_setting(slug: str) -> Optional[str]:
- for extra_slug, key in EXTRA_SETTINGS:
- if extra_slug == slug:
- return key
- return None
-
-
-async def _prompt_price_input(
- callback: types.CallbackQuery,
- state: FSMContext,
- texts,
- label: str,
- current_price: str,
- setting_key: str,
- return_callback: str,
-) -> None:
- await state.set_state(AdminPricingStates.waiting_for_price)
- await state.update_data(
- pricing_key=setting_key,
- pricing_label=label,
- return_callback=return_callback,
- )
-
- await callback.message.edit_text(
- texts.t(
- "ADMIN_PRICING_PROMPT",
- "Введите новую цену для {label}.\nТекущая цена: {current}.\n\nПришлите значение в рублях (пример: 990 или 349.99).",
- ).format(label=label, current=current_price),
- reply_markup=InlineKeyboardMarkup(inline_keyboard=[
- [InlineKeyboardButton(text=texts.BACK, callback_data=return_callback)]
- ]),
- )
- await callback.answer()
-
-
-@admin_required
-@error_handler
-async def show_pricing_dashboard(
- callback: types.CallbackQuery,
- db_user: User,
- db: AsyncSession,
-):
- texts = get_texts(db_user.language)
-
- await callback.message.edit_text(
- _build_main_summary(texts),
- reply_markup=_pricing_main_keyboard(texts),
- )
- await callback.answer()
-
-
-@admin_required
-@error_handler
-async def show_pricing_periods(
- callback: types.CallbackQuery,
- db_user: User,
- db: AsyncSession,
-):
- texts = get_texts(db_user.language)
-
- period_lines = [
- texts.t(
- f"PERIOD_{days}_DAYS",
- f"📅 {days} дней - {_format_price(_get_current_value(key))}",
- )
- for days, key in PERIOD_SETTINGS
- ]
-
- text = "\n".join(
- part
- for part in (
- texts.t("ADMIN_PRICING_TITLE", "💰 Управление ценами"),
- "",
- texts.t("ADMIN_PRICING_SECTION_PERIODS", "📅 Периоды подписки"),
- "\n".join(period_lines),
- "",
- texts.t(
- "ADMIN_PRICING_HINT_PERIODS",
- "Выберите период, чтобы изменить его стоимость.",
- ),
- )
- if part
- )
-
- await callback.message.edit_text(
- text,
- reply_markup=_period_keyboard(texts),
- )
- await callback.answer()
-
-
-@admin_required
-@error_handler
-async def show_pricing_traffic(
- callback: types.CallbackQuery,
- db_user: User,
- db: AsyncSession,
-):
- texts = get_texts(db_user.language)
-
- traffic_lines = []
- for amount, key in TRAFFIC_SETTINGS:
- traffic_key = "UNLIMITED" if amount == 0 else f"{amount}GB"
- default_label = (
- f"📊 {_get_traffic_label(texts, amount)} - {_format_price(_get_current_value(key))}"
- )
- traffic_lines.append(texts.t(f"TRAFFIC_{traffic_key}", default_label))
-
- text = "\n".join(
- part
- for part in (
- texts.t("ADMIN_PRICING_TITLE", "💰 Управление ценами"),
- "",
- texts.t("ADMIN_PRICING_SECTION_TRAFFIC", "📦 Пакеты трафика"),
- "\n".join(traffic_lines),
- "",
- texts.t(
- "ADMIN_PRICING_HINT_TRAFFIC",
- "Выберите пакет, чтобы обновить его цену.",
- ),
- )
- if part
- )
-
- await callback.message.edit_text(
- text,
- reply_markup=_traffic_keyboard(texts),
- )
- await callback.answer()
-
-
-@admin_required
-@error_handler
-async def show_pricing_extras(
- callback: types.CallbackQuery,
- db_user: User,
- db: AsyncSession,
-):
- texts = get_texts(db_user.language)
- device_price = _format_price(_get_current_value("PRICE_PER_DEVICE"))
-
- text = "\n".join(
- part
- for part in (
- texts.t("ADMIN_PRICING_TITLE", "💰 Управление ценами"),
- "",
- texts.t("ADMIN_PRICING_SECTION_EXTRAS", "🔧 Дополнительные опции"),
- texts.t("ADMIN_PRICING_DEVICE_LABEL", "📱 Дополнительное устройство — {price}").format(
- price=device_price
- ),
- "",
- texts.t(
- "ADMIN_PRICING_HINT_EXTRAS",
- "Обновите стоимость дополнительных опций.",
- ),
- )
- if part
- )
-
- await callback.message.edit_text(
- text,
- reply_markup=_extras_keyboard(texts),
- )
- await callback.answer()
-
-
-@admin_required
-@error_handler
-async def start_edit_period_price(
- callback: types.CallbackQuery,
- state: FSMContext,
- db_user: User,
- db: AsyncSession,
-):
- texts = get_texts(db_user.language)
- try:
- days = int(callback.data.split("_")[-1])
- except ValueError:
- await callback.answer("❌", show_alert=True)
- return
-
- setting_key = _find_period_setting(days)
- if not setting_key:
- await callback.answer("❌", show_alert=True)
- return
-
- label = texts.t("ADMIN_PRICING_PERIOD_LABEL", "Тариф на {label}").format(
- label=_get_period_label(texts, days)
- )
- current_price = _format_price(_get_current_value(setting_key))
-
- await _prompt_price_input(
- callback,
- state,
- texts,
- label,
- current_price,
- setting_key,
- "admin_pricing_periods",
- )
-
-
-@admin_required
-@error_handler
-async def start_edit_traffic_price(
- callback: types.CallbackQuery,
- state: FSMContext,
- db_user: User,
- db: AsyncSession,
-):
- texts = get_texts(db_user.language)
- identifier = callback.data.split("_")[-1]
- setting = _find_traffic_setting(identifier)
-
- if not setting:
- await callback.answer("❌", show_alert=True)
- return
-
- amount, setting_key = setting
- label = texts.t("ADMIN_PRICING_TRAFFIC_LABEL", "Пакет {label}").format(
- label=_get_traffic_label(texts, amount)
- )
- current_price = _format_price(_get_current_value(setting_key))
-
- await _prompt_price_input(
- callback,
- state,
- texts,
- label,
- current_price,
- setting_key,
- "admin_pricing_traffic",
- )
-
-
-@admin_required
-@error_handler
-async def start_edit_extra_price(
- callback: types.CallbackQuery,
- state: FSMContext,
- db_user: User,
- db: AsyncSession,
-):
- texts = get_texts(db_user.language)
- slug = callback.data.split("_")[-1]
- setting_key = _find_extra_setting(slug)
-
- if not setting_key:
- await callback.answer("❌", show_alert=True)
- return
-
- if slug == "device":
- label = texts.t("ADMIN_PRICING_DEVICE_EDIT_LABEL", "Цена за дополнительное устройство")
- else:
- label = slug
-
- current_price = _format_price(_get_current_value(setting_key))
-
- await _prompt_price_input(
- callback,
- state,
- texts,
- label,
- current_price,
- setting_key,
- "admin_pricing_extras",
- )
-
-
-def _parse_price(text: str) -> Decimal:
- cleaned = (text or "").strip().replace(" ", "")
- if not cleaned:
- raise InvalidOperation
- normalized = cleaned.replace(",", ".")
- value = Decimal(normalized)
- return value.quantize(Decimal("0.01"))
-
-
-@admin_required
-@error_handler
-async def process_pricing_value(
- message: types.Message,
- state: FSMContext,
- db_user: User,
- db: AsyncSession,
-):
- data = await state.get_data()
- setting_key = data.get("pricing_key")
- label = data.get("pricing_label")
- return_callback = data.get("return_callback", "admin_pricing")
- texts = get_texts(db_user.language)
-
- if not setting_key or not label:
- await message.answer(texts.t("ADMIN_PRICING_STATE_EXPIRED", "⚠️ Истекло состояние ввода. Попробуйте снова."))
- await state.clear()
- return
-
- try:
- price_rubles = _parse_price(message.text)
- except (InvalidOperation, ValueError):
- await message.answer(
- texts.t(
- "ADMIN_PRICING_INVALID_FORMAT",
- "❌ Неверный формат. Используйте числа, например 990 или 349.99.",
- )
- )
- return
-
- if price_rubles < 0:
- await message.answer(
- texts.t(
- "ADMIN_PRICING_NEGATIVE_VALUE",
- "❌ Цена не может быть отрицательной.",
- )
- )
- return
-
- if price_rubles > MAX_PRICE_RUBLES:
- await message.answer(
- texts.t(
- "ADMIN_PRICING_TOO_HIGH",
- "❌ Слишком высокая цена. Укажите значение до {limit}.",
- ).format(limit=_format_price(int(MAX_PRICE_RUBLES * 100))),
- )
- return
-
- price_kopeks = int((price_rubles * 100).quantize(Decimal("1"), rounding=ROUND_HALF_UP))
-
- await bot_configuration_service.set_value(db, setting_key, price_kopeks)
- await state.clear()
-
- logger.info(
- "Админ %s обновил %s: %s коп.",
- db_user.telegram_id,
- setting_key,
- price_kopeks,
- )
-
- await message.answer(
- texts.t(
- "ADMIN_PRICING_SUCCESS",
- "✅ Цена для {label} обновлена: {value}",
- ).format(label=label, value=_format_price(price_kopeks)),
- reply_markup=InlineKeyboardMarkup(inline_keyboard=[
- [InlineKeyboardButton(text=texts.BACK, callback_data=return_callback)],
- [
- InlineKeyboardButton(
- text=texts.t("ADMIN_PRICING_RETURN", "🏠 К управлению ценами"),
- callback_data="admin_pricing",
- )
- ],
- ]),
- )
-
-
-def register_handlers(dp: Dispatcher) -> None:
- dp.callback_query.register(show_pricing_dashboard, F.data == "admin_pricing")
- dp.callback_query.register(show_pricing_periods, F.data == "admin_pricing_periods")
- dp.callback_query.register(show_pricing_traffic, F.data == "admin_pricing_traffic")
- dp.callback_query.register(show_pricing_extras, F.data == "admin_pricing_extras")
- dp.callback_query.register(
- start_edit_period_price,
- F.data.startswith("admin_pricing_edit_period_"),
- )
- dp.callback_query.register(
- start_edit_traffic_price,
- F.data.startswith("admin_pricing_edit_traffic_"),
- )
- dp.callback_query.register(
- start_edit_extra_price,
- F.data.startswith("admin_pricing_edit_extra_"),
- )
- dp.message.register(
- process_pricing_value,
- AdminPricingStates.waiting_for_price,
- )
-
diff --git a/app/handlers/admin/subscriptions.py b/app/handlers/admin/subscriptions.py
index 11771bdd..0c341a04 100644
--- a/app/handlers/admin/subscriptions.py
+++ b/app/handlers/admin/subscriptions.py
@@ -4,6 +4,7 @@ from aiogram.fsm.context import FSMContext
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
+from app.config import settings
from app.states import AdminStates
from app.database.models import User
from app.keyboards.admin import get_admin_subscriptions_keyboard
@@ -61,7 +62,6 @@ async def show_subscriptions_menu(
db: AsyncSession
):
stats = await get_subscriptions_statistics(db)
- texts = get_texts(db_user.language)
text = f"""
📱 Управление подписками
@@ -77,20 +77,20 @@ async def show_subscriptions_menu(
- За неделю: {stats['purchased_week']}
- За месяц: {stats['purchased_month']}
-ℹ️ {texts.t("ADMIN_SUBSCRIPTIONS_MAIN_HINT", "Управление серверами и ценами теперь доступно на главной странице админ-панели.")}
-
Выберите действие:
"""
-
+
keyboard = [
[
types.InlineKeyboardButton(text="📋 Список подписок", callback_data="admin_subs_list"),
types.InlineKeyboardButton(text="⏰ Истекающие", callback_data="admin_subs_expiring")
],
[
- types.InlineKeyboardButton(text="📊 Статистика", callback_data="admin_subs_stats")
+ types.InlineKeyboardButton(text="📊 Статистика", callback_data="admin_subs_stats"),
+ types.InlineKeyboardButton(text="💰 Настройки цен", callback_data="admin_subs_pricing")
],
[
+ types.InlineKeyboardButton(text="🌐 Управление серверами", callback_data="admin_servers"),
types.InlineKeyboardButton(text="🌍 География", callback_data="admin_subs_countries")
],
[
@@ -282,22 +282,45 @@ async def show_pricing_settings(
db_user: User,
db: AsyncSession
):
- texts = get_texts(db_user.language)
+ text = f"""
+⚙️ Настройки цен
+Периоды подписки:
+- 14 дней: {settings.format_price(settings.PRICE_14_DAYS)}
+- 30 дней: {settings.format_price(settings.PRICE_30_DAYS)}
+- 60 дней: {settings.format_price(settings.PRICE_60_DAYS)}
+- 90 дней: {settings.format_price(settings.PRICE_90_DAYS)}
+- 180 дней: {settings.format_price(settings.PRICE_180_DAYS)}
+- 360 дней: {settings.format_price(settings.PRICE_360_DAYS)}
+
+Трафик-пакеты:
+- 5 ГБ: {settings.format_price(settings.PRICE_TRAFFIC_5GB)}
+- 10 ГБ: {settings.format_price(settings.PRICE_TRAFFIC_10GB)}
+- 25 ГБ: {settings.format_price(settings.PRICE_TRAFFIC_25GB)}
+- 50 ГБ: {settings.format_price(settings.PRICE_TRAFFIC_50GB)}
+- 100 ГБ: {settings.format_price(settings.PRICE_TRAFFIC_100GB)}
+- 250 ГБ: {settings.format_price(settings.PRICE_TRAFFIC_250GB)}
+
+Дополнительно:
+- За устройство: {settings.format_price(settings.PRICE_PER_DEVICE)}
+"""
+
+ keyboard = [
+ # [
+ # types.InlineKeyboardButton(text="📅 Периоды", callback_data="admin_edit_period_prices"),
+ # types.InlineKeyboardButton(text="📈 Трафик", callback_data="admin_edit_traffic_prices")
+ # ],
+ # [
+ # types.InlineKeyboardButton(text="📱 Устройства", callback_data="admin_edit_device_price")
+ # ],
+ [
+ types.InlineKeyboardButton(text="⬅️ Назад", callback_data="admin_subscriptions")
+ ]
+ ]
+
await callback.message.edit_text(
- texts.t(
- "ADMIN_PRICING_RELOCATED",
- "ℹ️ Раздел управления ценами переехал. Откройте главный экран админ-панели и выберите пункт «Цены и тарифы».",
- ),
- reply_markup=types.InlineKeyboardMarkup(inline_keyboard=[
- [
- types.InlineKeyboardButton(
- text=texts.t("ADMIN_PRICING_OPEN", "💰 Перейти к управлению ценами"),
- callback_data="admin_pricing"
- )
- ],
- [types.InlineKeyboardButton(text="⬅️ Назад", callback_data="admin_subscriptions")]
- ])
+ text,
+ reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard)
)
await callback.answer()
diff --git a/app/keyboards/admin.py b/app/keyboards/admin.py
index 54ed11b8..ee7729af 100644
--- a/app/keyboards/admin.py
+++ b/app/keyboards/admin.py
@@ -17,8 +17,6 @@ def get_admin_main_keyboard(language: str = "ru") -> InlineKeyboardMarkup:
[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")],
- [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_SETTINGS", "⚙️ Настройки"), callback_data="admin_submenu_settings")],
[InlineKeyboardButton(text=_t(texts, "ADMIN_MAIN_SYSTEM", "🛠️ Система"), callback_data="admin_submenu_system")],
[InlineKeyboardButton(text=texts.BACK, callback_data="back_to_menu")]
diff --git a/app/localization/texts.py b/app/localization/texts.py
index a6157de8..ccd8eff1 100644
--- a/app/localization/texts.py
+++ b/app/localization/texts.py
@@ -42,8 +42,6 @@ def _build_dynamic_values(language: str) -> Dict[str, Any]:
"TRAFFIC_50GB": f"📊 50 ГБ - {settings.format_price(settings.PRICE_TRAFFIC_50GB)}",
"TRAFFIC_100GB": f"📊 100 ГБ - {settings.format_price(settings.PRICE_TRAFFIC_100GB)}",
"TRAFFIC_250GB": f"📊 250 ГБ - {settings.format_price(settings.PRICE_TRAFFIC_250GB)}",
- "TRAFFIC_500GB": f"📊 500 ГБ - {settings.format_price(settings.PRICE_TRAFFIC_500GB)}",
- "TRAFFIC_1000GB": f"📊 1000 ГБ - {settings.format_price(settings.PRICE_TRAFFIC_1000GB)}",
"TRAFFIC_UNLIMITED": f"📊 Безлимит - {settings.format_price(settings.PRICE_TRAFFIC_UNLIMITED)}",
"SUPPORT_INFO": (
"\n🛟 Поддержка\n\n"
@@ -69,8 +67,6 @@ def _build_dynamic_values(language: str) -> Dict[str, Any]:
"TRAFFIC_50GB": f"📊 50 GB - {settings.format_price(settings.PRICE_TRAFFIC_50GB)}",
"TRAFFIC_100GB": f"📊 100 GB - {settings.format_price(settings.PRICE_TRAFFIC_100GB)}",
"TRAFFIC_250GB": f"📊 250 GB - {settings.format_price(settings.PRICE_TRAFFIC_250GB)}",
- "TRAFFIC_500GB": f"📊 500 GB - {settings.format_price(settings.PRICE_TRAFFIC_500GB)}",
- "TRAFFIC_1000GB": f"📊 1000 GB - {settings.format_price(settings.PRICE_TRAFFIC_1000GB)}",
"TRAFFIC_UNLIMITED": f"📊 Unlimited - {settings.format_price(settings.PRICE_TRAFFIC_UNLIMITED)}",
"SUPPORT_INFO": (
"\n🛟 RemnaWave Support\n\n"
diff --git a/app/states.py b/app/states.py
index 20936d4a..61015d48 100644
--- a/app/states.py
+++ b/app/states.py
@@ -30,7 +30,7 @@ class PromoCodeStates(StatesGroup):
waiting_for_referral_code = State()
class AdminStates(StatesGroup):
-
+
waiting_for_user_search = State()
editing_user_balance = State()
extending_subscription = State()
@@ -114,10 +114,6 @@ class AdminStates(StatesGroup):
viewing_user_from_purchases_list = State()
viewing_user_from_campaign_list = State()
-
-class AdminPricingStates(StatesGroup):
- waiting_for_price = State()
-
class SupportStates(StatesGroup):
waiting_for_message = State()
diff --git a/locales/en.json b/locales/en.json
index 494f34a5..d970b566 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -172,37 +172,6 @@
"ADMIN_PROMO_GROUPS_DEFAULT_LABEL": " (default)",
"ADMIN_PROMO_GROUPS_MEMBERS_COUNT": "Members: {count}",
"ADMIN_PROMO_GROUPS_EMPTY": "No promo groups found.",
- "ADMIN_SUBSCRIPTIONS_MAIN_HINT": "Server and pricing management is now available from the admin home screen.",
- "ADMIN_PRICING_TITLE": "💰 Pricing management",
- "ADMIN_PRICING_DESCRIPTION": "Adjust subscription plans, traffic bundles and extra options.",
- "ADMIN_PRICING_SECTION_PERIODS": "📅 Subscription periods",
- "ADMIN_PRICING_SECTION_TRAFFIC": "📦 Traffic bundles",
- "ADMIN_PRICING_SECTION_EXTRAS": "🔧 Extra options",
- "ADMIN_PRICING_PERIODS": "📅 Subscription periods",
- "ADMIN_PRICING_TRAFFIC": "📦 Traffic bundles",
- "ADMIN_PRICING_EXTRAS": "🔧 Extra options",
- "ADMIN_PRICING_EDIT_PERIOD": "✏️ {label}",
- "ADMIN_PRICING_EDIT_TRAFFIC": "✏️ {label}",
- "ADMIN_PRICING_EDIT_DEVICE": "✏️ Device price",
- "ADMIN_PRICING_DAYS_LABEL": "{days} d.",
- "ADMIN_PRICING_UNLIMITED_LABEL": "Unlimited",
- "ADMIN_PRICING_TRAFFIC_GB": "{gb} GB",
- "ADMIN_PRICING_DEVICE_LABEL": "📱 Extra device — {price}",
- "ADMIN_PRICING_HINT_PERIODS": "Choose a period to change its price.",
- "ADMIN_PRICING_HINT_TRAFFIC": "Choose a bundle to update its price.",
- "ADMIN_PRICING_HINT_EXTRAS": "Update the cost of extra options.",
- "ADMIN_PRICING_PERIOD_LABEL": "{label} plan",
- "ADMIN_PRICING_TRAFFIC_LABEL": "{label} bundle",
- "ADMIN_PRICING_DEVICE_EDIT_LABEL": "Extra device price",
- "ADMIN_PRICING_PROMPT": "Enter a new price for {label}.\nCurrent price: {current}.\n\nSend the value in RUB (example: 990 or 349.99).",
- "ADMIN_PRICING_STATE_EXPIRED": "⚠️ Input session expired. Please try again.",
- "ADMIN_PRICING_INVALID_FORMAT": "❌ Invalid format. Use numbers such as 990 or 349.99.",
- "ADMIN_PRICING_NEGATIVE_VALUE": "❌ Price cannot be negative.",
- "ADMIN_PRICING_TOO_HIGH": "❌ Price is too high. Use a value up to {limit}.",
- "ADMIN_PRICING_SUCCESS": "✅ Price for {label} updated: {value}",
- "ADMIN_PRICING_RETURN": "🏠 Back to pricing",
- "ADMIN_PRICING_RELOCATED": "ℹ️ Pricing management moved. Open the admin home screen and choose “Pricing”.",
- "ADMIN_PRICING_OPEN": "💰 Go to pricing management",
"CREATE_TICKET_BUTTON": "🎫 Create ticket",
"MY_TICKETS_BUTTON": "📋 My tickets",
"CONTACT_SUPPORT_BUTTON": "💬 Contact support",
@@ -577,8 +546,6 @@
"ADMIN_MAIN_PROMO_STATS": "💰 Promo codes / Stats",
"ADMIN_MAIN_SUPPORT": "🛟 Support",
"ADMIN_MAIN_MESSAGES": "📨 Messages",
- "ADMIN_MAIN_SERVERS": "🌐 Servers",
- "ADMIN_MAIN_PRICING": "💰 Pricing",
"ADMIN_MAIN_SETTINGS": "⚙️ Settings",
"ADMIN_MAIN_SYSTEM": "🛠️ System",
"ADMIN_USERS_SUBMENU_TITLE": "👥 **User and subscription management**\n\n",
diff --git a/locales/ru.json b/locales/ru.json
index 7b5aa4f5..13c5c434 100644
--- a/locales/ru.json
+++ b/locales/ru.json
@@ -30,37 +30,6 @@
"ADMIN_PROMO_GROUPS_DEFAULT_LABEL": " (базовая)",
"ADMIN_PROMO_GROUPS_MEMBERS_COUNT": "Участников: {count}",
"ADMIN_PROMO_GROUPS_EMPTY": "Промогруппы не найдены.",
- "ADMIN_SUBSCRIPTIONS_MAIN_HINT": "Управление серверами и ценами теперь доступно на главной странице админ-панели.",
- "ADMIN_PRICING_TITLE": "💰 Управление ценами",
- "ADMIN_PRICING_DESCRIPTION": "Настройте стоимость подписок, пакетов трафика и дополнительных опций.",
- "ADMIN_PRICING_SECTION_PERIODS": "📅 Периоды подписки",
- "ADMIN_PRICING_SECTION_TRAFFIC": "📦 Пакеты трафика",
- "ADMIN_PRICING_SECTION_EXTRAS": "🔧 Дополнительные опции",
- "ADMIN_PRICING_PERIODS": "📅 Периоды подписки",
- "ADMIN_PRICING_TRAFFIC": "📦 Трафик-пакеты",
- "ADMIN_PRICING_EXTRAS": "🔧 Доп. опции",
- "ADMIN_PRICING_EDIT_PERIOD": "✏️ {label}",
- "ADMIN_PRICING_EDIT_TRAFFIC": "✏️ {label}",
- "ADMIN_PRICING_EDIT_DEVICE": "✏️ Цена за устройство",
- "ADMIN_PRICING_DAYS_LABEL": "{days} дн.",
- "ADMIN_PRICING_UNLIMITED_LABEL": "Безлимит",
- "ADMIN_PRICING_TRAFFIC_GB": "{gb} ГБ",
- "ADMIN_PRICING_DEVICE_LABEL": "📱 Дополнительное устройство — {price}",
- "ADMIN_PRICING_HINT_PERIODS": "Выберите период, чтобы изменить его стоимость.",
- "ADMIN_PRICING_HINT_TRAFFIC": "Выберите пакет, чтобы обновить его цену.",
- "ADMIN_PRICING_HINT_EXTRAS": "Обновите стоимость дополнительных опций.",
- "ADMIN_PRICING_PERIOD_LABEL": "Тариф на {label}",
- "ADMIN_PRICING_TRAFFIC_LABEL": "Пакет {label}",
- "ADMIN_PRICING_DEVICE_EDIT_LABEL": "Цена за дополнительное устройство",
- "ADMIN_PRICING_PROMPT": "Введите новую цену для {label}.\nТекущая цена: {current}.\n\nПришлите значение в рублях (пример: 990 или 349.99).",
- "ADMIN_PRICING_STATE_EXPIRED": "⚠️ Истекло состояние ввода. Попробуйте снова.",
- "ADMIN_PRICING_INVALID_FORMAT": "❌ Неверный формат. Используйте числа, например 990 или 349.99.",
- "ADMIN_PRICING_NEGATIVE_VALUE": "❌ Цена не может быть отрицательной.",
- "ADMIN_PRICING_TOO_HIGH": "❌ Слишком высокая цена. Укажите значение до {limit}.",
- "ADMIN_PRICING_SUCCESS": "✅ Цена для {label} обновлена: {value}",
- "ADMIN_PRICING_RETURN": "🏠 К управлению ценами",
- "ADMIN_PRICING_RELOCATED": "ℹ️ Раздел управления ценами переехал. Откройте главный экран админ-панели и выберите пункт «Цены и тарифы».",
- "ADMIN_PRICING_OPEN": "💰 Перейти к управлению ценами",
"CREATE_TICKET_BUTTON": "🎫 Создать тикет",
"MY_TICKETS_BUTTON": "📋 Мои тикеты",
"CONTACT_SUPPORT_BUTTON": "💬 Связаться с поддержкой",
@@ -577,8 +546,6 @@
"ADMIN_MAIN_PROMO_STATS": "💰 Промокоды/Статистика",
"ADMIN_MAIN_SUPPORT": "🛟 Поддержка",
"ADMIN_MAIN_MESSAGES": "📨 Сообщения",
- "ADMIN_MAIN_SERVERS": "🌐 Серверы",
- "ADMIN_MAIN_PRICING": "💰 Цены и тарифы",
"ADMIN_MAIN_SETTINGS": "⚙️ Настройки",
"ADMIN_MAIN_SYSTEM": "🛠️ Система",
"ADMIN_USERS_SUBMENU_TITLE": "👥 **Управление пользователями и подписками**\n\n",