translate to rus

This commit is contained in:
2026-07-09 23:49:42 +03:00
parent cb573a52d5
commit 11f03eae41
8 changed files with 89 additions and 79 deletions
+46 -46
View File
@@ -35,11 +35,11 @@ class BotApp:
def _register_handlers(self) -> None:
self.router.message(CommandStart())(self.start)
self.router.message(F.text == " New invite")(self.new_invite)
self.router.message(F.text == " Новое приглашение")(self.new_invite)
self.router.message(F.text == "/new_user")(self.new_invite)
self.router.message(F.text == "📄 VPN info")(self.vpn_info)
self.router.message(F.text == "💳 Extend VPN")(self.extend_menu)
self.router.message(F.text == "🔄 Reset traffic")(self.reset_explain)
self.router.message(F.text == "📄 Информация о VPN")(self.vpn_info)
self.router.message(F.text == "💳 Продлить VPN")(self.extend_menu)
self.router.message(F.text == "🔄 Сбросить трафик")(self.reset_explain)
self.router.message(F.contact)(self.save_contact)
self.router.callback_query(F.data.startswith("extend:"))(self.select_extend)
self.router.callback_query(F.data == "reset:confirm")(self.confirm_reset)
@@ -55,26 +55,26 @@ class BotApp:
return
if self.is_admin(user_id):
await message.answer("Admin menu", reply_markup=admin_menu())
await message.answer("Меню администратора", reply_markup=admin_menu())
return
if user_id and self.db.get_by_tg_user_id(user_id):
await message.answer("Welcome back.", reply_markup=main_menu())
await message.answer("С возвращением.", reply_markup=main_menu())
return
await message.answer("Ask an admin for an invite link.")
await message.answer("Попросите администратора выдать ссылку-приглашение.")
async def new_invite(self, message: Message) -> None:
if not self.is_admin(message.from_user.id if message.from_user else None):
await message.answer("Only admins can create invite links.")
await message.answer("Создавать приглашения могут только администраторы.")
return
bot_user = await message.bot.get_me()
token = generate_invite_token()
self.db.create_invite(token=token, admin_id=message.from_user.id, created_at=now_ts())
link = f"https://t.me/{bot_user.username}?start={token}"
text = f"Invite link created:\n{link}"
text = f"Ссылка-приглашение создана:\n{link}"
await message.answer(text)
await self.notify_admins(message.bot, f" New invite created by admin {message.from_user.id}\n{link}")
await self.notify_admins(message.bot, f" Новое приглашение создано администратором {message.from_user.id}\n{link}")
async def activate_invite(self, message: Message, token: str) -> None:
tg = message.from_user
@@ -82,14 +82,14 @@ class BotApp:
return
existing = self.db.get_by_tg_user_id(tg.id)
if existing:
await message.answer("You are already activated.", reply_markup=main_menu())
await message.answer("Вы уже активированы.", reply_markup=main_menu())
return
invite = self.db.get_by_token(token)
if invite is None:
await message.answer("Invalid invite link.")
await message.answer("Некорректная ссылка-приглашение.")
return
if invite["tg_user_id"] is not None:
await message.answer("This invite link was already used.")
await message.answer("Эта ссылка-приглашение уже использована.")
return
marzban_username = format_marzban_username(tg.id)
@@ -112,15 +112,15 @@ class BotApp:
)
user = await self.marzban.get_user(marzban_username)
except (MarzbanError, ValueError) as exc:
await message.answer(f"Activation failed: {exc}")
await self.notify_admins(message.bot, f"⚠️ Activation failed for {tg.id}: {exc}")
await message.answer(f"Не удалось активировать VPN: {exc}")
await self.notify_admins(message.bot, f"⚠️ Ошибка активации для {tg.id}: {exc}")
return
await message.answer("VPN trial activated for 3 days / 10 GB.", reply_markup=main_menu())
await message.answer("Пробный VPN активирован на 3 дня / 10 GB.", reply_markup=main_menu())
await message.answer(format_vpn_info(user))
await self.notify_admins(
message.bot,
f"User activated invite\nTelegram: {tg.first_name or ''} {tg.last_name or ''} @{tg.username or '-'}\nMarzban: {marzban_username}",
f"Пользователь активировал приглашение\nTelegram: {tg.first_name or ''} {tg.last_name or ''} @{tg.username or '-'}\nMarzban: {marzban_username}",
)
async def get_current_local_user(self, message_or_query: Message | CallbackQuery) -> dict[str, Any] | None:
@@ -132,37 +132,37 @@ class BotApp:
async def vpn_info(self, message: Message) -> None:
row = await self.get_current_local_user(message)
if not row or not row.get("marzban_username"):
await message.answer("You are not activated. Use an invite link first.")
await message.answer("Вы не активированы. Сначала откройте ссылку-приглашение.")
return
try:
user = await self.marzban.get_user(row["marzban_username"])
except MarzbanError as exc:
await message.answer(f"Could not fetch VPN info: {exc}")
await message.answer(f"Не удалось получить информацию о VPN: {exc}")
return
await message.answer(format_vpn_info(user))
async def extend_menu(self, message: Message) -> None:
row = await self.get_current_local_user(message)
if not row:
await message.answer("You are not activated. Use an invite link first.")
await message.answer("Вы не активированы. Сначала откройте ссылку-приглашение.")
return
await message.answer("Choose extension period:", reply_markup=extend_keyboard())
await message.answer("Выберите срок продления:", reply_markup=extend_keyboard())
async def select_extend(self, query: CallbackQuery) -> None:
if query.from_user is None:
return
row = self.db.get_by_tg_user_id(query.from_user.id)
if not row:
await query.message.answer("You are not activated. Use an invite link first.")
await query.message.answer("Вы не активированы. Сначала откройте ссылку-приглашение.")
await query.answer()
return
months = int(query.data.split(":", 1)[1])
amount = payment_amount(months)
self.pending_payments[query.from_user.id] = PendingPayment(months=months, amount=amount)
await query.message.answer(
f"Extension selected: {months} month(s), {amount} ₽.\n\n"
f"Выбрано продление: {months} мес., {amount} ₽.\n\n"
f"{self.config.payment_text}\n\n"
"After payment, send a screenshot/photo or file here."
"После оплаты пришлите сюда скриншот/фото или файл с подтверждением."
)
await query.answer()
@@ -174,68 +174,68 @@ class BotApp:
return
row = self.db.get_by_tg_user_id(message.from_user.id)
if not row or not row.get("marzban_username"):
await message.answer("You are not activated. Use an invite link first.")
await message.answer("Вы не активированы. Сначала откройте ссылку-приглашение.")
return
try:
user = await self.marzban.extend_user(row["marzban_username"], pending.months)
except MarzbanError as exc:
await message.answer(f"Could not extend VPN: {exc}")
await self.notify_admins(message.bot, f"⚠️ Extension failed for {row['marzban_username']}: {exc}")
await message.answer(f"Не удалось продлить VPN: {exc}")
await self.notify_admins(message.bot, f"⚠️ Ошибка продления для {row['marzban_username']}: {exc}")
return
self.pending_payments.pop(message.from_user.id, None)
await message.answer("Payment proof received. VPN was extended automatically.")
await message.answer("Подтверждение оплаты получено. VPN автоматически продлён.")
await message.answer(format_vpn_info(user))
caption = (
"💳 Payment proof received and VPN extended\n"
f"User: {user_display_name(row)}\n"
"💳 Получено подтверждение оплаты, VPN продлён\n"
f"Пользователь: {user_display_name(row)}\n"
f"Marzban: {row['marzban_username']}\n"
f"Period: {pending.months} month(s)\n"
f"Amount: {pending.amount}\n"
f"New expiration: {format_timestamp(user.get('expire'))}"
f"Период: {pending.months} мес.\n"
f"Сумма: {pending.amount}\n"
f"Новая дата окончания: {format_timestamp(user.get('expire'))}"
)
await self.forward_payment_to_admins(message, caption)
async def reset_explain(self, message: Message) -> None:
row = await self.get_current_local_user(message)
if not row:
await message.answer("You are not activated. Use an invite link first.")
await message.answer("Вы не активированы. Сначала откройте ссылку-приглашение.")
return
await message.answer(
"Reset traffic will restore your traffic limit to 50 GB.\n\n"
"Important: all days of your current partial paid month will be removed. "
"Your remaining VPN time will be rounded down to full 30-day months.\n\n"
"Example: if you have 1 month and 12 days left, after reset you will have exactly 1 month left.\n\n"
"Do you want to continue?",
"Сброс трафика восстановит лимит до 50 GB.\n\n"
"Важно: все дни текущего неполного оплаченного месяца будут удалены. "
"Оставшееся время VPN будет округлено вниз до целых периодов по 30 дней.\n\n"
"Пример: если осталось 1 месяц и 12 дней, после сброса останется ровно 1 месяц.\n\n"
"Продолжить?",
reply_markup=reset_confirm_keyboard(),
)
async def confirm_reset(self, query: CallbackQuery) -> None:
row = self.db.get_by_tg_user_id(query.from_user.id)
if not row or not row.get("marzban_username"):
await query.message.answer("You are not activated. Use an invite link first.")
await query.message.answer("Вы не активированы. Сначала откройте ссылку-приглашение.")
await query.answer()
return
try:
user, full_months, _new_expire = await self.marzban.reset_paid_traffic_with_time_penalty(row["marzban_username"])
except MarzbanError as exc:
await query.message.answer(f"Could not reset traffic: {exc}")
await query.message.answer(f"Не удалось сбросить трафик: {exc}")
await query.answer()
return
await query.message.answer(
f"Traffic reset complete. Remaining time was rounded down to {full_months} full month(s)."
f"Трафик сброшен. Оставшееся время округлено вниз до {full_months} полн. мес."
)
await query.message.answer(format_vpn_info(user))
await query.answer()
async def cancel_reset(self, query: CallbackQuery) -> None:
await query.message.answer("Reset cancelled.")
await query.message.answer("Сброс отменён.")
await query.answer()
async def save_contact(self, message: Message) -> None:
if not message.from_user or not message.contact:
return
if message.contact.user_id and message.contact.user_id != message.from_user.id:
await message.answer("Please share your own contact, not another contact.")
await message.answer("Пожалуйста, отправьте свой контакт, а не чужой.")
return
phone = message.contact.phone_number
self.db.update_phone(tg_user_id=message.from_user.id, phone=phone)
@@ -250,9 +250,9 @@ class BotApp:
try:
await self.marzban.modify_user(row["marzban_username"], {"note": note})
except MarzbanError:
await message.answer("Phone saved locally, but I could not update Marzban note now.")
await message.answer("Телефон сохранён локально, но сейчас не удалось обновить заметку в Marzban.")
return
await message.answer("Phone saved.", reply_markup=main_menu())
await message.answer("Телефон сохранён.", reply_markup=main_menu())
async def notify_admins(self, bot: Bot, text: str) -> None:
for admin_id in self.config.admin_ids: