diff --git a/bot/handlers/admin/ads.py b/bot/handlers/admin/ads.py
index 33bd539..ddd6027 100644
--- a/bot/handlers/admin/ads.py
+++ b/bot/handlers/admin/ads.py
@@ -178,7 +178,7 @@ async def ads_delete_cancel(callback: types.CallbackQuery, settings: Settings, i
camp = await ad_dal.get_campaign_by_id(session, camp_id)
if not camp:
- await callback.answer(_("admin_ads_not_found", default="Кампания не найдена."), show_alert=True)
+ await callback.answer(_("admin_ads_not_found"), show_alert=True)
return
try:
stats = await ad_dal.get_campaign_stats(session, camp_id)
@@ -224,7 +224,7 @@ async def ads_delete_confirm(callback: types.CallbackQuery, settings: Settings,
existed = await ad_dal.delete_campaign(session, camp_id)
if not existed:
- await callback.answer(_("admin_ads_not_found", default="Кампания не найдена."), show_alert=True)
+ await callback.answer(_("admin_ads_not_found"), show_alert=True)
return
await session.commit()
diff --git a/bot/handlers/admin/broadcast.py b/bot/handlers/admin/broadcast.py
index be425c9..221886b 100644
--- a/bot/handlers/admin/broadcast.py
+++ b/bot/handlers/admin/broadcast.py
@@ -122,7 +122,6 @@ async def process_broadcast_message_handler(
await message.answer(
_(
"admin_broadcast_invalid_html",
- default="❌ Некорректный HTML в сообщении. Пожалуйста, отправьте корректный HTML (поддерживаются теги Telegram) или уберите теги.\nОшибка: {error}",
error=str(e),
)
)
@@ -347,15 +346,6 @@ async def confirm_broadcast_callback_handler(
total_failed = failed_count + dynamic_failed
return _(
"broadcast_queue_result",
- default=(
- "🚀 Рассылка поставлена в очередь!\n"
- "📤 В очередь добавлено: {sent_count}\n"
- "❌ Ошибок: {failed_count}\n\n"
- "📊 Статус очередей:\n"
- "👥 Очередь пользователей: {user_queue_size} сообщений\n"
- "📢 Очередь групп: {group_queue_size} сообщений\n\n"
- "ℹ️ Сообщения будут отправлены автоматически с соблюдением лимитов Telegram."
- ),
sent_count=sent_count,
failed_count=total_failed,
user_queue_size=stats["user_queue_size"],
diff --git a/bot/handlers/admin/logs_admin.py b/bot/handlers/admin/logs_admin.py
index f12034b..7fa37cb 100644
--- a/bot/handlers/admin/logs_admin.py
+++ b/bot/handlers/admin/logs_admin.py
@@ -338,8 +338,7 @@ async def export_logs_csv_handler(callback: types.CallbackQuery,
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
await callback.answer(_(
- "admin_logs_csv_export_started",
- default="🔄 Начинаю экспорт логов в CSV..."
+ "admin_logs_csv_export_started"
))
try:
@@ -349,8 +348,7 @@ async def export_logs_csv_handler(callback: types.CallbackQuery,
if not logs_models:
await callback.message.answer(_(
- "admin_logs_csv_no_data",
- default="❌ Нет данных для экспорта"
+ "admin_logs_csv_no_data"
))
return
@@ -360,16 +358,16 @@ async def export_logs_csv_handler(callback: types.CallbackQuery,
# Write header
headers = [
- _("admin_csv_header_log_id", default="Log ID"),
- _("admin_csv_header_timestamp", default="Timestamp"),
- _("admin_csv_header_user_id", default="User ID"),
- _("admin_csv_header_telegram_username", default="Telegram Username"),
- _("admin_csv_header_telegram_first_name", default="Telegram First Name"),
- _("admin_csv_header_event_type", default="Event Type"),
- _("admin_csv_header_content", default="Content"),
- _("admin_csv_header_is_admin_event", default="Is Admin Event"),
- _("admin_csv_header_target_user_id", default="Target User ID"),
- _("admin_csv_header_raw_update_preview", default="Raw Update Preview")
+ _("admin_csv_header_log_id"),
+ _("admin_csv_header_timestamp"),
+ _("admin_csv_header_user_id"),
+ _("admin_csv_header_telegram_username"),
+ _("admin_csv_header_telegram_first_name"),
+ _("admin_csv_header_event_type"),
+ _("admin_csv_header_content"),
+ _("admin_csv_header_is_admin_event"),
+ _("admin_csv_header_target_user_id"),
+ _("admin_csv_header_raw_update_preview")
]
csv_writer.writerow(headers)
@@ -414,7 +412,6 @@ async def export_logs_csv_handler(callback: types.CallbackQuery,
csv_file,
caption=_(
"admin_logs_csv_export_success",
- default="✅ Экспорт логов завершен!\n\n📊 Записей: {count}\n📅 Дата экспорта: {date}",
count=len(logs_models),
date=now.strftime('%Y-%m-%d %H:%M:%S')
)
@@ -424,6 +421,5 @@ async def export_logs_csv_handler(callback: types.CallbackQuery,
logging.error(f"Error exporting logs to CSV: {e}", exc_info=True)
await callback.message.answer(_(
"admin_logs_csv_export_failed",
- default="❌ Ошибка при экспорте логов: {error}",
error=str(e)
))
diff --git a/bot/handlers/admin/payments.py b/bot/handlers/admin/payments.py
index 9f29d8c..f7ba8eb 100644
--- a/bot/handlers/admin/payments.py
+++ b/bot/handlers/admin/payments.py
@@ -71,9 +71,9 @@ def format_payment_text(payment: Payment, i18n: JsonI18n, lang: str, settings: S
if traffic_mode:
traffic_val = payment.subscription_duration_months or 0
traffic_display = str(int(traffic_val)) if float(traffic_val).is_integer() else f"{traffic_val:g}"
- period_line = _("admin_payment_traffic_label", default="🗂 Трафик: {traffic_gb} GB", traffic_gb=traffic_display)
+ period_line = _("admin_payment_traffic_label", traffic_gb=traffic_display)
else:
- period_line = _("admin_payment_months_label", default="📅 Период: {months} мес.", months=payment.subscription_duration_months or 0)
+ period_line = _("admin_payment_months_label", months=payment.subscription_duration_months or 0)
return (
f"{status_emoji} {payment.amount} {payment.currency}\n"
@@ -102,7 +102,7 @@ async def view_payments_handler(callback: types.CallbackQuery, i18n_data: dict,
if not payments and page == 0:
await callback.message.edit_text(
- _("admin_no_payments_found", default="Платежи не найдены."),
+ _("admin_no_payments_found"),
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n),
parse_mode="HTML"
)
@@ -110,7 +110,7 @@ async def view_payments_handler(callback: types.CallbackQuery, i18n_data: dict,
return
# Format payments text
- text_parts = [_("admin_payments_header", default="💰 Все платежи")]
+ text_parts = [_("admin_payments_header")]
text_parts.append(_("admin_payments_pagination_info",
shown=len(payments),
total=total_count,
@@ -140,11 +140,11 @@ async def view_payments_handler(callback: types.CallbackQuery, i18n_data: dict,
# Export and refresh buttons
builder.row(
InlineKeyboardButton(
- text=_("admin_export_payments_csv", default="📊 Экспорт CSV"),
+ text=_("admin_export_payments_csv"),
callback_data="payments_export_csv"
),
InlineKeyboardButton(
- text=_("admin_refresh_payments", default="🔄 Обновить"),
+ text=_("admin_refresh_payments"),
callback_data=f"payments_page:{page}"
)
)
@@ -191,7 +191,7 @@ async def export_payments_csv_handler(callback: types.CallbackQuery, i18n_data:
if not all_payments:
await callback.answer(
- _("admin_no_payments_to_export", default="Нет платежей для экспорта."),
+ _("admin_no_payments_to_export"),
show_alert=True
)
return
@@ -202,18 +202,18 @@ async def export_payments_csv_handler(callback: types.CallbackQuery, i18n_data:
# Write header
writer.writerow([
- _("admin_csv_payment_id", default="ID"),
- _("admin_csv_user_id", default="User ID"),
- _("admin_csv_username", default="Username"),
- _("admin_csv_first_name", default="First Name"),
- _("admin_csv_amount", default="Amount"),
- _("admin_csv_currency", default="Currency"),
- _("admin_csv_provider", default="Provider"),
- _("admin_csv_status", default="Status"),
- _("admin_csv_description", default="Description"),
- _("admin_csv_units", default="Months/GB"),
- _("admin_csv_created_at", default="Created At"),
- _("admin_csv_provider_payment_id", default="Provider Payment ID")
+ _("admin_csv_payment_id"),
+ _("admin_csv_user_id"),
+ _("admin_csv_username"),
+ _("admin_csv_first_name"),
+ _("admin_csv_amount"),
+ _("admin_csv_currency"),
+ _("admin_csv_provider"),
+ _("admin_csv_status"),
+ _("admin_csv_description"),
+ _("admin_csv_units"),
+ _("admin_csv_created_at"),
+ _("admin_csv_provider_payment_id")
])
traffic_mode = getattr(settings, "traffic_sale_mode", False)
@@ -255,13 +255,12 @@ async def export_payments_csv_handler(callback: types.CallbackQuery, i18n_data:
await callback.message.reply_document(
document=file,
- caption=_("admin_payments_export_success",
- default="📊 Payments export completed!\nTotal records: {count}",
+ caption=_("admin_payments_export_success",
count=len(all_payments))
)
await callback.answer(
- _("admin_export_sent", default="File sent!"),
+ _("admin_export_sent"),
show_alert=False
)
diff --git a/bot/handlers/admin/promo/bulk.py b/bot/handlers/admin/promo/bulk.py
index 1b6b302..7013275 100644
--- a/bot/handlers/admin/promo/bulk.py
+++ b/bot/handlers/admin/promo/bulk.py
@@ -34,8 +34,7 @@ async def create_bulk_promo_prompt_handler(callback: types.CallbackQuery,
# Step 1: Ask for quantity
prompt_text = _(
- "admin_bulk_promo_step1_quantity",
- default="🎟 Массовое создание промокодов\n\nШаг 1 из 4: Количество\n\nВведите количество промокодов для создания (1-100):"
+ "admin_bulk_promo_step1_quantity"
)
try:
@@ -77,8 +76,7 @@ async def process_bulk_promo_quantity_handler(message: types.Message,
quantity = int(message.text.strip())
if not (1 <= quantity <= 100):
await message.answer(_(
- "admin_bulk_promo_invalid_quantity",
- default="❌ Количество промокодов должно быть от 1 до 100"
+ "admin_bulk_promo_invalid_quantity"
))
return
@@ -87,7 +85,6 @@ async def process_bulk_promo_quantity_handler(message: types.Message,
# Step 2: Ask for bonus days
prompt_text = _(
"admin_bulk_promo_step2_bonus_days",
- default="🎟 Массовое создание промокодов\n\nШаг 2 из 4: Бонусные дни\n\nКоличество: {quantity}\n\nВведите количество бонусных дней для каждого промокода (1-365):",
quantity=quantity
)
@@ -100,8 +97,7 @@ async def process_bulk_promo_quantity_handler(message: types.Message,
except ValueError:
await message.answer(_(
- "admin_promo_invalid_number",
- default="❌ Введите корректное число"
+ "admin_promo_invalid_number"
))
except Exception as e:
logging.error(f"Error processing bulk promo quantity: {e}")
@@ -125,8 +121,7 @@ async def process_bulk_promo_bonus_days_handler(message: types.Message,
bonus_days = int(message.text.strip())
if not (1 <= bonus_days <= 365):
await message.answer(_(
- "admin_promo_invalid_bonus_days",
- default="❌ Количество бонусных дней должно быть от 1 до 365"
+ "admin_promo_invalid_bonus_days"
))
return
@@ -136,7 +131,6 @@ async def process_bulk_promo_bonus_days_handler(message: types.Message,
data = await state.get_data()
prompt_text = _(
"admin_bulk_promo_step3_max_activations",
- default="🎟 Массовое создание промокодов\n\nШаг 3 из 4: Лимит активаций\n\nКоличество: {quantity}\nБонусные дни: {bonus_days}\n\nВведите максимальное количество активаций для каждого промокода (1-10000):",
quantity=data.get("quantity"),
bonus_days=bonus_days
)
@@ -150,8 +144,7 @@ async def process_bulk_promo_bonus_days_handler(message: types.Message,
except ValueError:
await message.answer(_(
- "admin_promo_invalid_number",
- default="❌ Введите корректное число"
+ "admin_promo_invalid_number"
))
except Exception as e:
logging.error(f"Error processing bulk promo bonus days: {e}")
@@ -175,8 +168,7 @@ async def process_bulk_promo_max_activations_handler(message: types.Message,
max_activations = int(message.text.strip())
if not (1 <= max_activations <= 10000):
await message.answer(_(
- "admin_promo_invalid_max_activations",
- default="❌ Максимальное количество активаций должно быть от 1 до 10000"
+ "admin_promo_invalid_max_activations"
))
return
@@ -186,7 +178,6 @@ async def process_bulk_promo_max_activations_handler(message: types.Message,
data = await state.get_data()
prompt_text = _(
"admin_bulk_promo_step4_validity",
- default="🎟 Массовое создание промокодов\n\nШаг 4 из 4: Срок действия\n\nКоличество: {quantity}\nБонусные дни: {bonus_days}\nМакс. активаций: {max_activations}\n\nВыберите срок действия промокодов:",
quantity=data.get("quantity"),
bonus_days=data.get("bonus_days"),
max_activations=max_activations
@@ -196,19 +187,19 @@ async def process_bulk_promo_max_activations_handler(message: types.Message,
builder = InlineKeyboardBuilder()
builder.row(
InlineKeyboardButton(
- text=_("admin_promo_unlimited_validity", default="🔄 Без ограничений"),
+ text=_("admin_promo_unlimited_validity"),
callback_data="bulk_promo_unlimited_validity"
)
)
builder.row(
InlineKeyboardButton(
- text=_("admin_promo_set_validity_days", default="📅 Указать дни"),
+ text=_("admin_promo_set_validity_days"),
callback_data="bulk_promo_set_validity"
)
)
builder.row(
InlineKeyboardButton(
- text=_("admin_back_to_panel", default="🔙 В админ панель"),
+ text=_("admin_back_to_panel"),
callback_data="admin_action:main"
)
)
@@ -222,8 +213,7 @@ async def process_bulk_promo_max_activations_handler(message: types.Message,
except ValueError:
await message.answer(_(
- "admin_promo_invalid_number",
- default="❌ Введите корректное число"
+ "admin_promo_invalid_number"
))
except Exception as e:
logging.error(f"Error processing bulk promo max activations: {e}")
@@ -257,7 +247,6 @@ async def process_bulk_promo_set_validity(callback: types.CallbackQuery,
data = await state.get_data()
prompt_text = _(
"admin_bulk_promo_enter_validity_days",
- default="🎟 Массовое создание промокодов\n\nШаг 4 из 4: Срок действия\n\nКоличество: {quantity}\nБонусные дни: {bonus_days}\nМакс. активаций: {max_activations}\n\nВведите количество дней действия промокодов (1-365):",
quantity=data.get("quantity"),
bonus_days=data.get("bonus_days"),
max_activations=data.get("max_activations")
@@ -296,8 +285,7 @@ async def process_bulk_promo_validity_days_handler(message: types.Message,
validity_days = int(message.text.strip())
if not (1 <= validity_days <= 365):
await message.answer(_(
- "admin_promo_invalid_validity_days",
- default="❌ Срок действия должен быть от 1 до 365 дней"
+ "admin_promo_invalid_validity_days"
))
return
@@ -306,8 +294,7 @@ async def process_bulk_promo_validity_days_handler(message: types.Message,
except ValueError:
await message.answer(_(
- "admin_promo_invalid_number",
- default="❌ Введите корректное число"
+ "admin_promo_invalid_number"
))
except Exception as e:
logging.error(f"Error processing bulk promo validity days: {e}")
@@ -333,7 +320,6 @@ async def create_bulk_promo_codes_final(callback_or_message,
# Show progress message
progress_text = _(
"admin_bulk_promo_creating",
- default="🔄 Создание {quantity} промокодов...",
quantity=quantity
)
@@ -395,12 +381,10 @@ async def create_bulk_promo_codes_final(callback_or_message,
# Success message
success_lines = [
_(
- "admin_bulk_promo_created_title",
- default="✅ Массовое создание завершено!\n"
+ "admin_bulk_promo_created_title"
),
_(
"admin_bulk_promo_created_stats",
- default="📊 Создано: {created} из {total}",
created=len(created_codes),
total=quantity
)
@@ -409,14 +393,11 @@ async def create_bulk_promo_codes_final(callback_or_message,
if data.get("validity_days"):
validity_text = f"{data['validity_days']} дней"
else:
- validity_text = _("admin_promo_unlimited", default="Без ограничений")
+ validity_text = _("admin_promo_unlimited")
success_lines.append(
_(
"admin_bulk_promo_settings",
- default="🎁 Бонусные дни: {bonus_days}\n"
- "📊 Макс. активаций: {max_activations}\n"
- "⏰ Срок действия: {validity}",
bonus_days=data["bonus_days"],
max_activations=data["max_activations"],
validity=validity_text
@@ -521,7 +502,7 @@ async def create_bulk_promo_codes_final(callback_or_message,
except Exception as e:
logging.error(f"Error creating bulk promo codes: {e}")
- error_text = _("error_occurred_try_again", default="❌ Произошла ошибка. Попробуйте снова.")
+ error_text = _("error_occurred_try_again")
if hasattr(callback_or_message, 'message'): # CallbackQuery
await callback_or_message.message.answer(error_text)
@@ -564,5 +545,5 @@ async def cancel_bulk_promo_creation_state_to_menu(callback: types.CallbackQuery
reply_markup=get_admin_panel_keyboard(i18n, current_lang, settings)
)
- await callback.answer(_("admin_bulk_promo_creation_cancelled", default="Массовое создание промокодов отменено"))
- await state.clear()
\ No newline at end of file
+ await callback.answer(_("admin_bulk_promo_creation_cancelled"))
+ await state.clear()
diff --git a/bot/handlers/admin/promo/create.py b/bot/handlers/admin/promo/create.py
index 6044913..e26cea6 100644
--- a/bot/handlers/admin/promo/create.py
+++ b/bot/handlers/admin/promo/create.py
@@ -30,8 +30,7 @@ async def create_promo_prompt_handler(callback: types.CallbackQuery,
# Step 1: Ask for promo code
prompt_text = _(
- "admin_promo_step1_code",
- default="🎟 Создание промокода\n\nШаг 1 из 4: Код промокода\n\nВведите код промокода (3-30 символов, только буквы и цифры):"
+ "admin_promo_step1_code"
)
try:
@@ -68,8 +67,7 @@ async def process_promo_code_handler(message: types.Message,
code_str = message.text.strip().upper()
if not (3 <= len(code_str) <= 30 and code_str.isalnum()):
await message.answer(_(
- "admin_promo_invalid_code_format",
- default="❌ Код промокода должен содержать 3-30 символов (только буквы и цифры)"
+ "admin_promo_invalid_code_format"
))
return
@@ -77,8 +75,7 @@ async def process_promo_code_handler(message: types.Message,
existing_promo = await promo_code_dal.get_promo_code_by_code(session, code_str)
if existing_promo:
await message.answer(_(
- "admin_promo_code_already_exists",
- default="❌ Промокод с таким кодом уже существует"
+ "admin_promo_code_already_exists"
))
return
@@ -87,7 +84,6 @@ async def process_promo_code_handler(message: types.Message,
# Step 2: Ask for bonus days
prompt_text = _(
"admin_promo_step2_bonus_days",
- default="🎟 Создание промокода\n\nШаг 2 из 4: Бонусные дни\n\nКод: {code}\n\nВведите количество бонусных дней (1-365):",
code=code_str
)
@@ -120,8 +116,7 @@ async def process_promo_bonus_days_handler(message: types.Message,
bonus_days = int(message.text.strip())
if not (1 <= bonus_days <= 365):
await message.answer(_(
- "admin_promo_invalid_bonus_days",
- default="❌ Количество бонусных дней должно быть от 1 до 365"
+ "admin_promo_invalid_bonus_days"
))
return
@@ -131,7 +126,6 @@ async def process_promo_bonus_days_handler(message: types.Message,
data = await state.get_data()
prompt_text = _(
"admin_promo_step3_max_activations",
- default="🎟 Создание промокода\n\nШаг 3 из 4: Лимит активаций\n\nКод: {code}\nБонусные дни: {bonus_days}\n\nВведите максимальное количество активаций (1-10000):",
code=data.get("promo_code"),
bonus_days=bonus_days
)
@@ -145,8 +139,7 @@ async def process_promo_bonus_days_handler(message: types.Message,
except ValueError:
await message.answer(_(
- "admin_promo_invalid_number",
- default="❌ Введите корректное число"
+ "admin_promo_invalid_number"
))
except Exception as e:
logging.error(f"Error processing promo bonus days: {e}")
@@ -170,8 +163,7 @@ async def process_promo_max_activations_handler(message: types.Message,
max_activations = int(message.text.strip())
if not (1 <= max_activations <= 10000):
await message.answer(_(
- "admin_promo_invalid_max_activations",
- default="❌ Максимальное количество активаций должно быть от 1 до 10000"
+ "admin_promo_invalid_max_activations"
))
return
@@ -181,7 +173,6 @@ async def process_promo_max_activations_handler(message: types.Message,
data = await state.get_data()
prompt_text = _(
"admin_promo_step4_validity",
- default="🎟 Создание промокода\n\nШаг 4 из 4: Срок действия\n\nКод: {code}\nБонусные дни: {bonus_days}\nМакс. активаций: {max_activations}\n\nВыберите срок действия промокода:",
code=data.get("promo_code"),
bonus_days=data.get("bonus_days"),
max_activations=max_activations
@@ -191,19 +182,19 @@ async def process_promo_max_activations_handler(message: types.Message,
builder = InlineKeyboardBuilder()
builder.row(
InlineKeyboardButton(
- text=_("admin_promo_unlimited_validity", default="🔄 Без ограничений"),
+ text=_("admin_promo_unlimited_validity"),
callback_data="promo_unlimited_validity"
)
)
builder.row(
InlineKeyboardButton(
- text=_("admin_promo_set_validity_days", default="📅 Указать дни"),
+ text=_("admin_promo_set_validity_days"),
callback_data="promo_set_validity"
)
)
builder.row(
InlineKeyboardButton(
- text=_("admin_back_to_panel", default="🔙 В админ панель"),
+ text=_("admin_back_to_panel"),
callback_data="admin_action:main"
)
)
@@ -217,8 +208,7 @@ async def process_promo_max_activations_handler(message: types.Message,
except ValueError:
await message.answer(_(
- "admin_promo_invalid_number",
- default="❌ Введите корректное число"
+ "admin_promo_invalid_number"
))
except Exception as e:
logging.error(f"Error processing promo max activations: {e}")
@@ -252,7 +242,6 @@ async def process_promo_set_validity(callback: types.CallbackQuery,
data = await state.get_data()
prompt_text = _(
"admin_promo_enter_validity_days",
- default="🎟 Создание промокода\n\nШаг 4 из 4: Срок действия\n\nКод: {code}\nБонусные дни: {bonus_days}\nМакс. активаций: {max_activations}\n\nВведите количество дней действия промокода (1-365):",
code=data.get("promo_code"),
bonus_days=data.get("bonus_days"),
max_activations=data.get("max_activations")
@@ -291,8 +280,7 @@ async def process_promo_validity_days_handler(message: types.Message,
validity_days = int(message.text.strip())
if not (1 <= validity_days <= 365):
await message.answer(_(
- "admin_promo_invalid_validity_days",
- default="❌ Срок действия должен быть от 1 до 365 дней"
+ "admin_promo_invalid_validity_days"
))
return
@@ -301,8 +289,7 @@ async def process_promo_validity_days_handler(message: types.Message,
except ValueError:
await message.answer(_(
- "admin_promo_invalid_number",
- default="❌ Введите корректное число"
+ "admin_promo_invalid_number"
))
except Exception as e:
logging.error(f"Error processing promo validity days: {e}")
@@ -349,14 +336,9 @@ async def create_promo_code_final(callback_or_message,
logging.info(f"Promo code '{data['promo_code']}' created with ID {created_promo.promo_code_id}")
# Success message
- valid_until_str = _("admin_promo_unlimited", default="Без ограничений") if not data.get("validity_days") else f"{data['validity_days']} дней"
+ valid_until_str = _("admin_promo_unlimited") if not data.get("validity_days") else f"{data['validity_days']} дней"
success_text = _(
"admin_promo_created_success",
- default="✅ Промокод успешно создан!\n\n"
- "🎟 Код: {code}\n"
- "🎁 Бонусные дни: {bonus_days}\n"
- "📊 Макс. активаций: {max_activations}\n"
- "⏰ Срок действия: {valid_until_str}",
code=data["promo_code"],
bonus_days=data["bonus_days"],
max_activations=data["max_activations"],
@@ -388,7 +370,7 @@ async def create_promo_code_final(callback_or_message,
except Exception as e:
logging.error(f"Error creating promo code: {e}")
- error_text = _("error_occurred_try_again", default="❌ Произошла ошибка. Попробуйте снова.")
+ error_text = _("error_occurred_try_again")
if hasattr(callback_or_message, 'message'): # CallbackQuery
await callback_or_message.message.answer(error_text)
@@ -432,5 +414,5 @@ async def cancel_promo_creation_state_to_menu(callback: types.CallbackQuery,
reply_markup=get_admin_panel_keyboard(i18n, current_lang, settings)
)
- await callback.answer(_("admin_promo_creation_cancelled", default="Создание промокода отменено"))
- await state.clear()
\ No newline at end of file
+ await callback.answer(_("admin_promo_creation_cancelled"))
+ await state.clear()
diff --git a/bot/handlers/admin/statistics.py b/bot/handlers/admin/statistics.py
index ac58d30..f136b9c 100644
--- a/bot/handlers/admin/statistics.py
+++ b/bot/handlers/admin/statistics.py
@@ -34,30 +34,30 @@ async def show_statistics_handler(callback: types.CallbackQuery,
user_stats = await user_dal.get_enhanced_user_statistics(session)
stats_text_parts.append(
- f"\n👥 {_('admin_enhanced_users_stats_header', default='Пользователи')}"
+ f"\n👥 {_('admin_enhanced_users_stats_header')}"
)
stats_text_parts.append(
- f"📊 {_('admin_user_stats_total_label', default='Всего')}: {user_stats['total_users']}"
+ f"📊 {_('admin_user_stats_total_label')}: {user_stats['total_users']}"
)
# Removed: Active today moved to panel stats
stats_text_parts.append(
- f"💳 {_('admin_user_stats_paid_subs_label', default='С платной подпиской')}: {user_stats['paid_subscriptions']}"
+ f"💳 {_('admin_user_stats_paid_subs_label')}: {user_stats['paid_subscriptions']}"
)
stats_text_parts.append(
- f"🆓 {_('admin_user_stats_trial_label', default='На пробном периоде')}: {user_stats['trial_users']}"
+ f"🆓 {_('admin_user_stats_trial_label')}: {user_stats['trial_users']}"
)
stats_text_parts.append(
- f"😴 {_('admin_user_stats_inactive_label', default='Неактивных')}: {user_stats['inactive_users']}"
+ f"😴 {_('admin_user_stats_inactive_label')}: {user_stats['inactive_users']}"
)
stats_text_parts.append(
- f"🚫 {_('admin_user_stats_banned_label', default='Заблокированных')}: {user_stats['banned_users']}"
+ f"🚫 {_('admin_user_stats_banned_label')}: {user_stats['banned_users']}"
)
stats_text_parts.append(
- f"🎁 {_('admin_user_stats_referral_label', default='Привлечено по реферальной программе')}: {user_stats['referral_users']}"
+ f"🎁 {_('admin_user_stats_referral_label')}: {user_stats['referral_users']}"
)
# Panel Statistics - moved above financial
- stats_text_parts.append(f"\n🖥 {_('admin_panel_stats_header', default='Статистика панели')}")
+ stats_text_parts.append(f"\n🖥 {_('admin_panel_stats_header')}")
try:
async with PanelApiService(settings) as panel_service:
@@ -80,12 +80,12 @@ async def show_statistics_handler(callback: types.CallbackQuery,
total_users = users.get('totalUsers', 0)
online_now = online_stats.get('onlineNow', 0)
- stats_text_parts.append(f"🟢 {_('admin_panel_online_label', default='Онлайн')}: {online_now}")
- stats_text_parts.append(f"📊 {_('admin_panel_active_label', default='Активных')}: {active_users}")
- stats_text_parts.append(f"🔴 {_('admin_panel_disabled_label', default='Отключенных')}: {disabled_users}")
- stats_text_parts.append(f"⏰ {_('admin_panel_expired_label', default='Истекшие')}: {expired_users}")
- stats_text_parts.append(f"⚠️ {_('admin_panel_limited_label', default='Ограниченные')}: {limited_users}")
- stats_text_parts.append(f"👥 {_('admin_panel_total_users_label', default='Всего пользователей')}: {total_users}")
+ stats_text_parts.append(f"🟢 {_('admin_panel_online_label')}: {online_now}")
+ stats_text_parts.append(f"📊 {_('admin_panel_active_label')}: {active_users}")
+ stats_text_parts.append(f"🔴 {_('admin_panel_disabled_label')}: {disabled_users}")
+ stats_text_parts.append(f"⏰ {_('admin_panel_expired_label')}: {expired_users}")
+ stats_text_parts.append(f"⚠️ {_('admin_panel_limited_label')}: {limited_users}")
+ stats_text_parts.append(f"👥 {_('admin_panel_total_users_label')}: {total_users}")
# System resources
memory = system_stats.get('memory', {})
@@ -93,9 +93,9 @@ async def show_statistics_handler(callback: types.CallbackQuery,
memory_total = memory.get('total', 1)
memory_used = memory.get('used', 0)
memory_usage = (memory_used / memory_total) * 100 if memory_total > 0 else 0
- stats_text_parts.append(f"💾 {_('admin_panel_memory_usage_label', default='Использование RAM')}: {memory_usage:.1f}%")
+ stats_text_parts.append(f"💾 {_('admin_panel_memory_usage_label')}: {memory_usage:.1f}%")
else:
- stats_text_parts.append(f"⚠️ {_('admin_panel_system_stats_error', default='Ошибка получения системной статистики')}")
+ stats_text_parts.append(f"⚠️ {_('admin_panel_system_stats_error')}")
# Bandwidth stats
if bandwidth_stats:
@@ -107,13 +107,13 @@ async def show_statistics_handler(callback: types.CallbackQuery,
if week_traffic:
week_total = week_traffic.get('current', '0 B')
- stats_text_parts.append(f"📊 {_('admin_panel_traffic_week_label', default='Трафик за неделю')}: {week_total}")
+ stats_text_parts.append(f"📊 {_('admin_panel_traffic_week_label')}: {week_total}")
if month_traffic:
month_total = month_traffic.get('current', '0 B')
- stats_text_parts.append(f"📊 {_('admin_panel_traffic_month_label', default='Трафик за месяц')}: {month_total}")
+ stats_text_parts.append(f"📊 {_('admin_panel_traffic_month_label')}: {month_total}")
else:
- stats_text_parts.append(f"⚠️ {_('admin_panel_bandwidth_stats_error', default='Ошибка получения статистики трафика')}")
+ stats_text_parts.append(f"⚠️ {_('admin_panel_bandwidth_stats_error')}")
# Nodes stats
if nodes_stats and 'lastSevenDays' in nodes_stats:
@@ -124,35 +124,35 @@ async def show_statistics_handler(callback: types.CallbackQuery,
unique_nodes.add(node_data.get('nodeName', ''))
total_nodes_count = len(unique_nodes)
# Assume all nodes are active since we don't have status info
- stats_text_parts.append(f"🔗 {_('admin_panel_nodes_label', default='Активных нод')}: {total_nodes_count}/{total_nodes_count}")
+ stats_text_parts.append(f"🔗 {_('admin_panel_nodes_label')}: {total_nodes_count}/{total_nodes_count}")
else:
# Use nodes total from system stats as fallback
nodes_info = system_stats.get('nodes', {}) if system_stats else {}
total_online = nodes_info.get('totalOnline', 0)
- stats_text_parts.append(f"🔗 {_('admin_panel_nodes_label', default='Активных нод')}: {total_online}")
+ stats_text_parts.append(f"🔗 {_('admin_panel_nodes_label')}: {total_online}")
except Exception as e:
logging.error(f"Failed to fetch panel statistics: {e}", exc_info=True)
- stats_text_parts.append(f"❌ {_('admin_panel_stats_fetch_error', default='Ошибка получения данных с панели')}")
- stats_text_parts.append(f"⚠️ {_('admin_panel_stats_error_details', default='Детали')}: {str(e)}")
+ stats_text_parts.append(f"❌ {_('admin_panel_stats_fetch_error')}")
+ stats_text_parts.append(f"⚠️ {_('admin_panel_stats_error_details')}: {str(e)}")
# Financial statistics
financial_stats = await payment_dal.get_financial_statistics(session)
stats_text_parts.append(
- f"\n💰 {_('admin_financial_stats_header', default='Финансовая статистика')}"
+ f"\n💰 {_('admin_financial_stats_header')}"
)
stats_text_parts.append(
- f"📅 {_('admin_financial_today_label', default='За сегодня')}: {financial_stats['today_revenue']:.2f} RUB ({financial_stats['today_payments_count']} {_('admin_financial_payments_label', default='платежей')})"
+ f"📅 {_('admin_financial_today_label')}: {financial_stats['today_revenue']:.2f} RUB ({financial_stats['today_payments_count']} {_('admin_financial_payments_label')})"
)
stats_text_parts.append(
- f"📅 {_('admin_financial_week_label', default='За неделю')}: {financial_stats['week_revenue']:.2f} RUB"
+ f"📅 {_('admin_financial_week_label')}: {financial_stats['week_revenue']:.2f} RUB"
)
stats_text_parts.append(
- f"📅 {_('admin_financial_month_label', default='За месяц')}: {financial_stats['month_revenue']:.2f} RUB"
+ f"📅 {_('admin_financial_month_label')}: {financial_stats['month_revenue']:.2f} RUB"
)
stats_text_parts.append(
- f"🏆 {_('admin_financial_all_time_label', default='За все время')}: {financial_stats['all_time_revenue']:.2f} RUB"
+ f"🏆 {_('admin_financial_all_time_label')}: {financial_stats['all_time_revenue']:.2f} RUB"
)
last_payments_models: List[
diff --git a/bot/handlers/admin/user_management.py b/bot/handlers/admin/user_management.py
index 07f092d..4ecf6e1 100644
--- a/bot/handlers/admin/user_management.py
+++ b/bot/handlers/admin/user_management.py
@@ -56,7 +56,6 @@ async def users_list_handler(callback: types.CallbackQuery,
# Format message
header_text = _(
"admin_users_list_header",
- default="👥 Список пользователей\n\nСтраница {current}/{total} ({total_users} пользователей)",
current=page + 1,
total=total_pages,
total_users=total_users
@@ -88,8 +87,7 @@ async def user_search_prompt_handler(callback: types.CallbackQuery,
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
prompt_text = _(
- "admin_user_management_prompt",
- default="👤 Управление пользователями\n\nВведите ID пользователя или @username для поиска:"
+ "admin_user_management_prompt"
)
try:
@@ -116,56 +114,54 @@ def get_user_card_keyboard(user_id: int, i18n_instance, lang: str,
# Row 1: Trial and Subscription actions
builder.button(
- text=_(key="admin_user_reset_trial_button", default="🔄 Сбросить триал"),
+ text=_(key="admin_user_reset_trial_button"),
callback_data=f"user_action:reset_trial:{user_id}"
)
builder.button(
- text=_(key="admin_user_add_subscription_button", default="➕ Добавить дни"),
+ text=_(key="admin_user_add_subscription_button"),
callback_data=f"user_action:add_subscription:{user_id}"
)
# Row 2: Block/Unblock and Message
builder.button(
- text=_(key="admin_user_toggle_ban_button", default="🚫 Заблокировать/Разблокировать"),
+ text=_(key="admin_user_toggle_ban_button"),
callback_data=f"user_action:toggle_ban:{user_id}"
)
builder.button(
- text=_(key="admin_user_send_message_button", default="✉️ Отправить сообщение"),
+ text=_(key="admin_user_send_message_button"),
callback_data=f"user_action:send_message:{user_id}"
)
# Row 3: View actions
builder.button(
- text=_(key="admin_user_view_logs_button", default="📜 Действия пользователя"),
+ text=_(key="admin_user_view_logs_button"),
callback_data=f"user_action:view_logs:{user_id}"
)
builder.button(
- text=_(key="admin_user_refresh_button", default="🔄 Обновить"),
+ text=_(key="admin_user_refresh_button"),
callback_data=f"user_action:refresh:{user_id}"
)
# Row 4: Quick links
builder.button(
- text=_(key="user_card_open_profile_button",
- default="👤 Открыть профиль"),
+ text=_(key="user_card_open_profile_button"),
url=f"tg://user?id={user_id}"
)
if referrer_id:
builder.button(
- text=_(key="user_card_open_referrer_profile_button",
- default="👤 Открыть профиль пригласившего"),
+ text=_(key="user_card_open_referrer_profile_button"),
url=f"tg://user?id={referrer_id}"
)
# Row 5: Destructive action
builder.button(
- text=_(key="admin_user_delete_button", default="❌ Удалить пользователя"),
+ text=_(key="admin_user_delete_button"),
callback_data=f"user_action:delete_user:{user_id}"
)
# Row 6: Navigation
builder.button(
- text=_(key="admin_user_search_new_button", default="🔍 Найти другого"),
+ text=_(key="admin_user_search_new_button"),
callback_data="admin_action:users_management"
)
builder.button(
@@ -215,10 +211,10 @@ async def format_user_card(user: User, session: AsyncSession,
# Basic user info
card_parts = []
- card_parts.append(f"👤 {_('admin_user_card_title', default='Карточка пользователя')}\n")
+ card_parts.append(f"👤 {_('admin_user_card_title')}\n")
# User details
- na_value = _("admin_user_na_value", default="N/A")
+ na_value = _("admin_user_na_value")
safe_first_name = sanitize_display_name(user.first_name) if user.first_name else None
user_name = safe_first_name or na_value
if user.username:
@@ -231,23 +227,23 @@ async def format_user_card(user: User, session: AsyncSession,
username_display = na_value
registration_date = user.registration_date.strftime('%Y-%m-%d %H:%M') if user.registration_date else na_value
- card_parts.append(f"{_('admin_user_id_label', default='🆔 ID:')} {hcode(str(user.user_id))}")
- card_parts.append(f"{_('admin_user_name_label', default='👤 Имя:')} {hcode(user_name)}")
- card_parts.append(f"{_('admin_user_username_label', default='📱 Username:')} {hcode(username_display)}")
- card_parts.append(f"{_('admin_user_language_label', default='🌍 Язык:')} {hcode(user.language_code or na_value)}")
- card_parts.append(f"{_('admin_user_registration_label', default='📅 Регистрация:')} {hcode(registration_date)}")
+ card_parts.append(f"{_('admin_user_id_label')} {hcode(str(user.user_id))}")
+ card_parts.append(f"{_('admin_user_name_label')} {hcode(user_name)}")
+ card_parts.append(f"{_('admin_user_username_label')} {hcode(username_display)}")
+ card_parts.append(f"{_('admin_user_language_label')} {hcode(user.language_code or na_value)}")
+ card_parts.append(f"{_('admin_user_registration_label')} {hcode(registration_date)}")
# Ban status
- ban_status = _("admin_user_status_banned", default="🚫 Заблокирован") if user.is_banned else _("admin_user_status_active", default="✅ Активен")
- card_parts.append(f"{_('admin_user_status_label', default='🛡 Статус:')} {ban_status}")
+ ban_status = _("admin_user_status_banned") if user.is_banned else _("admin_user_status_active")
+ card_parts.append(f"{_('admin_user_status_label')} {ban_status}")
# Referral info
if user.referred_by_id:
- card_parts.append(f"{_('admin_user_referral_label', default='🎁 Привлечен по реферальной программе от:')} {hcode(str(user.referred_by_id))}")
+ card_parts.append(f"{_('admin_user_referral_label')} {hcode(str(user.referred_by_id))}")
# Panel info
if user.panel_user_uuid:
- card_parts.append(f"{_('admin_user_panel_uuid_label', default='🔗 Panel UUID:')} {hcode(user.panel_user_uuid[:8] + '...' if len(user.panel_user_uuid) > 8 else user.panel_user_uuid)}")
+ card_parts.append(f"{_('admin_user_panel_uuid_label')} {hcode(user.panel_user_uuid[:8] + '...' if len(user.panel_user_uuid) > 8 else user.panel_user_uuid)}")
card_parts.append("") # Empty line
@@ -255,38 +251,38 @@ async def format_user_card(user: User, session: AsyncSession,
try:
subscription_details = await subscription_service.get_active_subscription_details(session, user.user_id)
if subscription_details:
- card_parts.append(f"💳 {_('admin_user_subscription_info', default='Информация о подписке:')}")
+ card_parts.append(f"💳 {_('admin_user_subscription_info')}")
end_date = subscription_details.get('end_date')
if end_date:
end_date_str = end_date.strftime('%Y-%m-%d %H:%M') if isinstance(end_date, datetime) else str(end_date)
- card_parts.append(f"{_('admin_user_subscription_active_until', default='⏰ Действует до:')} {hcode(end_date_str)}")
+ card_parts.append(f"{_('admin_user_subscription_active_until')} {hcode(end_date_str)}")
status = subscription_details.get('status_from_panel', 'UNKNOWN')
- card_parts.append(f"{_('admin_user_panel_status_label', default='📊 Статус на панели:')} {hcode(status)}")
+ card_parts.append(f"{_('admin_user_panel_status_label')} {hcode(status)}")
traffic_limit = subscription_details.get('traffic_limit_bytes')
traffic_used = subscription_details.get('traffic_used_bytes')
if traffic_limit and traffic_used is not None:
traffic_limit_gb = traffic_limit / (1024**3)
traffic_used_gb = traffic_used / (1024**3)
- card_parts.append(f"{_('admin_user_traffic_label', default='📊 Трафик:')} {hcode(f'{traffic_used_gb:.2f}GB / {traffic_limit_gb:.2f}GB')}")
+ card_parts.append(f"{_('admin_user_traffic_label')} {hcode(f'{traffic_used_gb:.2f}GB / {traffic_limit_gb:.2f}GB')}")
else:
- card_parts.append(f"{_('admin_user_subscription_label', default='💼 Подписка:')} {hcode(_('admin_user_subscription_none', default='Нет активной подписки'))}")
+ card_parts.append(f"{_('admin_user_subscription_label')} {hcode(_('admin_user_subscription_none'))}")
except Exception as e:
logging.error(f"Error getting subscription details for user {user.user_id}: {e}")
- card_parts.append(f"{_('admin_user_subscription_label', default='💼 Подписка:')} {hcode(_('admin_user_subscription_error', default='Ошибка загрузки'))}")
+ card_parts.append(f"{_('admin_user_subscription_label')} {hcode(_('admin_user_subscription_error'))}")
# Statistics
try:
# Count user logs
logs_count = await message_log_dal.count_user_message_logs(session, user.user_id)
- card_parts.append(f"{_('admin_user_actions_count_label', default='📜 Всего действий:')} {hcode(str(logs_count))}")
+ card_parts.append(f"{_('admin_user_actions_count_label')} {hcode(str(logs_count))}")
# Check if user had any subscriptions
had_subscriptions = await subscription_service.has_had_any_subscription(session, user.user_id)
- trial_status = _("admin_user_trial_used", default="Использовал") if had_subscriptions else _("admin_user_trial_not_used", default="Не использовал")
- card_parts.append(f"{_('admin_user_trial_label', default='🏡 Триал:')} {hcode(trial_status)}")
+ trial_status = _("admin_user_trial_used") if had_subscriptions else _("admin_user_trial_not_used")
+ card_parts.append(f"{_('admin_user_trial_label')} {hcode(trial_status)}")
# Financial analytics (admin-only)
try:
@@ -294,11 +290,11 @@ async def format_user_card(user: User, session: AsyncSession,
# Total amount paid by this user
total_paid = await payment_dal.get_user_total_paid(session, user.user_id)
- card_parts.append(f"{_('admin_user_total_paid_label', default='💰 Всего оплачено:')} {hcode(f'{total_paid:.2f} RUB')}")
+ card_parts.append(f"{_('admin_user_total_paid_label')} {hcode(f'{total_paid:.2f} RUB')}")
# Total revenue from referrals
referral_revenue = await payment_dal.get_referral_revenue(session, user.user_id)
- card_parts.append(f"{_('admin_user_referral_revenue_label', default='💸 Доход по рефералам:')} {hcode(f'{referral_revenue:.2f} RUB')}")
+ card_parts.append(f"{_('admin_user_referral_revenue_label')} {hcode(f'{referral_revenue:.2f} RUB')}")
except Exception as e_fin:
logging.error(f"Failed to build financial analytics for admin card {user.user_id}: {e_fin}")
@@ -308,8 +304,8 @@ async def format_user_card(user: User, session: AsyncSession,
stats = await referral_service.get_referral_stats(session, user.user_id)
invited_count = stats.get('invited_count', 0)
purchased_count = stats.get('purchased_count', 0)
- card_parts.append(f"{_('admin_user_invited_friends_label', default='👥 Приглашено друзей:')} {hcode(str(invited_count))}")
- card_parts.append(f"{_('admin_user_ref_purchased_label', default='💳 Купили подписку:')} {hcode(str(purchased_count))}")
+ card_parts.append(f"{_('admin_user_invited_friends_label')} {hcode(str(invited_count))}")
+ card_parts.append(f"{_('admin_user_ref_purchased_label')} {hcode(str(purchased_count))}")
except Exception as e_rs:
logging.error(f"Failed to build referral stats for admin card {user.user_id}: {e_rs}")
@@ -349,7 +345,6 @@ async def process_user_search_handler(message: types.Message, state: FSMContext,
if not user_model:
await message.answer(_(
"admin_user_not_found",
- default="❌ Пользователь не найден: {input}",
input=hcode(input_text)
))
return
@@ -379,8 +374,7 @@ async def process_user_search_handler(message: types.Message, state: FSMContext,
except Exception as e:
logging.error(f"Error displaying user card for {user_model.user_id}: {e}")
await message.answer(_(
- "admin_user_card_error",
- default="❌ Ошибка отображения карточки пользователя"
+ "admin_user_card_error"
))
@@ -410,8 +404,7 @@ async def user_action_handler(callback: types.CallbackQuery, state: FSMContext,
user = await user_dal.get_user_by_id(session, user_id)
if not user:
await callback.answer(_(
- "admin_user_not_found_action",
- default="Пользователь не найден"
+ "admin_user_not_found_action"
), show_alert=True)
return
@@ -447,8 +440,7 @@ async def handle_reset_trial(callback: types.CallbackQuery, user: User,
await session.commit()
await callback.answer(_(
- "admin_user_trial_reset_success",
- default="✅ Триал сброшен! Пользователь может активировать триал заново."
+ "admin_user_trial_reset_success"
), show_alert=True)
# Refresh user card
@@ -458,8 +450,7 @@ async def handle_reset_trial(callback: types.CallbackQuery, user: User,
logging.error(f"Error resetting trial for user {user.user_id}: {e}")
await session.rollback()
await callback.answer(_(
- "admin_user_trial_reset_error",
- default="❌ Ошибка сброса триала"
+ "admin_user_trial_reset_error"
), show_alert=True)
@@ -473,7 +464,6 @@ async def handle_add_subscription_prompt(callback: types.CallbackQuery, state: F
prompt_text = _(
"admin_user_add_subscription_prompt",
- default="➕ Добавление дней подписки для пользователя {user_id}\n\nВведите количество дней для добавления:",
user_id=user.user_id
)
@@ -504,10 +494,9 @@ async def handle_toggle_ban(callback: types.CallbackQuery, user: User,
await session.commit()
- status_text = _("admin_user_ban_action_banned", default="заблокирован") if new_ban_status else _("admin_user_ban_action_unbanned", default="разблокирован")
+ status_text = _("admin_user_ban_action_banned") if new_ban_status else _("admin_user_ban_action_unbanned")
await callback.answer(_(
"admin_user_ban_toggle_success",
- default="✅ Пользователь {status}",
status=status_text
), show_alert=True)
@@ -524,8 +513,7 @@ async def handle_toggle_ban(callback: types.CallbackQuery, user: User,
logging.error(f"Error toggling ban for user {user.user_id}: {e}")
await session.rollback()
await callback.answer(_(
- "admin_user_ban_toggle_error",
- default="❌ Ошибка изменения статуса блокировки"
+ "admin_user_ban_toggle_error"
), show_alert=True)
@@ -539,7 +527,6 @@ async def handle_send_message_prompt(callback: types.CallbackQuery, state: FSMCo
prompt_text = _(
"admin_user_send_message_prompt",
- default="✉️ Отправка сообщения пользователю {user_id}\n\nВведите текст сообщения:",
user_id=user.user_id
)
@@ -563,13 +550,12 @@ async def handle_view_user_logs(callback: types.CallbackQuery, user: User,
if not logs:
await callback.answer(_(
- "admin_user_no_logs",
- default="📜 У пользователя нет действий"
+ "admin_user_no_logs"
), show_alert=True)
return
logs_text_parts = [
- f"{_('admin_user_recent_actions_title', default='📜 Последние действия пользователя {user_id}:', user_id=user.user_id)}\n"
+ f"{_('admin_user_recent_actions_title', user_id=user.user_id)}\n"
]
for log in logs:
@@ -587,11 +573,11 @@ async def handle_view_user_logs(callback: types.CallbackQuery, user: User,
# Create inline keyboard for full logs
builder = InlineKeyboardBuilder()
builder.button(
- text=_(key="admin_user_view_all_logs_button", default="📋 Все действия"),
+ text=_(key="admin_user_view_all_logs_button"),
callback_data=f"admin_logs:view_user:{user.user_id}:0"
)
builder.button(
- text=_(key="admin_user_back_to_card_button", default="🔙 К карточке"),
+ text=_(key="admin_user_back_to_card_button"),
callback_data=f"user_action:refresh:{user.user_id}"
)
builder.adjust(1)
@@ -614,8 +600,7 @@ async def handle_view_user_logs(callback: types.CallbackQuery, user: User,
except Exception as e:
logging.error(f"Error viewing logs for user {user.user_id}: {e}")
await callback.answer(_(
- "admin_user_logs_error",
- default="❌ Ошибка загрузки действий пользователя"
+ "admin_user_logs_error"
), show_alert=True)
@@ -682,7 +667,6 @@ async def handle_delete_user_prompt(callback: types.CallbackQuery, state: FSMCon
await callback.answer(
_(
"admin_user_delete_not_allowed",
- default="❌ У вас нет прав для удаления пользователей.",
),
show_alert=True,
)
@@ -696,11 +680,6 @@ async def handle_delete_user_prompt(callback: types.CallbackQuery, state: FSMCon
prompt_text = _(
"admin_user_delete_confirmation_prompt",
- default=(
- "⚠️ Вы хотите полностью удалить пользователя {user_id}.\n\n"
- "Отправьте точный Telegram ID этого пользователя, чтобы подтвердить удаление.\n"
- "Любой другой ответ отменит операцию."
- ),
user_id=hcode(str(user.user_id)),
)
@@ -771,7 +750,6 @@ async def process_delete_user_confirmation_handler(message: types.Message,
await message.answer(
_(
"admin_user_delete_not_allowed",
- default="❌ У вас нет прав для удаления пользователей.",
)
)
await state.clear()
@@ -783,7 +761,6 @@ async def process_delete_user_confirmation_handler(message: types.Message,
await message.answer(
_(
"admin_user_delete_state_missing",
- default="⚠️ Нет активной операции удаления. Начните заново.",
)
)
await state.clear()
@@ -794,7 +771,6 @@ async def process_delete_user_confirmation_handler(message: types.Message,
await message.answer(
_(
"admin_user_delete_cancelled",
- default="Операция удаления отменена по запросу.",
)
)
await state.clear()
@@ -804,7 +780,6 @@ async def process_delete_user_confirmation_handler(message: types.Message,
await message.answer(
_(
"admin_user_delete_mismatch",
- default="⚠️ ID не совпадает. Удаление отменено.",
)
)
await state.clear()
@@ -815,7 +790,6 @@ async def process_delete_user_confirmation_handler(message: types.Message,
await message.answer(
_(
"admin_user_delete_already_removed",
- default="ℹ️ Пользователь уже удален.",
)
)
await state.clear()
@@ -830,10 +804,6 @@ async def process_delete_user_confirmation_handler(message: types.Message,
await message.answer(
_(
"admin_user_delete_panel_error",
- default=(
- "❌ Не удалось удалить пользователя на панели. "
- "Операция прервана."
- ),
)
)
await session.rollback()
@@ -847,7 +817,6 @@ async def process_delete_user_confirmation_handler(message: types.Message,
await message.answer(
_(
"admin_user_delete_already_removed",
- default="ℹ️ Пользователь уже удален.",
)
)
await state.clear()
@@ -859,7 +828,6 @@ async def process_delete_user_confirmation_handler(message: types.Message,
await message.answer(
_(
"admin_user_delete_success",
- default="✅ Пользователь {user_id} удален из бота и панели.",
user_id=hcode(str(target_user_id)),
),
parse_mode="HTML",
@@ -870,7 +838,6 @@ async def process_delete_user_confirmation_handler(message: types.Message,
await message.answer(
_(
"admin_user_delete_error",
- default="❌ Не удалось завершить удаление пользователя. Попробуйте позже.",
)
)
finally:
@@ -903,8 +870,7 @@ async def process_subscription_days_handler(message: types.Message, state: FSMCo
raise ValueError("Invalid days count")
except ValueError:
await message.answer(_(
- "admin_user_invalid_days",
- default="❌ Неверное количество дней. Введите число от 1 до 3650."
+ "admin_user_invalid_days"
))
return
@@ -918,7 +884,6 @@ async def process_subscription_days_handler(message: types.Message, state: FSMCo
await session.commit()
await message.answer(_(
"admin_user_subscription_added_success",
- default="✅ Успешно добавлено {days} дней подписки пользователю {user_id}",
days=days_to_add,
user_id=target_user_id
))
@@ -945,16 +910,14 @@ async def process_subscription_days_handler(message: types.Message, state: FSMCo
else:
await session.rollback()
await message.answer(_(
- "admin_user_subscription_added_error",
- default="❌ Ошибка добавления дней подписки"
+ "admin_user_subscription_added_error"
))
except Exception as e:
logging.error(f"Error adding subscription days for user {target_user_id}: {e}")
await session.rollback()
await message.answer(_(
- "admin_user_subscription_added_error",
- default="❌ Ошибка добавления дней подписки"
+ "admin_user_subscription_added_error"
))
await state.clear()
@@ -983,8 +946,7 @@ async def process_direct_message_handler(message: types.Message, state: FSMConte
text = (message.text or message.caption or "").strip()
if len(text) > 4000:
await message.answer(_(
- "admin_user_message_too_long",
- default="❌ Сообщение слишком длинное (максимум 4000 символов)"
+ "admin_user_message_too_long"
))
return
@@ -998,16 +960,14 @@ async def process_direct_message_handler(message: types.Message, state: FSMConte
# Prepare admin signature and get content
admin_signature = _(
- "admin_direct_message_signature",
- default="\n\n---\n💬 Сообщение от администратора"
+ "admin_direct_message_signature"
)
content = get_message_content(message)
if not content.text and not content.file_id:
await message.answer(_(
- "admin_direct_empty_message",
- default="❌ Пустое сообщение. Отправьте текст или медиа."
+ "admin_direct_empty_message"
))
return
@@ -1026,7 +986,6 @@ async def process_direct_message_handler(message: types.Message, state: FSMConte
except TelegramBadRequest as e:
await message.answer(_(
"admin_broadcast_invalid_html",
- default="❌ Некорректный HTML в сообщении. Пожалуйста, отправьте корректный HTML (поддерживаются теги Telegram) или уберите теги.\nОшибка: {error}",
error=str(e),
))
return
@@ -1034,7 +993,6 @@ async def process_direct_message_handler(message: types.Message, state: FSMConte
# Confirm to admin
await message.answer(_(
"admin_user_message_sent_success",
- default="✅ Сообщение отправлено пользователю {user_id}",
user_id=target_user_id
))
@@ -1062,8 +1020,7 @@ async def process_direct_message_handler(message: types.Message, state: FSMConte
except Exception as e:
logging.error(f"Error sending direct message to user {target_user_id}: {e}")
await message.answer(_(
- "admin_user_message_sent_error",
- default="❌ Ошибка отправки сообщения"
+ "admin_user_message_sent_error"
))
await state.clear()
@@ -1081,8 +1038,7 @@ async def ban_user_prompt_handler(callback: types.CallbackQuery,
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
prompt_text = _(
- "admin_ban_user_prompt",
- default="🚫 Блокировка пользователя\n\nВведите ID пользователя или @username для блокировки:"
+ "admin_ban_user_prompt"
)
try:
@@ -1113,8 +1069,7 @@ async def unban_user_prompt_handler(callback: types.CallbackQuery,
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
prompt_text = _(
- "admin_unban_user_prompt",
- default="✅ Разблокировка пользователя\n\nВведите ID пользователя или @username для разблокировки:"
+ "admin_unban_user_prompt"
)
try:
@@ -1150,8 +1105,7 @@ async def view_banned_users_handler(callback: types.CallbackQuery,
if not banned_users:
message_text = _(
- "admin_banned_users_empty",
- default="📋 Заблокированные пользователи\n\nСписок пуст"
+ "admin_banned_users_empty"
)
else:
user_list = []
@@ -1163,7 +1117,6 @@ async def view_banned_users_handler(callback: types.CallbackQuery,
message_text = _(
"admin_banned_users_list",
- default="📋 Заблокированные пользователи ({count}):\n\n{users}",
count=len(banned_users),
users="\n".join(user_list)
)
@@ -1208,7 +1161,6 @@ async def process_ban_user_handler(message: types.Message, state: FSMContext,
if not user_model:
await message.answer(_(
"admin_user_not_found",
- default="❌ Пользователь не найден: {input}",
input=hcode(input_text)
))
return
@@ -1217,8 +1169,7 @@ async def process_ban_user_handler(message: types.Message, state: FSMContext,
# Check if user is already banned
if user_model.is_banned:
await message.answer(_(
- "admin_user_already_banned",
- default="⚠️ Пользователь уже заблокирован"
+ "admin_user_already_banned"
))
await state.clear()
return
@@ -1234,7 +1185,6 @@ async def process_ban_user_handler(message: types.Message, state: FSMContext,
await message.answer(_(
"admin_user_ban_success",
- default="✅ Пользователь {input} заблокирован",
input=hcode(input_text)
))
@@ -1242,8 +1192,7 @@ async def process_ban_user_handler(message: types.Message, state: FSMContext,
logging.error(f"Error banning user {user_model.user_id}: {e}")
await session.rollback()
await message.answer(_(
- "admin_user_ban_error",
- default="❌ Ошибка блокировки пользователя"
+ "admin_user_ban_error"
))
await state.clear()
@@ -1279,7 +1228,6 @@ async def process_unban_user_handler(message: types.Message, state: FSMContext,
if not user_model:
await message.answer(_(
"admin_user_not_found",
- default="❌ Пользователь не найден: {input}",
input=hcode(input_text)
))
return
@@ -1288,8 +1236,7 @@ async def process_unban_user_handler(message: types.Message, state: FSMContext,
# Check if user is not banned
if not user_model.is_banned:
await message.answer(_(
- "admin_user_not_banned",
- default="⚠️ Пользователь не заблокирован"
+ "admin_user_not_banned"
))
await state.clear()
return
@@ -1305,7 +1252,6 @@ async def process_unban_user_handler(message: types.Message, state: FSMContext,
await message.answer(_(
"admin_user_unban_success",
- default="✅ Пользователь {input} разблокирован",
input=hcode(input_text)
))
@@ -1313,8 +1259,7 @@ async def process_unban_user_handler(message: types.Message, state: FSMContext,
logging.error(f"Error unbanning user {user_model.user_id}: {e}")
await session.rollback()
await message.answer(_(
- "admin_user_unban_error",
- default="❌ Ошибка разблокировки пользователя"
+ "admin_user_unban_error"
))
await state.clear()
@@ -1357,7 +1302,7 @@ async def user_card_from_list_handler(callback: types.CallbackQuery,
user.referred_by_id
)
keyboard.button(
- text=_("admin_user_back_to_list_button", default="⬅️ К списку"),
+ text=_("admin_user_back_to_list_button"),
callback_data=f"admin_action:users_list:{page}"
)
quick_links_width = 2 if user.referred_by_id else 1
diff --git a/bot/handlers/inline_mode.py b/bot/handlers/inline_mode.py
index 145fbfb..feb1897 100644
--- a/bot/handlers/inline_mode.py
+++ b/bot/handlers/inline_mode.py
@@ -103,20 +103,16 @@ async def create_referral_result(
# Create message content (use same text as friend message)
message_text = _(
"referral_friend_message",
- default="🚀 Привет! Попробуй этот VPN - быстрый, надёжный и доступный!\n\n"
- "🎁 По моей ссылке тебе дадут бонусные дни к подписке!\n\n{referral_link}",
referral_link=referral_link
)
return InlineQueryResultArticle(
id="referral_link",
title=_(
- "inline_referral_title",
- default="🎁 Пригласить друга"
+ "inline_referral_title"
),
description=_(
- "inline_referral_description",
- default="Поделиться реферальной ссылкой для получения бонусов"
+ "inline_referral_description"
),
input_message_content=InputTextMessageContent(
message_text=message_text,
@@ -167,13 +163,6 @@ async def create_user_stats_result(session: AsyncSession, i18n_instance, lang: s
stats_text = _(
"inline_user_stats_message",
- default="📊 Статистика Бота\n👥 Пользователи\n\n"
- "📊 Всего: {total}\n"
- "💳 С платной подпиской: {paid}\n"
- "🆓 На пробном периоде: {trial}\n"
- "😴 Неактивных: {inactive}\n"
- "🚫 Заблокированных: {banned}\n"
- "🎁 Привлечено по реферальной программе: {referral}",
total=user_stats['total_users'],
active_today=user_stats['active_today'],
paid=user_stats['paid_subscriptions'],
@@ -186,12 +175,10 @@ async def create_user_stats_result(session: AsyncSession, i18n_instance, lang: s
return InlineQueryResultArticle(
id="admin_user_stats",
title=_(
- "inline_admin_user_stats_title",
- default="📊 Статистика пользователей"
+ "inline_admin_user_stats_title"
),
description=_(
"inline_user_stats_description",
- default="Всего: {total}, Платных: {active}",
total=user_stats['total_users'],
active=user_stats['paid_subscriptions']
),
@@ -217,12 +204,6 @@ async def create_financial_stats_result(session: AsyncSession, i18n_instance, la
stats_text = _(
"inline_financial_stats_message",
- default="💰 Финансовая статистика\n\n"
- "📅 За сегодня: {today:.2f} RUB\n"
- " ({today_count} платежей)\n"
- "📅 За неделю: {week:.2f} RUB\n"
- "📅 За месяц: {month:.2f} RUB\n"
- "🏆 За все время: {all_time:.2f} RUB",
today=financial_stats['today_revenue'],
today_count=financial_stats['today_payments_count'],
week=financial_stats['week_revenue'],
@@ -233,12 +214,10 @@ async def create_financial_stats_result(session: AsyncSession, i18n_instance, la
return InlineQueryResultArticle(
id="admin_financial_stats",
title=_(
- "inline_admin_financial_stats_title",
- default="💰 Финансовая статистика"
+ "inline_admin_financial_stats_title"
),
description=_(
"inline_financial_description",
- default="Сегодня: {today} RUB",
today=f"{financial_stats['today_revenue']:.2f}"
),
input_message_content=InputTextMessageContent(
@@ -311,17 +290,6 @@ async def create_system_stats_result(session: AsyncSession, i18n_instance, lang:
stats_text = _(
"inline_system_stats_message",
- default="🖥 Статистика панели\n\n"
- "🟢 Онлайн: {online}\n"
- "📊 Активных: {active}\n"
- "🔴 Отключенных: {disabled}\n"
- "⏰ Истекшие: {expired}\n"
- "⚠️ Ограниченные: {limited}\n"
- "👥 Всего пользователей: {total}\n"
- "💾 Использование RAM: {memory:.1f}%\n"
- "📊 Трафик за неделю: {week_traffic}\n"
- "📊 Трафик за месяц: {month_traffic}\n"
- "🔗 Активных нод: {active_nodes}/{total_nodes}",
online=online_now,
active=active_users,
disabled=disabled_users,
@@ -335,17 +303,15 @@ async def create_system_stats_result(session: AsyncSession, i18n_instance, lang:
total_nodes=total_nodes
)
else:
- stats_text = _("inline_panel_stats_error", default="❌ Ошибка получения данных с панели")
+ stats_text = _("inline_panel_stats_error")
return InlineQueryResultArticle(
id="admin_system_stats",
title=_(
- "inline_admin_system_stats_title",
- default="🖥 Системная статистика"
+ "inline_admin_system_stats_title"
),
description=_(
"inline_system_description",
- default="🟢 Онлайн: {online}, 📊 Активных: {active}",
online=online_now,
active=active_users
),
@@ -359,15 +325,14 @@ async def create_system_stats_result(session: AsyncSession, i18n_instance, lang:
except Exception as e:
logging.error(f"Error creating system stats result: {e}")
# Fallback error message
- error_text = _("inline_panel_stats_error", default="❌ Ошибка получения данных с панели")
+ error_text = _("inline_panel_stats_error")
return InlineQueryResultArticle(
id="admin_system_stats",
title=_(
- "inline_admin_system_stats_title",
- default="🖥 Системная статистика"
+ "inline_admin_system_stats_title"
),
- description=_("inline_system_error", default="Ошибка получения данных"),
+ description=_("inline_system_error"),
input_message_content=InputTextMessageContent(
message_text=error_text,
parse_mode="HTML"
@@ -375,5 +340,3 @@ async def create_system_stats_result(session: AsyncSession, i18n_instance, lang:
thumbnail_url=settings.INLINE_SYSTEM_STATS_THUMBNAIL_URL
)
return None
-
-
diff --git a/bot/handlers/user/subscription/core.py b/bot/handlers/user/subscription/core.py
index 108bf25..d40ece5 100644
--- a/bot/handlers/user/subscription/core.py
+++ b/bot/handlers/user/subscription/core.py
@@ -40,17 +40,29 @@ async def display_subscription_options(event: Union[types.Message, types.Callbac
return
currency_symbol_val = settings.DEFAULT_CURRENCY_SYMBOL
- traffic_mode = bool(getattr(settings, "traffic_sale_mode", False))
- options = settings.traffic_packages if traffic_mode else settings.subscription_options
- text_content = (
- get_text("select_traffic_package") if traffic_mode else get_text("select_subscription_period")
- ) if options else get_text("no_subscription_options_available")
+ traffic_packages = getattr(settings, "traffic_packages", {}) or {}
+ stars_traffic_packages = getattr(settings, "stars_traffic_packages", {}) or {}
+ traffic_mode = bool(getattr(settings, "traffic_sale_mode", False) or stars_traffic_packages)
- reply_markup = (
- get_subscription_options_keyboard(options, currency_symbol_val, current_lang, i18n, traffic_mode=traffic_mode)
- if options
- else get_back_to_main_menu_markup(current_lang, i18n)
- )
+ if traffic_mode:
+ if traffic_packages:
+ options = traffic_packages
+ elif stars_traffic_packages:
+ options = stars_traffic_packages
+ currency_symbol_val = "⭐"
+ else:
+ options = {}
+ else:
+ options = settings.subscription_options
+
+ if options:
+ text_content = get_text("select_traffic_package") if traffic_mode else get_text("select_subscription_period")
+ reply_markup = get_subscription_options_keyboard(
+ options, currency_symbol_val, current_lang, i18n, traffic_mode=traffic_mode
+ )
+ else:
+ text_content = get_text("no_subscription_options_available")
+ reply_markup = get_back_to_main_menu_markup(current_lang, i18n)
target_message_obj = event.message if isinstance(event, types.CallbackQuery) else event
if not target_message_obj:
@@ -108,7 +120,7 @@ async def my_subscription_command_handler(
text = get_text("subscription_not_active")
buy_button = InlineKeyboardButton(
- text=get_text("menu_subscribe_inline", default="Купить"), callback_data="main_action:subscribe"
+ text=get_text("menu_subscribe_inline"), callback_data="main_action:subscribe"
)
back_markup = get_back_to_main_menu_markup(current_lang, i18n)
diff --git a/bot/handlers/user/subscription/payment_methods.py b/bot/handlers/user/subscription/payment_methods.py
index 947d592..efea859 100644
--- a/bot/handlers/user/subscription/payment_methods.py
+++ b/bot/handlers/user/subscription/payment_methods.py
@@ -51,9 +51,9 @@ async def payment_methods_manage(callback: types.CallbackQuery, settings: Settin
return get_text("payment_method_wallet_title", last4=l4)
return get_text("payment_method_wallet_title", last4="****")
if last4:
- network_name = network or get_text("payment_network_card", default="Card")
+ network_name = network or get_text("payment_network_card")
return get_text("payment_method_card_title", network=network_name, last4=last4)
- network_name = network or get_text("payment_network_generic", default="Payment method")
+ network_name = network or get_text("payment_network_generic")
return get_text("payment_method_generic_title", network=network_name)
for m in methods:
@@ -177,9 +177,9 @@ async def payment_method_delete(callback: types.CallbackQuery, settings: Setting
return _("payment_method_wallet_title", last4=l4)
return _("payment_method_wallet_title", last4="****")
if last4:
- network_name = network or _("payment_network_card", default="Card")
+ network_name = network or _("payment_network_card")
return _("payment_method_card_title", network=network_name, last4=last4)
- network_name = network or _("payment_network_generic", default="Payment method")
+ network_name = network or _("payment_network_generic")
return _("payment_method_generic_title", network=network_name)
title = _format_pm_title(m.card_network, m.card_last4)
cards.append((str(m.method_id), title if not m.is_default else f"⭐ {title}"))
@@ -239,9 +239,9 @@ async def payment_method_view(callback: types.CallbackQuery, settings: Settings,
return _("payment_method_wallet_title", last4=l4)
return _("payment_method_wallet_title", last4="****")
if last4:
- network_name = network or _("payment_network_card", default="Card")
+ network_name = network or _("payment_network_card")
return _("payment_method_card_title", network=network_name, last4=last4)
- network_name = network or _("payment_network_generic", default="Payment method")
+ network_name = network or _("payment_network_generic")
return _("payment_method_generic_title", network=network_name)
title = _format_pm_title(sel.card_network, sel.card_last4)
@@ -307,9 +307,9 @@ async def payment_method_view(callback: types.CallbackQuery, settings: Settings,
return _("payment_method_wallet_title", last4=l4)
return _("payment_method_wallet_title", last4="****")
if last4:
- network_name = network or _("payment_network_card", default="Card")
+ network_name = network or _("payment_network_card")
return _("payment_method_card_title", network=network_name, last4=last4)
- network_name = network or _("payment_network_generic", default="Payment method")
+ network_name = network or _("payment_network_generic")
return _("payment_method_generic_title", network=network_name)
title = _format_pm_title(billing.card_network, billing.card_last4)
@@ -440,9 +440,9 @@ async def payment_methods_list(callback: types.CallbackQuery, settings: Settings
return get_text("payment_method_wallet_title", last4=l4)
return get_text("payment_method_wallet_title", last4="****")
if last4:
- network_name = network or get_text("payment_network_card", default="Card")
+ network_name = network or get_text("payment_network_card")
return get_text("payment_method_card_title", network=network_name, last4=last4)
- network_name = network or get_text("payment_network_generic", default="Payment method")
+ network_name = network or get_text("payment_network_generic")
return get_text("payment_method_generic_title", network=network_name)
title = _format_pm_title(m.card_network, m.card_last4)
cards.append((str(m.method_id), title if not m.is_default else f"⭐ {title}"))
diff --git a/bot/handlers/user/subscription/payments_subscription.py b/bot/handlers/user/subscription/payments_subscription.py
index e6145e1..7a63e31 100644
--- a/bot/handlers/user/subscription/payments_subscription.py
+++ b/bot/handlers/user/subscription/payments_subscription.py
@@ -29,7 +29,9 @@ async def select_subscription_period_callback_handler(
pass
return
- traffic_mode = bool(getattr(settings, "traffic_sale_mode", False))
+ traffic_packages = getattr(settings, "traffic_packages", {}) or {}
+ stars_traffic_packages = getattr(settings, "stars_traffic_packages", {}) or {}
+ traffic_mode = bool(getattr(settings, "traffic_sale_mode", False) or stars_traffic_packages)
try:
months = float(callback.data.split(":")[-1])
except (ValueError, IndexError):
@@ -40,23 +42,47 @@ async def select_subscription_period_callback_handler(
pass
return
- price_source = settings.traffic_packages if traffic_mode else settings.subscription_options
- stars_price_source = settings.stars_traffic_packages if traffic_mode else settings.stars_subscription_options
+ price_source = traffic_packages if traffic_mode else settings.subscription_options
+ stars_price_source = stars_traffic_packages if traffic_mode else settings.stars_subscription_options
price_rub = price_source.get(months)
- if price_rub is None:
- logging.error(
- f"Price not found for option {months} using {'traffic_packages' if traffic_mode else 'subscription_options'}."
- )
- try:
- await callback.answer(get_text("error_try_again"), show_alert=True)
- except Exception:
- pass
- return
-
- currency_symbol_val = settings.DEFAULT_CURRENCY_SYMBOL
- text_content = get_text("choose_payment_method_traffic") if traffic_mode else get_text("choose_payment_method")
stars_price = stars_price_source.get(months)
+ currency_symbol_val = settings.DEFAULT_CURRENCY_SYMBOL
+
+ if price_rub is None:
+ if traffic_mode and not price_source and stars_price is not None:
+ currency_methods_enabled = any(
+ [
+ settings.FREEKASSA_ENABLED,
+ settings.PLATEGA_ENABLED,
+ settings.SEVERPAY_ENABLED,
+ settings.YOOKASSA_ENABLED,
+ settings.CRYPTOPAY_ENABLED,
+ ]
+ )
+ if currency_methods_enabled:
+ logging.error(
+ "Currency price missing for traffic option %s while fiat providers are enabled.",
+ months,
+ )
+ try:
+ await callback.answer(get_text("error_try_again"), show_alert=True)
+ except Exception:
+ pass
+ return
+ price_rub = 0.0
+ currency_symbol_val = "⭐"
+ else:
+ logging.error(
+ f"Price not found for option {months} using {'traffic_packages' if traffic_mode else 'subscription_options'}."
+ )
+ try:
+ await callback.answer(get_text("error_try_again"), show_alert=True)
+ except Exception:
+ pass
+ return
+
+ text_content = get_text("choose_payment_method_traffic") if traffic_mode else get_text("choose_payment_method")
reply_markup = get_payment_method_keyboard(
months,
price_rub,
diff --git a/bot/keyboards/inline/admin_keyboards.py b/bot/keyboards/inline/admin_keyboards.py
index c27a3bf..1b38a66 100644
--- a/bot/keyboards/inline/admin_keyboards.py
+++ b/bot/keyboards/inline/admin_keyboards.py
@@ -26,7 +26,7 @@ def get_admin_panel_keyboard(i18n_instance, lang: str,
callback_data="admin_section:promo_marketing")
# Реклама
- builder.button(text=_(key="admin_ads_section", default="📈 Реклама"),
+ builder.button(text=_(key="admin_ads_section"),
callback_data="admin_action:ads")
# Системные функции
@@ -43,7 +43,7 @@ def get_stats_monitoring_keyboard(i18n_instance, lang: str) -> InlineKeyboardMar
builder.button(text=_(key="admin_stats_button"),
callback_data="admin_action:stats")
- builder.button(text=_(key="admin_view_payments_button", default="💰 Платежи"),
+ builder.button(text=_(key="admin_view_payments_button"),
callback_data="admin_action:view_payments")
builder.button(text=_(key="admin_view_logs_menu_button"),
callback_data="admin_action:view_logs_menu")
@@ -125,7 +125,7 @@ def get_system_functions_keyboard(i18n_instance, lang: str) -> InlineKeyboardMar
def get_ads_menu_keyboard(i18n_instance, lang: str) -> InlineKeyboardMarkup:
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
builder = InlineKeyboardBuilder()
- builder.button(text=_(key="admin_ads_create_button", default="➕ Создать кампанию"),
+ builder.button(text=_(key="admin_ads_create_button"),
callback_data="admin_action:ads_create")
builder.button(text=_(key="back_to_admin_panel_button"),
callback_data="admin_action:main")
@@ -156,7 +156,7 @@ def get_ads_list_keyboard(
if current_page > 0:
row.append(
InlineKeyboardButton(
- text="⬅️ " + _("prev_page_button", default="Prev"),
+ text="⬅️ " + _("prev_page_button"),
callback_data=f"admin_ads:page:{current_page - 1}",
)
)
@@ -169,14 +169,14 @@ def get_ads_list_keyboard(
if current_page < total_pages - 1:
row.append(
InlineKeyboardButton(
- text=_("next_page_button", default="Next") + " ➡️",
+ text=_("next_page_button") + " ➡️",
callback_data=f"admin_ads:page:{current_page + 1}",
)
)
if row:
builder.row(*row)
- builder.button(text=_(key="admin_ads_create_button", default="➕ Создать кампанию"),
+ builder.button(text=_(key="admin_ads_create_button"),
callback_data="admin_action:ads_create")
builder.button(text=_(key="back_to_admin_panel_button"),
callback_data="admin_action:main")
@@ -188,9 +188,9 @@ def get_ad_card_keyboard(i18n_instance, lang: str, campaign_id: int, back_page:
_ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs)
builder = InlineKeyboardBuilder()
# Dangerous action: Delete campaign
- builder.button(text=_(key="admin_ads_delete_button", default="🗑 Удалить кампанию"),
+ builder.button(text=_(key="admin_ads_delete_button"),
callback_data=f"admin_ads:delete:{campaign_id}:{back_page}")
- builder.button(text=_(key="back_to_ads_list_button", default="⬅️ К списку"),
+ builder.button(text=_(key="back_to_ads_list_button"),
callback_data=f"admin_ads:page:{back_page}")
builder.button(text=_(key="back_to_admin_panel_button"),
callback_data="admin_action:main")
@@ -227,12 +227,12 @@ def get_logs_pagination_keyboard(
if current_page > 0:
row_buttons.append(
InlineKeyboardButton(
- text="⬅️ " + _("prev_page_button", default="Prev"),
+ text="⬅️ " + _("prev_page_button"),
callback_data=f"{base_callback_data}:{current_page - 1}"))
if current_page < total_pages - 1:
row_buttons.append(
InlineKeyboardButton(
- text=_("next_page_button", default="Next") + " ➡️",
+ text=_("next_page_button") + " ➡️",
callback_data=f"{base_callback_data}:{current_page + 1}"))
if row_buttons: builder.row(*row_buttons)
@@ -386,8 +386,7 @@ def get_user_card_keyboard(user_id: int,
callback_data=f"admin_ban_confirm:{user_id}:{banned_list_page}")
builder.button(
text=_(
- key="user_card_open_profile_button",
- default="👤 Open profile"
+ key="user_card_open_profile_button"
),
url=f"tg://user?id={user_id}"
)
@@ -418,16 +417,13 @@ def get_broadcast_confirmation_keyboard(lang: str,
# Row: target selection (all / active / inactive)
target_all_label = _(
- key="broadcast_target_all_button",
- default="👥 Все"
+ key="broadcast_target_all_button"
)
target_active_label = _(
- key="broadcast_target_active_button",
- default="✅ Активные"
+ key="broadcast_target_active_button"
)
target_inactive_label = _(
- key="broadcast_target_inactive_button",
- default="⌛ Неактивные"
+ key="broadcast_target_inactive_button"
)
# Highlight current selection with a prefix
@@ -449,9 +445,9 @@ def get_broadcast_confirmation_keyboard(lang: str,
builder.adjust(3)
# Row: confirmation
- builder.button(text=_(key="confirm_broadcast_send_button", default="🚀 Отправить"),
+ builder.button(text=_(key="confirm_broadcast_send_button"),
callback_data="broadcast_final_action:send")
- builder.button(text=_(key="cancel_broadcast_button", default="❌ Отмена"),
+ builder.button(text=_(key="cancel_broadcast_button"),
callback_data="broadcast_final_action:cancel")
builder.adjust(2)
return builder.as_markup()
diff --git a/bot/services/notification_service.py b/bot/services/notification_service.py
index 53b4ec5..8c79903 100644
--- a/bot/services/notification_service.py
+++ b/bot/services/notification_service.py
@@ -52,7 +52,6 @@ class NotificationService:
InlineKeyboardButton(
text=translate(
"log_open_profile_link",
- default="👤 Открыть профиль",
),
url=f"tg://user?id={user_id}",
)
@@ -64,7 +63,6 @@ class NotificationService:
InlineKeyboardButton(
text=translate(
"log_open_referrer_profile_button",
- default="👤 Открыть профиль пригласившего",
),
url=f"tg://user?id={referrer_id}",
)
@@ -200,16 +198,11 @@ class NotificationService:
referrer_link = hd.link(str(referred_by_id), f"tg://user?id={referred_by_id}")
referral_text = _(
"log_referral_suffix",
- default=" (реферал от {referrer_link})",
referrer_link=referrer_link,
)
message = _(
"log_new_user_registration",
- default="👤 Новый пользователь\n\n"
- "🆔 ID: {user_id}\n"
- "👤 Имя: {user_display}{referral_text}\n"
- "📅 Время: {timestamp}",
user_id=user_id,
user_display=user_display,
referral_text=referral_text,
@@ -249,12 +242,6 @@ class NotificationService:
traffic_label = str(int(traffic_gb)) if float(traffic_gb).is_integer() else f"{traffic_gb:g}"
message = _(
"log_payment_received_traffic",
- default="{provider_emoji} Получен платеж\n\n"
- "👤 Пользователь: {user_display}\n"
- "💰 Сумма: {amount} {currency}\n"
- "🗂 Трафик: {traffic_gb} GB\n"
- "🏦 Провайдер: {payment_provider}\n"
- "🕐 Время: {timestamp}",
provider_emoji=provider_emoji,
user_display=user_display,
amount=amount,
@@ -266,12 +253,6 @@ class NotificationService:
else:
message = _(
"log_payment_received",
- default="{provider_emoji} Получен платеж\n\n"
- "👤 Пользователь: {user_display}\n"
- "💰 Сумма: {amount} {currency}\n"
- "📅 Период: {months} мес.\n"
- "🏦 Провайдер: {payment_provider}\n"
- "🕐 Время: {timestamp}",
provider_emoji=provider_emoji,
user_display=user_display,
amount=amount,
@@ -301,11 +282,6 @@ class NotificationService:
message = _(
"log_promo_activation",
- default="🎁 Активирован промокод\n\n"
- "👤 Пользователь: {user_display}\n"
- "🏷 Код: {promo_code}\n"
- "🎯 Бонус: +{bonus_days} дн.\n"
- "🕐 Время: {timestamp}",
user_display=user_display,
promo_code=promo_code,
bonus_days=bonus_days,
@@ -332,10 +308,6 @@ class NotificationService:
message = _(
"log_trial_activation",
- default="🆓 Активирован триал\n\n"
- "👤 Пользователь: {user_display}\n"
- "⏰ Действует до: {end_date}\n"
- "🕐 Время: {timestamp}",
user_display=user_display,
end_date=end_date.strftime("%Y-%m-%d %H:%M"),
timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -364,12 +336,6 @@ class NotificationService:
message = _(
"log_panel_sync",
- default="{status_emoji} Синхронизация с панелью\n\n"
- "📊 Статус: {status}\n"
- "👥 Обработано пользователей: {users_processed}\n"
- "📋 Синхронизировано подписок: {subs_synced}\n"
- "🕐 Время: {timestamp}\n\n"
- "📝 Детали:\n{details}",
status_emoji=status_emoji,
status=status,
users_processed=users_processed,
@@ -400,11 +366,6 @@ class NotificationService:
message = _(
"log_suspicious_promo",
- default="⚠️ Подозрительная попытка ввода промокода\n\n"
- "👤 Пользователь: {user_display}\n"
- "🆔 ID: {user_id}\n"
- "📝 Ввод:
{suspicious_input}\n"
- "🕐 Время: {timestamp}",
user_display=hd.quote(user_display),
user_id=user_id,
suspicious_input=hd.quote(suspicious_input),
diff --git a/config/settings.py b/config/settings.py
index 535efb9..ce33b1b 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -423,7 +423,7 @@ class Settings(BaseSettings):
@property
def traffic_sale_mode(self) -> bool:
"""When true, the bot sells traffic packages instead of time-based subscriptions."""
- return bool(self.traffic_packages)
+ return bool(self.traffic_packages or self.stars_traffic_packages)
def referral_bonus_inviter(self) -> Dict[int, int]:
bonuses: Dict[int, int] = {}
diff --git a/locales/ru.json b/locales/ru.json
index 42b3e2b..de5f44d 100644
--- a/locales/ru.json
+++ b/locales/ru.json
@@ -9,7 +9,7 @@
"menu_activate_trial_button": "🆓 Пробный период",
"menu_subscribe_inline": "🚀 Купить",
"menu_my_subscription_inline": "🔐 Моя подписка",
- "no_subscription_options_available": "Выдача подписки не натроена администратором бота",
+ "no_subscription_options_available": "Выдача подписки не настроена администратором бота",
"menu_referral_inline": "🎁 Рефералы",
"referral_no_bonuses_configured": "Извините, реферальная программа в данный момент отключена",
"menu_apply_promo_button": "🎟 Промокод",