Initial commit: VPN Telegram bot (sanitized defaults)
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
"""Клиент Crypto Pay API (@CryptoBot)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
class CryptoPayError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class Invoice:
|
||||
invoice_id: int
|
||||
status: str
|
||||
amount: str
|
||||
pay_url: str
|
||||
bot_invoice_url: str
|
||||
payload: str | None = None
|
||||
fiat: str | None = None
|
||||
|
||||
|
||||
class CryptoPay:
|
||||
def __init__(self, token: str, *, testnet: bool = False) -> None:
|
||||
self.token = token.strip()
|
||||
self.base_url = (
|
||||
"https://testnet-pay.crypt.bot/api"
|
||||
if testnet
|
||||
else "https://pay.crypt.bot/api"
|
||||
)
|
||||
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
return bool(self.token)
|
||||
|
||||
async def _call(self, method: str, **params: Any) -> Any:
|
||||
if not self.enabled:
|
||||
raise CryptoPayError("CRYPTO_BOT_TOKEN не задан")
|
||||
headers = {"Crypto-Pay-API-Token": self.token}
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
resp = await client.post(
|
||||
f"{self.base_url}/{method}",
|
||||
headers=headers,
|
||||
json={k: v for k, v in params.items() if v is not None},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
if not data.get("ok"):
|
||||
raise CryptoPayError(str(data.get("error") or data))
|
||||
return data["result"]
|
||||
|
||||
async def get_me(self) -> dict[str, Any]:
|
||||
return await self._call("getMe")
|
||||
|
||||
async def create_fiat_invoice(
|
||||
self,
|
||||
*,
|
||||
amount_rub: float,
|
||||
description: str,
|
||||
payload: str,
|
||||
expires_in: int = 1800,
|
||||
accepted_assets: str = "USDT,TON,BTC,ETH,LTC",
|
||||
) -> Invoice:
|
||||
result = await self._call(
|
||||
"createInvoice",
|
||||
currency_type="fiat",
|
||||
fiat="RUB",
|
||||
amount=f"{amount_rub:.2f}",
|
||||
description=description[:1024],
|
||||
payload=payload[:4096],
|
||||
accepted_assets=accepted_assets,
|
||||
expires_in=expires_in,
|
||||
allow_comments=False,
|
||||
allow_anonymous=True,
|
||||
)
|
||||
return self._parse_invoice(result)
|
||||
|
||||
async def get_invoice(self, invoice_id: int) -> Invoice | None:
|
||||
result = await self._call(
|
||||
"getInvoices",
|
||||
invoice_ids=str(invoice_id),
|
||||
)
|
||||
items = result if isinstance(result, list) else result.get("items") or []
|
||||
if not items:
|
||||
return None
|
||||
return self._parse_invoice(items[0])
|
||||
|
||||
@staticmethod
|
||||
def _parse_invoice(raw: dict[str, Any]) -> Invoice:
|
||||
pay_url = (
|
||||
raw.get("bot_invoice_url")
|
||||
or raw.get("pay_url")
|
||||
or raw.get("mini_app_invoice_url")
|
||||
or ""
|
||||
)
|
||||
return Invoice(
|
||||
invoice_id=int(raw["invoice_id"]),
|
||||
status=str(raw.get("status") or "active"),
|
||||
amount=str(raw.get("amount") or ""),
|
||||
pay_url=pay_url,
|
||||
bot_invoice_url=str(raw.get("bot_invoice_url") or pay_url),
|
||||
payload=raw.get("payload"),
|
||||
fiat=raw.get("fiat"),
|
||||
)
|
||||
Reference in New Issue
Block a user