# - *- coding: utf- 8 - *- import json from typing import Any, Dict, Optional, Tuple, Union from aiogram import Bot from aiogram.types import CallbackQuery, Message from aiohttp import ClientConnectorCertificateError from tgbot.database import Paymentsx from tgbot.utils.const_functions import ded, to_number from tgbot.utils.misc.bot_logging import bot_logger from tgbot.utils.misc.bot_models import ARS from tgbot.utils.misc_functions import send_admins # Список поддерживаемых валют ALLOW_CURRENCIES = [ "BTC", "ETH", "LTC", "USDT", "USDC", "TRX", "TON", "BNB", "SOL", "DOGE", ] # АПИ для работы с CryptoBot class CryptobotAPI: # Настройка клиента CryptoBot def __init__( self, bot: Bot, arSession: ARS, update: Optional[Union[Message, CallbackQuery]] = None, token: str = "None", adding: bool = False, skipping_error: bool = False, ): self.bot = bot self.arSession = arSession self.update = update self.token = token self.adding = adding self.skipping_error = skipping_error # Инициализация данных @classmethod async def connect( cls, bot: Bot, arSession: ARS, update: Optional[Union[Message, CallbackQuery]] = None, token: Optional[str] = None, skipping_error: bool = False, ) -> "CryptobotAPI": adding = token is not None if token is None: payments = await Paymentsx().get() token = payments.cryptobot_token adding = False return cls( bot=bot, arSession=arSession, update=update, token=token, adding=adding, skipping_error=skipping_error, ) # Уведомления о неработоспособности кассы/кошелька async def error_notification(self, error_code: str = "Unknown"): bot_logger.warning("CryptoBot недоступен: %s", error_code) if not self.skipping_error: if self.adding and self.update is not None: await self.update.edit_text( f"🔷 Не удалось добавить CryptoBot кассу ❌\n" f"❗️ Ошибка: {error_code}" ) else: await send_admins( self.bot, f"🔷 CryptoBot недоступен. Как можно быстрее его замените\n" f"❗️ Ошибка: {error_code}", ) # Проверка кассы/кошелька async def check(self) -> Tuple[bool, str]: status, response = await self._request("getMe") if status and response["ok"]: return True, ded( f""" 🔷 CryptoBot кошелёк полностью функционирует ✅ ➖➖➖➖➖➖➖➖➖➖ ▪️ Токен: {self.token} ▪️ Айди: {response['result']['app_id']} ▪️ Имя: {response['result']['name']} """ ) return False, "🔷 Не удалось проверить CryptoBot кошелёк ❌" # Получение баланса async def balance(self) -> str: status, response = await self._request("getBalance") if status and response["ok"]: save_currencies = [] response_balances = sorted( response["result"], reverse=True, key=lambda balance: to_number(balance["available"]), ) for currency in response_balances: if currency["currency_code"] in ALLOW_CURRENCIES: save_currencies.append( f"▪️ {currency['currency_code']}: {currency['available']}" ) save_currencies = "\n".join(save_currencies) return ded(f""" 🔷 Баланс CryptoBot кошелька составляет ➖➖➖➖➖➖➖➖➖➖ {save_currencies} """) return "🔷 Не удалось получить баланс CryptoBot кошелька ❌" # Создание счета на оплату async def bill( self, pay_amount: Union[float, int] ) -> Tuple[Union[str, bool], str, str]: assets_currencies = ",".join(ALLOW_CURRENCIES) payload = { "currency_type": "fiat", "fiat": "RUB", "amount": str(pay_amount), "expires_in": 10800, "accepted_assets": assets_currencies, } status, response = await self._request("createInvoice", payload) if status and response["ok"]: bill_message = ded(f""" 💰 Пополнение баланса ➖➖➖➖➖➖➖➖➖➖ ▪️ Для пополнения баланса, нажмите на кнопку ниже Перейти к оплате и оплатите выставленный вам счёт ▪️ У вас имеется 3 часа на оплату счета ▪️ Сумма пополнения: {pay_amount}₽ ➖➖➖➖➖➖➖➖➖➖ ❗️ После оплаты, нажмите на Проверить оплату """) return ( bill_message, response["result"]["mini_app_invoice_url"], response["result"]["invoice_id"], ) return False, "", "" # Проверка счета на оплату async def bill_check( self, bill_receipt: Optional[Union[str, int]] = None, records: int = 1 ) -> Tuple[int, float]: payload = { "invoice_ids": f"{bill_receipt}", "fiat": "RUB", } status, response = await self._request("getInvoices", payload) pay_status, pay_amount = 1, 0 if status and response["ok"]: get_invoice = response["result"]["items"][0] if get_invoice["status"] == "active": pay_status = 2 elif get_invoice["status"] == "expired": pay_status = 3 else: pay_status = 0 pay_amount = to_number(get_invoice["amount"]) return pay_status, pay_amount # Генерация запроса async def _request( self, method: str, data: Optional[Dict[str, Any]] = None, ) -> Tuple[bool, Any]: session = await self.arSession.get_session() base_url = "https://pay.crypt.bot/api/" headers = { "Crypto-Pay-API-Token": self.token, "Content-Type": "application/x-www-form-urlencoded", } url = base_url + method try: response = await session.post( url=url, headers=headers, data=data, ) response_data = json.loads((await response.read()).decode()) if response.status == 200: return True, response_data else: await self.error_notification( f"{response.status} - {str(response_data)}" ) return False, response_data except ClientConnectorCertificateError: bot_logger.warning("Ошибка SSL при запросе CryptoBot", exc_info=True) await self.error_notification("CERTIFICATE_VERIFY_FAILED") return False, "CERTIFICATE_VERIFY_FAILED" except Exception as ex: bot_logger.warning("Ошибка запроса CryptoBot", exc_info=True) await self.error_notification(str(ex)) return False, str(ex)