mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-20 11:50:27 +00:00
Update tariffs.py
This commit is contained in:
@@ -191,6 +191,9 @@ def get_tariff_view_keyboard(
|
||||
InlineKeyboardButton(text="📱💰 Цена за устройство", callback_data=f"admin_tariff_edit_device_price:{tariff.id}"),
|
||||
InlineKeyboardButton(text="⏰ Дни триала", callback_data=f"admin_tariff_edit_trial_days:{tariff.id}"),
|
||||
])
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text="📈 Докупка трафика", callback_data=f"admin_tariff_edit_traffic_topup:{tariff.id}"),
|
||||
])
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text="🌐 Серверы", callback_data=f"admin_tariff_edit_squads:{tariff.id}"),
|
||||
InlineKeyboardButton(text="👥 Промогруппы", callback_data=f"admin_tariff_edit_promo:{tariff.id}"),
|
||||
@@ -229,6 +232,23 @@ def get_tariff_view_keyboard(
|
||||
return InlineKeyboardMarkup(inline_keyboard=buttons)
|
||||
|
||||
|
||||
def _format_traffic_topup_packages(tariff: Tariff) -> str:
|
||||
"""Форматирует пакеты докупки трафика для отображения."""
|
||||
if not getattr(tariff, 'traffic_topup_enabled', False):
|
||||
return "❌ Отключено"
|
||||
|
||||
packages = tariff.get_traffic_topup_packages() if hasattr(tariff, 'get_traffic_topup_packages') else {}
|
||||
if not packages:
|
||||
return "✅ Включено, но пакеты не настроены"
|
||||
|
||||
lines = ["✅ Включено"]
|
||||
for gb in sorted(packages.keys()):
|
||||
price = packages[gb]
|
||||
lines.append(f" • {gb} ГБ: {_format_price_kopeks(price)}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def format_tariff_info(tariff: Tariff, language: str, subs_count: int = 0) -> str:
|
||||
"""Форматирует информацию о тарифе."""
|
||||
texts = get_texts(language)
|
||||
@@ -264,6 +284,9 @@ def format_tariff_info(tariff: Tariff, language: str, subs_count: int = 0) -> st
|
||||
else:
|
||||
device_price_display = "Недоступно"
|
||||
|
||||
# Форматируем докупку трафика
|
||||
traffic_topup_display = _format_traffic_topup_packages(tariff)
|
||||
|
||||
return f"""📦 <b>Тариф: {tariff.name}</b>
|
||||
|
||||
{status}
|
||||
@@ -277,6 +300,9 @@ def format_tariff_info(tariff: Tariff, language: str, subs_count: int = 0) -> st
|
||||
• Триал: {trial_status}
|
||||
• Дней триала: {trial_days_display}
|
||||
|
||||
<b>Докупка трафика:</b>
|
||||
{traffic_topup_display}
|
||||
|
||||
<b>Цены:</b>
|
||||
{prices_display}
|
||||
|
||||
@@ -1310,6 +1336,285 @@ async def process_edit_tariff_trial_days(
|
||||
)
|
||||
|
||||
|
||||
# ============ РЕДАКТИРОВАНИЕ ДОКУПКИ ТРАФИКА ============
|
||||
|
||||
def _parse_traffic_topup_packages(text: str) -> Dict[int, int]:
|
||||
"""
|
||||
Парсит строку с пакетами докупки трафика.
|
||||
Формат: "5:5000, 10:9000, 20:15000" (ГБ:цена_в_копейках)
|
||||
"""
|
||||
packages = {}
|
||||
text = text.replace(";", ",").replace("=", ":")
|
||||
|
||||
for part in text.split(","):
|
||||
part = part.strip()
|
||||
if not part:
|
||||
continue
|
||||
|
||||
if ":" not in part:
|
||||
continue
|
||||
|
||||
gb_str, price_str = part.split(":", 1)
|
||||
try:
|
||||
gb = int(gb_str.strip())
|
||||
price = int(price_str.strip())
|
||||
if gb > 0 and price > 0:
|
||||
packages[gb] = price
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
return packages
|
||||
|
||||
|
||||
def _format_traffic_topup_packages_for_edit(packages: Dict[int, int]) -> str:
|
||||
"""Форматирует пакеты докупки для редактирования."""
|
||||
if not packages:
|
||||
return "5:5000, 10:9000, 20:15000"
|
||||
|
||||
parts = []
|
||||
for gb in sorted(packages.keys()):
|
||||
parts.append(f"{gb}:{packages[gb]}")
|
||||
|
||||
return ", ".join(parts)
|
||||
|
||||
|
||||
@admin_required
|
||||
@error_handler
|
||||
async def start_edit_tariff_traffic_topup(
|
||||
callback: types.CallbackQuery,
|
||||
db_user: User,
|
||||
db: AsyncSession,
|
||||
state: FSMContext,
|
||||
):
|
||||
"""Показывает меню настройки докупки трафика."""
|
||||
texts = get_texts(db_user.language)
|
||||
tariff_id = int(callback.data.split(":")[1])
|
||||
tariff = await get_tariff_by_id(db, tariff_id)
|
||||
|
||||
if not tariff:
|
||||
await callback.answer("Тариф не найден", show_alert=True)
|
||||
return
|
||||
|
||||
# Проверяем, безлимитный ли тариф
|
||||
if tariff.is_unlimited_traffic:
|
||||
await callback.answer("Докупка недоступна для безлимитного тарифа", show_alert=True)
|
||||
return
|
||||
|
||||
is_enabled = getattr(tariff, 'traffic_topup_enabled', False)
|
||||
packages = tariff.get_traffic_topup_packages() if hasattr(tariff, 'get_traffic_topup_packages') else {}
|
||||
|
||||
# Форматируем текущие настройки
|
||||
if is_enabled:
|
||||
status = "✅ Включено"
|
||||
if packages:
|
||||
packages_display = "\n".join(f" • {gb} ГБ: {_format_price_kopeks(price)}" for gb, price in sorted(packages.items()))
|
||||
else:
|
||||
packages_display = " Пакеты не настроены"
|
||||
else:
|
||||
status = "❌ Отключено"
|
||||
packages_display = " -"
|
||||
|
||||
buttons = []
|
||||
|
||||
# Переключение вкл/выкл
|
||||
if is_enabled:
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text="❌ Отключить", callback_data=f"admin_tariff_toggle_traffic_topup:{tariff_id}")
|
||||
])
|
||||
else:
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text="✅ Включить", callback_data=f"admin_tariff_toggle_traffic_topup:{tariff_id}")
|
||||
])
|
||||
|
||||
# Редактирование пакетов (только если включено)
|
||||
if is_enabled:
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text="📦 Настроить пакеты", callback_data=f"admin_tariff_edit_topup_packages:{tariff_id}")
|
||||
])
|
||||
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text=texts.BACK, callback_data=f"admin_tariff_view:{tariff_id}")
|
||||
])
|
||||
|
||||
await callback.message.edit_text(
|
||||
f"📈 <b>Докупка трафика для «{tariff.name}»</b>\n\n"
|
||||
f"Статус: {status}\n\n"
|
||||
f"<b>Пакеты:</b>\n{packages_display}\n\n"
|
||||
"Пользователи смогут докупать трафик по заданным ценам.",
|
||||
reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@admin_required
|
||||
@error_handler
|
||||
async def toggle_tariff_traffic_topup(
|
||||
callback: types.CallbackQuery,
|
||||
db_user: User,
|
||||
db: AsyncSession,
|
||||
):
|
||||
"""Переключает включение/выключение докупки трафика."""
|
||||
tariff_id = int(callback.data.split(":")[1])
|
||||
tariff = await get_tariff_by_id(db, tariff_id)
|
||||
|
||||
if not tariff:
|
||||
await callback.answer("Тариф не найден", show_alert=True)
|
||||
return
|
||||
|
||||
is_enabled = getattr(tariff, 'traffic_topup_enabled', False)
|
||||
new_value = not is_enabled
|
||||
|
||||
tariff = await update_tariff(db, tariff, traffic_topup_enabled=new_value)
|
||||
|
||||
status_text = "включена" if new_value else "отключена"
|
||||
await callback.answer(f"Докупка трафика {status_text}")
|
||||
|
||||
# Перерисовываем меню
|
||||
texts = get_texts(db_user.language)
|
||||
packages = tariff.get_traffic_topup_packages() if hasattr(tariff, 'get_traffic_topup_packages') else {}
|
||||
|
||||
if new_value:
|
||||
status = "✅ Включено"
|
||||
if packages:
|
||||
packages_display = "\n".join(f" • {gb} ГБ: {_format_price_kopeks(price)}" for gb, price in sorted(packages.items()))
|
||||
else:
|
||||
packages_display = " Пакеты не настроены"
|
||||
else:
|
||||
status = "❌ Отключено"
|
||||
packages_display = " -"
|
||||
|
||||
buttons = []
|
||||
|
||||
if new_value:
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text="❌ Отключить", callback_data=f"admin_tariff_toggle_traffic_topup:{tariff_id}")
|
||||
])
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text="📦 Настроить пакеты", callback_data=f"admin_tariff_edit_topup_packages:{tariff_id}")
|
||||
])
|
||||
else:
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text="✅ Включить", callback_data=f"admin_tariff_toggle_traffic_topup:{tariff_id}")
|
||||
])
|
||||
|
||||
buttons.append([
|
||||
InlineKeyboardButton(text=texts.BACK, callback_data=f"admin_tariff_view:{tariff_id}")
|
||||
])
|
||||
|
||||
try:
|
||||
await callback.message.edit_text(
|
||||
f"📈 <b>Докупка трафика для «{tariff.name}»</b>\n\n"
|
||||
f"Статус: {status}\n\n"
|
||||
f"<b>Пакеты:</b>\n{packages_display}\n\n"
|
||||
"Пользователи смогут докупать трафик по заданным ценам.",
|
||||
reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
except TelegramBadRequest:
|
||||
pass
|
||||
|
||||
|
||||
@admin_required
|
||||
@error_handler
|
||||
async def start_edit_traffic_topup_packages(
|
||||
callback: types.CallbackQuery,
|
||||
db_user: User,
|
||||
db: AsyncSession,
|
||||
state: FSMContext,
|
||||
):
|
||||
"""Начинает редактирование пакетов докупки трафика."""
|
||||
texts = get_texts(db_user.language)
|
||||
tariff_id = int(callback.data.split(":")[1])
|
||||
tariff = await get_tariff_by_id(db, tariff_id)
|
||||
|
||||
if not tariff:
|
||||
await callback.answer("Тариф не найден", show_alert=True)
|
||||
return
|
||||
|
||||
await state.set_state(AdminStates.editing_tariff_traffic_topup_packages)
|
||||
await state.update_data(tariff_id=tariff_id, language=db_user.language)
|
||||
|
||||
packages = tariff.get_traffic_topup_packages() if hasattr(tariff, 'get_traffic_topup_packages') else {}
|
||||
current_packages = _format_traffic_topup_packages_for_edit(packages)
|
||||
|
||||
if packages:
|
||||
packages_display = "\n".join(f" • {gb} ГБ: {_format_price_kopeks(price)}" for gb, price in sorted(packages.items()))
|
||||
else:
|
||||
packages_display = " Не настроены"
|
||||
|
||||
await callback.message.edit_text(
|
||||
f"📦 <b>Настройка пакетов докупки трафика</b>\n\n"
|
||||
f"Тариф: <b>{tariff.name}</b>\n\n"
|
||||
f"<b>Текущие пакеты:</b>\n{packages_display}\n\n"
|
||||
"Введите пакеты в формате:\n"
|
||||
f"<code>{current_packages}</code>\n\n"
|
||||
"(ГБ:цена_в_копейках, через запятую)\n"
|
||||
"Например: <code>5:5000, 10:9000</code> = 5ГБ за 50₽, 10ГБ за 90₽",
|
||||
reply_markup=InlineKeyboardMarkup(inline_keyboard=[
|
||||
[InlineKeyboardButton(text=texts.CANCEL, callback_data=f"admin_tariff_edit_traffic_topup:{tariff_id}")]
|
||||
]),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@admin_required
|
||||
@error_handler
|
||||
async def process_edit_traffic_topup_packages(
|
||||
message: types.Message,
|
||||
db_user: User,
|
||||
db: AsyncSession,
|
||||
state: FSMContext,
|
||||
):
|
||||
"""Обрабатывает новые пакеты докупки трафика."""
|
||||
data = await state.get_data()
|
||||
tariff_id = data.get("tariff_id")
|
||||
|
||||
tariff = await get_tariff_by_id(db, tariff_id)
|
||||
if not tariff:
|
||||
await message.answer("Тариф не найден")
|
||||
await state.clear()
|
||||
return
|
||||
|
||||
packages = _parse_traffic_topup_packages(message.text.strip())
|
||||
|
||||
if not packages:
|
||||
await message.answer(
|
||||
"Не удалось распознать пакеты.\n\n"
|
||||
"Формат: <code>ГБ:цена_в_копейках</code>\n"
|
||||
"Пример: <code>5:5000, 10:9000, 20:15000</code>",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
return
|
||||
|
||||
# Преобразуем в формат для JSON (строковые ключи)
|
||||
packages_json = {str(gb): price for gb, price in packages.items()}
|
||||
|
||||
tariff = await update_tariff(db, tariff, traffic_topup_packages=packages_json)
|
||||
await state.clear()
|
||||
|
||||
# Показываем обновленное меню
|
||||
texts = get_texts(db_user.language)
|
||||
packages_display = "\n".join(f" • {gb} ГБ: {_format_price_kopeks(price)}" for gb, price in sorted(packages.items()))
|
||||
|
||||
buttons = [
|
||||
[InlineKeyboardButton(text="❌ Отключить", callback_data=f"admin_tariff_toggle_traffic_topup:{tariff_id}")],
|
||||
[InlineKeyboardButton(text="📦 Настроить пакеты", callback_data=f"admin_tariff_edit_topup_packages:{tariff_id}")],
|
||||
[InlineKeyboardButton(text=texts.BACK, callback_data=f"admin_tariff_view:{tariff_id}")]
|
||||
]
|
||||
|
||||
await message.answer(
|
||||
f"✅ <b>Пакеты обновлены!</b>\n\n"
|
||||
f"📈 <b>Докупка трафика для «{tariff.name}»</b>\n\n"
|
||||
f"Статус: ✅ Включено\n\n"
|
||||
f"<b>Пакеты:</b>\n{packages_display}\n\n"
|
||||
"Пользователи смогут докупать трафик по заданным ценам.",
|
||||
reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons),
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
|
||||
# ============ УДАЛЕНИЕ ТАРИФА ============
|
||||
|
||||
@admin_required
|
||||
@@ -1855,6 +2160,12 @@ def register_handlers(dp: Dispatcher):
|
||||
dp.callback_query.register(start_edit_tariff_trial_days, F.data.startswith("admin_tariff_edit_trial_days:"))
|
||||
dp.message.register(process_edit_tariff_trial_days, AdminStates.editing_tariff_trial_days)
|
||||
|
||||
# Редактирование докупки трафика
|
||||
dp.callback_query.register(start_edit_tariff_traffic_topup, F.data.startswith("admin_tariff_edit_traffic_topup:"))
|
||||
dp.callback_query.register(toggle_tariff_traffic_topup, F.data.startswith("admin_tariff_toggle_traffic_topup:"))
|
||||
dp.callback_query.register(start_edit_traffic_topup_packages, F.data.startswith("admin_tariff_edit_topup_packages:"))
|
||||
dp.message.register(process_edit_traffic_topup_packages, AdminStates.editing_tariff_traffic_topup_packages)
|
||||
|
||||
# Удаление
|
||||
dp.callback_query.register(confirm_delete_tariff, F.data.startswith("admin_tariff_delete:"))
|
||||
dp.callback_query.register(delete_tariff_confirmed, F.data.startswith("admin_tariff_delete_confirm:"))
|
||||
|
||||
Reference in New Issue
Block a user