translate to rus
This commit is contained in:
+46
-46
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user