Initial commit: VPN Telegram bot (sanitized defaults)

This commit is contained in:
VPN Service
2026-07-26 23:21:41 +03:00
commit aaf04faa96
77 changed files with 33249 additions and 0 deletions
+264
View File
@@ -0,0 +1,264 @@
"""Пополнение баланса с сайта (Lava / AnyPay / Heleket / Crypto Pay)."""
from __future__ import annotations
import time
from dataclasses import dataclass
from typing import Any
from services.anypay import Anypay, AnypayError
from services.cryptopay import CryptoPay, CryptoPayError
from services.db import Database
from services.heleket import Heleket, HeleketError
from services.lava import Lava, LavaError
TOPUP_MIN_RUB = 10.0
TOPUP_MAX_RUB = 3000.0
TOPUP_PRESETS_RUB: tuple[int, ...] = (50, 100, 200, 500, 1000, 2000, 3000)
PROVIDERS = ("lava", "anypay", "heleket", "cryptobot")
class TopupError(ValueError):
pass
@dataclass(frozen=True, slots=True)
class TopupInvoice:
provider: str
amount_rub: float
pay_url: str
invoice_id: int
order_id: str | None = None
title: str = ""
def parse_topup_amount(raw: Any) -> float:
try:
amount = float(raw)
except (TypeError, ValueError) as exc:
raise TopupError("Укажите сумму числом") from exc
amount = round(amount, 2)
if amount < TOPUP_MIN_RUB:
raise TopupError(f"Минимум {int(TOPUP_MIN_RUB)} ₽ за одну оплату")
if amount > TOPUP_MAX_RUB:
raise TopupError(f"Максимум {int(TOPUP_MAX_RUB)} ₽ за одну оплату")
return amount
def normalize_provider(raw: Any) -> str:
provider = str(raw or "").strip().lower()
if provider in {"crypto", "cryptopay", "crypto_bot"}:
provider = "cryptobot"
if provider not in PROVIDERS:
raise TopupError("Неизвестный способ оплаты")
return provider
def _safe_order_id(raw: str) -> str:
cleaned = "".join(ch if ch.isalnum() or ch in "_-" else "_" for ch in raw)
return cleaned[:128]
def available_providers(
*,
cryptopay: CryptoPay,
heleket: Heleket,
lava: Lava,
anypay: Anypay | None = None,
) -> list[dict[str, Any]]:
items: list[dict[str, Any]] = []
if lava.enabled:
items.append(
{
"id": "lava",
"title": "Карта / СБП · Lava",
"hint": "Lava.ru · зачисление автоматически",
}
)
if anypay and anypay.enabled:
items.append(
{
"id": "anypay",
"title": "AnyPay",
"hint": "Карта, СБП, крипта и др. · anypay.io",
}
)
if heleket.enabled:
items.append(
{
"id": "heleket",
"title": "Крипта · Heleket",
"hint": "USDT и др. · зачисление по webhook",
}
)
if cryptopay.enabled:
items.append(
{
"id": "cryptobot",
"title": "Крипта · Crypto Bot",
"hint": "Оплата в @CryptoBot",
}
)
return items
def topup_methods_payload(
*,
cryptopay: CryptoPay,
heleket: Heleket,
lava: Lava,
anypay: Anypay | None = None,
) -> dict[str, Any]:
return {
"min_amount": int(TOPUP_MIN_RUB),
"max_amount": int(TOPUP_MAX_RUB),
"presets": list(TOPUP_PRESETS_RUB),
"providers": available_providers(
cryptopay=cryptopay, heleket=heleket, lava=lava, anypay=anypay
),
}
async def create_web_topup_invoice(
*,
db: Database,
telegram_id: int,
amount_rub: float,
provider: str,
cryptopay: CryptoPay,
heleket: Heleket,
lava: Lava,
anypay: Anypay | None = None,
) -> TopupInvoice:
amount = parse_topup_amount(amount_rub)
provider = normalize_provider(provider)
stamp = int(time.time())
order_id = _safe_order_id(f"web_topup_{telegram_id}_{int(amount)}_{stamp}")
if provider == "lava":
if not lava.enabled:
raise TopupError("Lava не настроена")
try:
inv = await lava.create_invoice(
amount_rub=amount,
order_id=order_id,
comment=f"Web topup {int(amount)} RUB uid={telegram_id}",
custom_fields=str(telegram_id),
)
except LavaError as exc:
raise TopupError(str(exc)) from exc
local_id = db.next_local_invoice_id()
db.create_invoice_record(
telegram_id=telegram_id,
invoice_id=local_id,
kind="topup",
amount_rub=amount,
pay_url=inv.pay_url,
payload=None,
provider="lava",
order_id=order_id,
external_uuid=inv.invoice_id,
)
return TopupInvoice(
provider="lava",
amount_rub=amount,
pay_url=inv.pay_url,
invoice_id=local_id,
order_id=order_id,
title="Карта / СБП · Lava",
)
if provider == "anypay":
if not anypay or not anypay.enabled:
raise TopupError("AnyPay не настроен")
local_id = db.next_local_invoice_id()
pay_id = str(local_id)
try:
inv = await anypay.create_invoice(
pay_id=pay_id,
amount_rub=amount,
desc=f"Web topup {int(amount)} RUB uid={telegram_id}",
)
except AnypayError as exc:
raise TopupError(str(exc)) from exc
db.create_invoice_record(
telegram_id=telegram_id,
invoice_id=local_id,
kind="topup",
amount_rub=amount,
pay_url=inv.pay_url,
payload=pay_id,
provider="anypay",
order_id=pay_id,
external_uuid=inv.transaction_id,
)
return TopupInvoice(
provider="anypay",
amount_rub=amount,
pay_url=inv.pay_url,
invoice_id=local_id,
order_id=pay_id,
title="AnyPay",
)
if provider == "heleket":
if not heleket.enabled:
raise TopupError("Heleket не настроен")
try:
inv = await heleket.create_invoice(
amount_rub=amount,
order_id=order_id,
description=f"Web topup {int(amount)} RUB uid={telegram_id}",
)
except HeleketError as exc:
raise TopupError(str(exc)) from exc
local_id = db.next_local_invoice_id()
db.create_invoice_record(
telegram_id=telegram_id,
invoice_id=local_id,
kind="topup",
amount_rub=amount,
pay_url=inv.pay_url,
payload=order_id,
provider="heleket",
order_id=order_id,
external_uuid=inv.uuid,
)
return TopupInvoice(
provider="heleket",
amount_rub=amount,
pay_url=inv.pay_url,
invoice_id=local_id,
order_id=order_id,
title="Крипта · Heleket",
)
if not cryptopay.enabled:
raise TopupError("Crypto Bot не настроен")
payload = f"topup:{telegram_id}:{int(amount)}:web"
try:
invoice = await cryptopay.create_fiat_invoice(
amount_rub=amount,
description=f"Пополнение баланса на {int(amount)}",
payload=payload,
)
except CryptoPayError as exc:
raise TopupError(str(exc)) from exc
db.create_invoice_record(
telegram_id=telegram_id,
invoice_id=invoice.invoice_id,
kind="topup",
amount_rub=amount,
pay_url=invoice.pay_url,
payload=payload,
provider="cryptobot",
)
return TopupInvoice(
provider="cryptobot",
amount_rub=amount,
pay_url=invoice.pay_url or invoice.bot_invoice_url,
invoice_id=invoice.invoice_id,
order_id=None,
title="Крипта · Crypto Bot",
)