Files

718 lines
23 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Клавиатуры бота."""
from __future__ import annotations
from aiogram.types import (
InlineKeyboardButton,
InlineKeyboardMarkup,
KeyboardButton,
ReplyKeyboardMarkup,
WebAppInfo,
)
from services.tariffs import Tariff
def main_menu(is_admin: bool = False, *, webapp_url: str | None = None) -> ReplyKeyboardMarkup:
rows: list[list[KeyboardButton]] = []
if webapp_url:
rows.append(
[
KeyboardButton(
text="🚀 Mini App",
web_app=WebAppInfo(url=webapp_url),
)
]
)
rows.extend(
[
[KeyboardButton(text="🛒 Тарифы"), KeyboardButton(text="💰 Баланс")],
[KeyboardButton(text="📱 Моя подписка"), KeyboardButton(text="📡 Статус серверов")],
[KeyboardButton(text="📥 Скачать клиент"), KeyboardButton(text="❓ FAQ")],
[KeyboardButton(text="🎫 Тикеты"), KeyboardButton(text="️ Помощь")],
]
)
if is_admin:
rows.append([KeyboardButton(text="🛠 Админка")])
return ReplyKeyboardMarkup(
keyboard=rows,
resize_keyboard=True,
input_field_placeholder="Выберите действие…",
)
def faq_root_inline() -> InlineKeyboardMarkup:
from services.faq import FAQ_ITEMS
rows: list[list[InlineKeyboardButton]] = []
for item in FAQ_ITEMS:
rows.append(
[
InlineKeyboardButton(
text=item.question,
callback_data=f"faq:view:{item.id}",
)
]
)
rows.append([InlineKeyboardButton(text="🏠 Меню", callback_data="menu:home")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def faq_back_inline() -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="⬅️ Все вопросы", callback_data="faq:root")],
[InlineKeyboardButton(text="🏠 Меню", callback_data="menu:home")],
]
)
def clients_root_inline() -> InlineKeyboardMarkup:
from services.clients import TOP_CLIENTS
rows: list[list[InlineKeyboardButton]] = []
row: list[InlineKeyboardButton] = []
for client in TOP_CLIENTS:
prefix = "⭐ " if client.featured else ""
row.append(
InlineKeyboardButton(
text=f"{prefix}{client.name}",
callback_data=f"clients:view:{client.id}",
)
)
if len(row) == 2:
rows.append(row)
row = []
if row:
rows.append(row)
rows.append(
[InlineKeyboardButton(text="📚 Все клиенты (docs.rw)", url="https://docs.rw/clients")]
)
rows.append([InlineKeyboardButton(text="🏠 Меню", callback_data="menu:home")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def clients_links_inline(links: tuple, *, back: str = "clients:root") -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = []
for link in links:
rows.append([InlineKeyboardButton(text=f"⬇️ {link.title}", url=link.url)])
rows.append([InlineKeyboardButton(text="⬅️ Назад", callback_data=back)])
return InlineKeyboardMarkup(inline_keyboard=rows)
def servers_inline() -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="🔄 Обновить", callback_data="servers:refresh")],
[InlineKeyboardButton(text="🏠 Меню", callback_data="menu:home")],
]
)
def balance_inline(
*,
crypto_enabled: bool = False,
heleket_enabled: bool = False,
digiseller_enabled: bool = False,
lava_enabled: bool = False,
anypay_enabled: bool = False,
stars_enabled: bool = False,
) -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = []
if stars_enabled:
rows.append(
[
InlineKeyboardButton(
text="⭐ Telegram Stars",
callback_data="pay:stars",
)
]
)
if crypto_enabled or heleket_enabled:
rows.append(
[InlineKeyboardButton(text="🪙 Пополнить криптой", callback_data="pay:topup")]
)
if lava_enabled:
rows.append(
[
InlineKeyboardButton(
text="💳 Lava (карта / СБП)",
callback_data="pay:lava",
)
]
)
if anypay_enabled:
rows.append(
[
InlineKeyboardButton(
text="💳 AnyPay",
callback_data="pay:anypay",
)
]
)
if digiseller_enabled:
rows.append(
[
InlineKeyboardButton(
text="💳 Digiseller",
callback_data="pay:digiseller",
)
]
)
rows.append(
[
InlineKeyboardButton(
text="🎫 Код пополнения",
callback_data="pay:topup_code",
)
]
)
rows.append([InlineKeyboardButton(text="🛒 Купить тариф", callback_data="shop:list")])
rows.append([InlineKeyboardButton(text="🏠 Меню", callback_data="menu:home")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def digiseller_inline(*, pay_url: str) -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = []
if pay_url:
rows.append([InlineKeyboardButton(text="💳 Оплатить на Digiseller", url=pay_url)])
rows.append(
[
InlineKeyboardButton(
text="🔄 Не зачислилось? Ввести код",
callback_data="pay:digiseller_code",
)
]
)
rows.append([InlineKeyboardButton(text="⬅️ Назад", callback_data="pay:balance")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def digiseller_cancel_inline() -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="❌ Отмена", callback_data="pay:digiseller_cancel")]
]
)
def topup_code_inline(*, pay_url: str = "") -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = []
if pay_url:
rows.append([InlineKeyboardButton(text="🛒 Купить код", url=pay_url)])
rows.append(
[InlineKeyboardButton(text="❌ Отмена", callback_data="pay:topup_code_cancel")]
)
return InlineKeyboardMarkup(inline_keyboard=rows)
def topup_providers_inline(
*,
crypto_enabled: bool,
heleket_enabled: bool,
) -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = []
if crypto_enabled:
rows.append(
[
InlineKeyboardButton(
text="Crypto Bot",
callback_data="pay:topup_provider:cryptobot",
)
]
)
if heleket_enabled:
rows.append(
[
InlineKeyboardButton(
text="Heleket (резерв)",
callback_data="pay:topup_provider:heleket",
)
]
)
rows.append([InlineKeyboardButton(text="⬅️ Назад", callback_data="pay:balance")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def topup_amounts_inline(provider: str = "cryptobot") -> InlineKeyboardMarkup:
amounts = [50, 100, 200, 500, 1000, 2000, 3000]
rows: list[list[InlineKeyboardButton]] = []
row: list[InlineKeyboardButton] = []
for amount in amounts:
row.append(
InlineKeyboardButton(
text=f"{amount} ₽",
callback_data=f"pay:topup_amount:{provider}:{amount}",
)
)
if len(row) == 3:
rows.append(row)
row = []
if row:
rows.append(row)
back = "pay:stars" if provider == "stars" else "pay:topup"
rows.append([InlineKeyboardButton(text="⬅️ Назад", callback_data=back)])
return InlineKeyboardMarkup(inline_keyboard=rows)
def stars_amounts_inline(*, rub_per_star: float) -> InlineKeyboardMarkup:
from services.stars import STAR_TOPUP_AMOUNTS_RUB, quote_topup
rows: list[list[InlineKeyboardButton]] = []
for amount in STAR_TOPUP_AMOUNTS_RUB:
q = quote_topup(amount, rub_per_star=rub_per_star)
rows.append(
[
InlineKeyboardButton(
text=f"{q.rub_label} · {q.stars_label}",
callback_data=f"pay:stars_amount:{amount}",
)
]
)
rows.append([InlineKeyboardButton(text="⬅️ Назад", callback_data="pay:balance")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def tariffs_inline(tariffs: list[Tariff]) -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = []
for t in tariffs:
rows.append(
[
InlineKeyboardButton(
text=f"{t.title} · {t.format_price()}",
callback_data=f"shop:view:{t.id}",
)
]
)
rows.append([InlineKeyboardButton(text="🏠 Меню", callback_data="menu:home")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def renew_tariffs_inline(tariffs: list[Tariff]) -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = []
for t in tariffs:
rows.append(
[
InlineKeyboardButton(
text=f" {t.title} · {t.format_price()}",
callback_data=f"renew:view:{t.id}",
)
]
)
rows.append([InlineKeyboardButton(text="⬅️ К подписке", callback_data="sub:mine")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def renew_confirm_inline(tariff_id: str) -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="✅ Продлить с баланса",
callback_data=f"renew:buy:{tariff_id}",
)
],
[InlineKeyboardButton(text="⬅️ Назад", callback_data="renew:list")],
]
)
def subscription_inline(
*,
has_subscription: bool,
devices_over: bool = False,
) -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = []
if has_subscription:
if devices_over:
rows.append(
[
InlineKeyboardButton(
text="⚠️ Оплатить лишние устройства",
callback_data="devices:list",
)
]
)
rows.append(
[
InlineKeyboardButton(
text="🔄 Продлить с баланса",
callback_data="renew:list",
)
]
)
rows.append(
[
InlineKeyboardButton(
text="📦 Докупить трафик",
callback_data="traffic:list",
)
]
)
rows.append(
[
InlineKeyboardButton(
text="📱 Доп. устройства",
callback_data="devices:list",
)
]
)
rows.append([InlineKeyboardButton(text="🛒 Тарифы", callback_data="shop:list")])
rows.append([InlineKeyboardButton(text="🏠 Меню", callback_data="menu:home")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def device_packs_inline(*, excess: int = 0) -> InlineKeyboardMarkup:
from services.devices import DEVICE_PACKS, EXTRA_DEVICE_PRICE_RUB
rows: list[list[InlineKeyboardButton]] = []
if excess > 0:
price = int(excess * EXTRA_DEVICE_PRICE_RUB)
rows.append(
[
InlineKeyboardButton(
text=f"⚠️ Оплатить превышение +{excess} · {price} ₽",
callback_data=f"devices:confirm:{excess}",
)
]
)
for pack in DEVICE_PACKS:
rows.append(
[
InlineKeyboardButton(
text=f"{pack.title} · {pack.format_price()}",
callback_data=f"devices:buy:{pack.slots}",
)
]
)
rows.append([InlineKeyboardButton(text="⬅️ К подписке", callback_data="sub:mine")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def device_confirm_inline(slots: int) -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="✅ Купить с баланса",
callback_data=f"devices:confirm:{slots}",
)
],
[InlineKeyboardButton(text="⬅️ Назад", callback_data="devices:list")],
]
)
def traffic_packs_inline() -> InlineKeyboardMarkup:
from services.traffic import TRAFFIC_PACKS
rows: list[list[InlineKeyboardButton]] = []
for pack in TRAFFIC_PACKS:
rows.append(
[
InlineKeyboardButton(
text=f"{pack.title} · {pack.format_price()}",
callback_data=f"traffic:buy:{pack.packs}",
)
]
)
rows.append([InlineKeyboardButton(text="⬅️ К подписке", callback_data="sub:mine")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def traffic_confirm_inline(packs: int) -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="✅ Купить с баланса",
callback_data=f"traffic:confirm:{packs}",
)
],
[InlineKeyboardButton(text="⬅️ Назад", callback_data="traffic:list")],
]
)
def tariff_pay_methods_inline(
tariff_id: str,
*,
crypto_enabled: bool,
heleket_enabled: bool = False,
lava_enabled: bool = False,
anypay_enabled: bool = False,
) -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = [
[
InlineKeyboardButton(
text="💰 С баланса",
callback_data=f"shop:buy_balance:{tariff_id}",
)
]
]
if crypto_enabled:
rows.append(
[
InlineKeyboardButton(
text="🪙 Crypto Bot",
callback_data=f"shop:buy_crypto:{tariff_id}",
)
]
)
if heleket_enabled:
rows.append(
[
InlineKeyboardButton(
text="🪙 Heleket",
callback_data=f"shop:buy_heleket:{tariff_id}",
)
]
)
if lava_enabled:
rows.append(
[
InlineKeyboardButton(
text="💳 Lava (карта / СБП)",
callback_data=f"shop:buy_lava:{tariff_id}",
)
]
)
if anypay_enabled:
rows.append(
[
InlineKeyboardButton(
text="💳 AnyPay",
callback_data=f"shop:buy_anypay:{tariff_id}",
)
]
)
rows.append([InlineKeyboardButton(text="⬅️ Назад", callback_data="shop:list")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def invoice_inline(
invoice_id: int,
pay_url: str,
*,
provider: str = "cryptobot",
) -> InlineKeyboardMarkup:
if provider == "anypay":
return InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="💳 Оплатить AnyPay", url=pay_url)],
[InlineKeyboardButton(text="🏠 Меню", callback_data="menu:home")],
]
)
if provider == "heleket":
check_cb = f"pay:check_hk:{invoice_id}"
elif provider == "lava":
check_cb = f"pay:check_lava:{invoice_id}"
else:
check_cb = f"pay:check:{invoice_id}"
return InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="🪙 Оплатить", url=pay_url)],
[
InlineKeyboardButton(
text="✅ Я оплатил — проверить",
callback_data=check_cb,
)
],
[InlineKeyboardButton(text="🏠 Меню", callback_data="menu:home")],
]
)
def admin_inline() -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(text="📡 Серверы", callback_data="admin:servers"),
InlineKeyboardButton(text="💚 Health API", callback_data="admin:health"),
],
[
InlineKeyboardButton(text="👥 Пользователи", callback_data="admin:users"),
InlineKeyboardButton(text="📊 Статистика", callback_data="admin:stats"),
],
[
InlineKeyboardButton(
text="🎫 Тикеты",
callback_data="admin:tickets",
)
],
[
InlineKeyboardButton(
text="💰 Пополнить пользователю",
callback_data="admin:topup",
)
],
[
InlineKeyboardButton(text="🔄 Обновить панель", callback_data="admin:home"),
InlineKeyboardButton(text="🏠 В меню", callback_data="menu:home"),
],
]
)
def tickets_root_inline(*, has_open: bool = False) -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = [
[InlineKeyboardButton(text="✍️ Новый тикет", callback_data="tix:new")],
[InlineKeyboardButton(text="📋 Мои тикеты", callback_data="tix:list")],
]
if has_open:
rows.insert(
1,
[InlineKeyboardButton(text="🟢 Открытые", callback_data="tix:list:open")],
)
rows.append([InlineKeyboardButton(text="🏠 Меню", callback_data="menu:home")])
return InlineKeyboardMarkup(inline_keyboard=rows)
def tickets_list_inline(
tickets: list,
*,
prefix: str = "tix:view",
back: str = "tix:root",
) -> InlineKeyboardMarkup:
from services.tickets import status_label
rows: list[list[InlineKeyboardButton]] = []
for t in tickets[:20]:
tid = int(t["id"])
st = status_label(str(t.get("status") or ""), short=True)
subj = str(t.get("subject") or "")[:28]
rows.append(
[
InlineKeyboardButton(
text=f"#{tid} · {st} · {subj}",
callback_data=f"{prefix}:{tid}",
)
]
)
rows.append([InlineKeyboardButton(text="⬅️ Назад", callback_data=back)])
return InlineKeyboardMarkup(inline_keyboard=rows)
def ticket_view_inline(ticket_id: int, *, status: str, is_admin: bool = False) -> InlineKeyboardMarkup:
rows: list[list[InlineKeyboardButton]] = []
if status != "closed":
reply_cb = f"admin:tix:reply:{ticket_id}" if is_admin else f"tix:reply:{ticket_id}"
close_cb = f"admin:tix:close:{ticket_id}" if is_admin else f"tix:close:{ticket_id}"
rows.append([InlineKeyboardButton(text="💬 Ответить", callback_data=reply_cb)])
rows.append([InlineKeyboardButton(text="🔒 Закрыть", callback_data=close_cb)])
elif is_admin:
rows.append(
[
InlineKeyboardButton(
text="🔓 Открыть снова",
callback_data=f"admin:tix:reopen:{ticket_id}",
)
]
)
back = "admin:tickets" if is_admin else "tix:list"
rows.append([InlineKeyboardButton(text="⬅️ К списку", callback_data=back)])
return InlineKeyboardMarkup(inline_keyboard=rows)
def ticket_cancel_inline(*, back: str = "tix:root") -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="❌ Отмена", callback_data=back)],
]
)
def ticket_notify_inline(ticket_id: int) -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="Открыть тикет",
callback_data=f"tix:view:{ticket_id}",
)
]
]
)
def admin_ticket_notify_inline(ticket_id: int) -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="Открыть тикет",
callback_data=f"admin:tix:view:{ticket_id}",
)
]
]
)
def admin_tickets_root_inline(stats: dict | None = None) -> InlineKeyboardMarkup:
open_n = int((stats or {}).get("open") or 0)
ans_n = int((stats or {}).get("answered") or 0)
rows = [
[
InlineKeyboardButton(
text=f"🟢 Открытые ({open_n})",
callback_data="admin:tickets:open",
)
],
[
InlineKeyboardButton(
text=f"💬 С ответом ({ans_n})",
callback_data="admin:tickets:answered",
)
],
[InlineKeyboardButton(text="📋 Все", callback_data="admin:tickets:all")],
[InlineKeyboardButton(text="⬅️ Админка", callback_data="admin:home")],
]
return InlineKeyboardMarkup(inline_keyboard=rows)
def admin_back_inline() -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(text="🔄 Обновить", callback_data="admin:servers"),
InlineKeyboardButton(text="⬅️ Назад", callback_data="admin:home"),
]
]
)
def admin_topup_cancel() -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(text="❌ Отмена", callback_data="admin:topup_cancel")]
]
)
def admin_topup_amounts(telegram_id: int) -> InlineKeyboardMarkup:
amounts = [100, 200, 500, 1000, 2000, 5000]
rows: list[list[InlineKeyboardButton]] = []
row: list[InlineKeyboardButton] = []
for amount in amounts:
row.append(
InlineKeyboardButton(
text=f"+{amount} ₽",
callback_data=f"admin:topup_do:{telegram_id}:{amount}",
)
)
if len(row) == 3:
rows.append(row)
row = []
if row:
rows.append(row)
rows.append(
[
InlineKeyboardButton(
text="✏️ Своя сумма",
callback_data=f"admin:topup_custom:{telegram_id}",
)
]
)
rows.append([InlineKeyboardButton(text="❌ Отмена", callback_data="admin:topup_cancel")])
return InlineKeyboardMarkup(inline_keyboard=rows)