diff --git a/tgbot/data/config.py b/tgbot/data/config.py
index 80126d0..ed2077f 100644
--- a/tgbot/data/config.py
+++ b/tgbot/data/config.py
@@ -168,23 +168,4 @@ def validate_bot_config() -> None:
def get_text_desc() -> str:
from tgbot.utils.const_functions import ded
- return ded(f"""
- ♻️ Версия бота: {BOT_VERSION}
- 👑 Разработчик бота - @djimbox
- 🍩 Донат разработчику: Click me
- 🤖 Новости и обновления: Click me
- 🔗 Тема с ботом [LOLZ]: Click me
- """).strip()
-
-
-# Получение варнинг-текста
-def get_text_warning() -> str:
- from tgbot.utils.const_functions import ded
-
- return ded(f"""
- ❗️ Если вы видите данное сообщение, значит Администратор бота не изменил текст установленный
- по умолчанию. Скорее всего данный бот занимается скамом, обманом или другими схожими действиями.
-
- ❗️ Если вас обманули, не надо писать РАЗРАБОТЧИКУ бота, он ничем не сможет помочь. Бот является
- открытым и распространяется публично. Абсолютно любой желающий мог его запустить.
- """)
+ return ded(f"""♻️ Версия бота: {BOT_VERSION}""").strip()
diff --git a/tgbot/database/db_payments.py b/tgbot/database/db_payments.py
index 4811101..9f5cd50 100644
--- a/tgbot/database/db_payments.py
+++ b/tgbot/database/db_payments.py
@@ -3,6 +3,7 @@ from typing import Any, Dict, Optional
from sqlalchemy import Integer, String, Float
from sqlalchemy.orm import Mapped, mapped_column
+from sqlalchemy.ext.asyncio import AsyncConnection
from tgbot.database.core import Base
from tgbot.database.entities import Payments
@@ -22,11 +23,81 @@ class PaymentsModel(Base):
String(16), nullable=False, default="False"
)
+ # Оплата через Lolzteam
+ lolzteam_token: Mapped[str] = mapped_column(String, nullable=False, default="None")
+ lolzteam_merchant_id: Mapped[Optional[int]] = mapped_column(
+ Integer, nullable=True, default=None
+ )
+ status_lolzteam: Mapped[str] = mapped_column(
+ String(16), nullable=False, default="False"
+ )
+
ModelBase = Payments
BaseModel = Payments
+async def ensure_payments_schema(conn: AsyncConnection) -> None:
+ columns = (
+ await conn.exec_driver_sql("PRAGMA table_info(storage_payments)")
+ ).mappings().all()
+
+ if not columns:
+ return
+
+ merchant_column = next(
+ (column for column in columns if column["name"] == "lolzteam_merchant_id"),
+ None,
+ )
+
+ if merchant_column is None or merchant_column["notnull"] == 0:
+ return
+
+ await conn.exec_driver_sql(
+ "ALTER TABLE storage_payments RENAME TO storage_payments__legacy"
+ )
+ await conn.exec_driver_sql(
+ """
+ CREATE TABLE storage_payments (
+ id INTEGER NOT NULL,
+ cryptobot_token VARCHAR NOT NULL,
+ stars_course FLOAT NOT NULL,
+ status_cryptobot VARCHAR(16) NOT NULL,
+ status_stars VARCHAR(16) NOT NULL,
+ lolzteam_token VARCHAR NOT NULL,
+ lolzteam_merchant_id INTEGER,
+ status_lolzteam VARCHAR(16) NOT NULL,
+ PRIMARY KEY (id)
+ )
+ """
+ )
+ await conn.exec_driver_sql(
+ """
+ INSERT INTO storage_payments (
+ id,
+ cryptobot_token,
+ stars_course,
+ status_cryptobot,
+ status_stars,
+ lolzteam_token,
+ lolzteam_merchant_id,
+ status_lolzteam
+ )
+ SELECT
+ id,
+ cryptobot_token,
+ stars_course,
+ status_cryptobot,
+ status_stars,
+ lolzteam_token,
+ lolzteam_merchant_id,
+ status_lolzteam
+ FROM storage_payments__legacy
+ """
+ )
+ await conn.exec_driver_sql("DROP TABLE storage_payments__legacy")
+
+
class Paymentsx(BaseRepository[PaymentsModel, Payments]):
# Подключение модели платежных настроек
def __init__(self):
diff --git a/tgbot/database/entities.py b/tgbot/database/entities.py
index 2a04f86..a022b7f 100644
--- a/tgbot/database/entities.py
+++ b/tgbot/database/entities.py
@@ -1,5 +1,6 @@
# - *- coding: utf- 8 - *-
from dataclasses import dataclass
+from typing import Optional
@dataclass
@@ -40,6 +41,12 @@ class Payments:
status_cryptobot: str
status_stars: str
+ # Оплата через Lolzteam
+ lolzteam_token: str
+ lolzteam_merchant_id: Optional[int]
+
+ status_lolzteam: str
+
@dataclass
class Purchase:
diff --git a/tgbot/database/repository.py b/tgbot/database/repository.py
index 341b9d3..dddf6db 100644
--- a/tgbot/database/repository.py
+++ b/tgbot/database/repository.py
@@ -162,6 +162,10 @@ async def prepare_database() -> None:
import tgbot.database.db_users # noqa: F401
async with engine.begin() as conn:
+ from tgbot.database.db_payments import ensure_payments_schema
+
+ await ensure_payments_schema(conn)
+
await conn.run_sync(Base.metadata.create_all)
from tgbot.database.db_payments import Paymentsx
diff --git a/tgbot/keyboards/inline_admin.py b/tgbot/keyboards/inline_admin.py
index 1cf4ec2..7fe60b5 100644
--- a/tgbot/keyboards/inline_admin.py
+++ b/tgbot/keyboards/inline_admin.py
@@ -98,6 +98,43 @@ async def payment_cryptobot_finl() -> InlineKeyboardMarkup:
return keyboard.as_markup()
+# Управление - Lolzteam
+async def payment_lolzteam_finl() -> InlineKeyboardMarkup:
+ keyboard = InlineKeyboardBuilder()
+
+ get_payments = await Paymentsx().get()
+
+ if get_payments.lolzteam_token == "None":
+ assets_symbol = "➖"
+ else:
+ assets_symbol = "➕"
+
+ if get_payments.status_lolzteam == "True":
+ status_kb = ikb(
+ f"{assets_symbol} | Статус: Включено ✅",
+ data="payment_lolzteam_status:False",
+ )
+ else:
+ status_kb = ikb(
+ f"{assets_symbol} | Статус: Выключено ❌",
+ data="payment_lolzteam_status:True",
+ )
+
+ keyboard.row(
+ ikb("Информация ♻️", data="payment_lolzteam_check"),
+ ).row(
+ ikb("Баланс 💰", data="payment_lolzteam_balance"),
+ ).row(
+ ikb("Изменить 🖍", data="payment_lolzteam_edit"),
+ ).row(
+ ikb("", data="..."),
+ ).row(
+ status_kb,
+ )
+
+ return keyboard.as_markup()
+
+
# Управление - Звёзды
async def payment_stars_finl() -> InlineKeyboardMarkup:
keyboard = InlineKeyboardBuilder()
diff --git a/tgbot/keyboards/inline_admin_products.py b/tgbot/keyboards/inline_admin_products.py
index b62598e..a6d14bb 100644
--- a/tgbot/keyboards/inline_admin_products.py
+++ b/tgbot/keyboards/inline_admin_products.py
@@ -23,7 +23,7 @@ async def category_edit_open_finl(
).row(
ikb(
"▪️ Скопировать ссылку",
- copy=f"t.me/{get_bot.username}?start=c_{category_id}",
+ copy=f"telegram.me/{get_bot.username}?start=c_{category_id}",
),
ikb("▪️ Удалить", data=f"category_edit_delete:{category_id}:{remover}"),
).row(
@@ -85,7 +85,7 @@ async def position_edit_open_finl(
).row(
ikb(
"▪️ Скопировать ссылку",
- copy=f"t.me/{get_bot.username}?start=p_{position_id}",
+ copy=f"telegram.me/{get_bot.username}?start=p_{position_id}",
),
ikb("▪️ Удалить Позицию", data=f"position_edit_delete:{position_id}:{remover}"),
).row(
diff --git a/tgbot/keyboards/inline_user.py b/tgbot/keyboards/inline_user.py
index 9b720c1..9141e17 100644
--- a/tgbot/keyboards/inline_user.py
+++ b/tgbot/keyboards/inline_user.py
@@ -27,7 +27,7 @@ def user_support_finl(support_login: str) -> InlineKeyboardMarkup:
keyboard = InlineKeyboardBuilder()
keyboard.row(
- ikb("💌 Написать в поддержку", url=f"https://t.me/{support_login}"),
+ ikb("💌 Написать в поддержку", url=f"https://telegram.me/{support_login}"),
)
return keyboard.as_markup()
@@ -43,6 +43,8 @@ async def refill_method_finl() -> InlineKeyboardMarkup:
if get_payments.status_cryptobot == "True":
keyboard.row(ikb("🔷 CryptoBot", data="user_refill_method:Cryptobot"))
+ if get_payments.status_lolzteam == "True":
+ keyboard.row(ikb("🟢 Lolzteam", data="user_refill_method:Lolzteam"))
if get_payments.status_stars == "True":
keyboard.row(ikb("⭐️ Звёзды", data="user_refill_method:Stars"))
@@ -74,6 +76,8 @@ async def refill_method_buy_finl() -> InlineKeyboardMarkup:
if get_payments.status_cryptobot == "True":
keyboard.row(ikb("🔷 CryptoBot", data="user_refill_method:Cryptobot"))
+ if get_payments.status_lolzteam == "True":
+ keyboard.row(ikb("🟢 Lolzteam", data="user_refill_method:Lolzteam"))
if get_payments.status_stars == "True":
keyboard.row(ikb("⭐️ Звёзды", data="user_refill_method:Stars"))
diff --git a/tgbot/keyboards/reply_main.py b/tgbot/keyboards/reply_main.py
index 78a6d5e..52c68bc 100644
--- a/tgbot/keyboards/reply_main.py
+++ b/tgbot/keyboards/reply_main.py
@@ -38,6 +38,7 @@ def payments_frep() -> ReplyKeyboardMarkup:
keyboard.row(
rkb("🔷 CryptoBot"),
+ rkb("🟢 Lolzteam"),
rkb("⭐️ Звёзды"),
).row(
rkb("🔙 Главное меню"),
diff --git a/tgbot/routers/admin/admin_payments.py b/tgbot/routers/admin/admin_payments.py
index d927015..358df22 100644
--- a/tgbot/routers/admin/admin_payments.py
+++ b/tgbot/routers/admin/admin_payments.py
@@ -7,9 +7,11 @@ from tgbot.database import Paymentsx
from tgbot.keyboards.inline_admin import (
close_finl,
payment_cryptobot_finl,
+ payment_lolzteam_finl,
payment_stars_finl,
)
from tgbot.services.api_cryptobot import CryptobotAPI
+from tgbot.services.api_lolzteam import LolzteamAPI
from tgbot.services.api_stars import StarsAPI
from tgbot.utils.const_functions import ded, is_number, to_number
from tgbot.utils.misc.bot_models import FSM, ARS
@@ -30,6 +32,19 @@ async def payments_cryptobot_open(
)
+# Управление - Lolzteam
+@router.message(F.text == "🟢 Lolzteam")
+async def payments_cryptobot_open(
+ message: Message, bot: Bot, state: FSM, arSession: ARS
+):
+ await state.clear()
+
+ await message.answer(
+ "🟢 Управление - Lolzteam",
+ reply_markup=await payment_lolzteam_finl(),
+ )
+
+
# Управление - Звёзды
@router.message(F.text == "⭐️ Звёзды")
async def payments_stars_open(message: Message, bot: Bot, state: FSM, arSession: ARS):
@@ -161,6 +176,163 @@ async def payments_cryptobot_get(
)
+################################################################################
+################################### LOLZTEAM ##################################
+# Баланс - Lolzteam
+@router.callback_query(F.data == "payment_lolzteam_balance")
+async def payments_lolzteam_balance(
+ call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
+):
+ response = await (
+ await LolzteamAPI.connect(
+ bot=bot,
+ arSession=arSession,
+ update=call,
+ skipping_error=True,
+ )
+ ).balance()
+
+ await call.message.answer(
+ response,
+ reply_markup=close_finl(),
+ )
+
+
+# Информация - Lolzteam
+@router.callback_query(F.data == "payment_lolzteam_check")
+async def payment_lolzteam_check(
+ call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
+):
+ status, response = await (
+ await LolzteamAPI.connect(
+ bot=bot,
+ arSession=arSession,
+ update=call,
+ skipping_error=True,
+ )
+ ).check()
+
+ await call.message.answer(
+ response,
+ reply_markup=close_finl(),
+ )
+
+
+# Изменение - Lolzteam
+@router.callback_query(F.data == "payment_lolzteam_edit")
+async def payment_lolzteam_edit(
+ call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
+):
+ await state.set_state("here_lolzteam_token")
+ await call.message.edit_text(
+ ded(f"""
+ 🟢 Изменение Lolzteam - API
+ ➖➖➖➖➖➖➖➖➖➖
+ ▪️ Создайте Приложение в "Lolzteam", сгенерируйте токен (basic, invoice, market) и отправьте
+ """),
+ disable_web_page_preview=True,
+ )
+
+
+# Выключатель - Lolzteam
+@router.callback_query(F.data.startswith("payment_lolzteam_status:"))
+async def payment_lolzteam_status(
+ call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
+):
+ get_status = call.data.split(":")[1]
+ get_payments = await Paymentsx().get()
+
+ if get_status == "True" and get_payments.lolzteam_token == "None":
+ return await call.answer(
+ "❌ Токен данной платежной системы не был добавлен", True
+ )
+
+ await Paymentsx().update(status_lolzteam=get_status)
+
+ await call.message.edit_text(
+ "🟢 Управление - Lolzteam",
+ reply_markup=await payment_lolzteam_finl(),
+ )
+
+
+############################## ПРИНЯТИЕ LOLZTEAM ##############################
+# Принятие токена Lolzteam
+@router.message(StateFilter("here_lolzteam_token"))
+async def payments_lolzteam_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
+ get_token = message.text
+
+ cache_message = await message.answer(
+ "🟢 Проверка введённых Lolzteam данных... 🔄"
+ )
+
+ status, response = await (
+ await LolzteamAPI.connect(
+ bot=bot,
+ arSession=arSession,
+ update=message,
+ skipping_error=True,
+ token=get_token,
+ )
+ ).check()
+
+ if status:
+ await Paymentsx().update(lolzteam_token=get_token)
+
+ await state.set_state("here_lolzteam_merchant_id")
+ await cache_message.edit_text(
+ ded(f"""
+ 🟢 Изменение Lolzteam - Мерчанты
+ ➖➖➖➖➖➖➖➖➖➖
+ ▪️ Создайте мерчант в "Lolzteam Market" и отправьте его ID
+ """),
+ disable_web_page_preview=True,
+ )
+
+ else:
+ await cache_message.edit_text(
+ "🟢 Не удалось изменить Lolzteam кошелёк ❌"
+ )
+
+
+# Принятие айди мерчанта Lolzteam
+@router.message(StateFilter("here_lolzteam_merchant_id"))
+async def payments_lolzteam_merchant_id_get(
+ message: Message, bot: Bot, state: FSM, arSession: ARS
+):
+ get_payments = await Paymentsx().get()
+ get_token = get_payments.lolzteam_token
+
+ get_merchant_id = message.text
+
+ await state.clear()
+
+ cache_message = await message.answer(
+ "🟢 Проверка введённых Lolzteam данных... 🔄"
+ )
+
+ status, response = await (
+ await LolzteamAPI.connect(
+ bot=bot,
+ arSession=arSession,
+ update=message,
+ skipping_error=True,
+ token=get_token,
+ merchant_id=get_merchant_id,
+ )
+ ).check()
+
+ if status:
+ await Paymentsx().update(lolzteam_merchant_id=get_merchant_id)
+ await cache_message.edit_text("🟢 Lolzteam был успешно изменён ✅")
+ else:
+ await cache_message.edit_text("🟢 Не удалось изменить Lolzteam ❌")
+
+ await message.answer(
+ "🟢 Управление - Lolzteam",
+ reply_markup=await payment_lolzteam_finl(),
+ )
+
+
################################################################################
#################################### ЗВЁЗДЫ ####################################
# Баланс - Звёзды
diff --git a/tgbot/routers/user/user_transactions.py b/tgbot/routers/user/user_transactions.py
index 0e96387..4d0b497 100644
--- a/tgbot/routers/user/user_transactions.py
+++ b/tgbot/routers/user/user_transactions.py
@@ -8,6 +8,7 @@ from aiogram.types import CallbackQuery, Message, PreCheckoutQuery
from tgbot.database import Paymentsx, Refillx, Userx, Settingsx
from tgbot.keyboards.inline_user import refill_bill_finl, refill_method_finl
from tgbot.services.api_cryptobot import CryptobotAPI
+from tgbot.services.api_lolzteam import LolzteamAPI
from tgbot.services.api_stars import StarsAPI
from tgbot.utils.const_functions import is_number, to_number, gen_id, ded
from tgbot.utils.misc.bot_logging import bot_logger
@@ -26,6 +27,7 @@ async def refill_method_list(call: CallbackQuery, bot: Bot, state: FSM, arSessio
if (
get_payments.status_cryptobot == "False"
+ and get_payments.status_lolzteam == "False"
and get_payments.status_stars == "False"
):
return await call.answer("❗️ Пополнения временно недоступны", True)
@@ -49,6 +51,10 @@ async def refill_method_select(
return await call.answer(
"❌ Пополнение данным способом временно недоступно", True
)
+ elif refill_method == "Lolzteam" and get_payments.status_lolzteam == "False":
+ return await call.answer(
+ "❌ Пополнение данным способом временно недоступно", True
+ )
elif refill_method == "Stars" and get_payments.status_stars == "False":
return await call.answer(
"❌ Пополнение данным способом временно недоступно", True
@@ -98,6 +104,15 @@ async def refill_amount_get(message: Message, bot: Bot, state: FSM, arSession: A
)
).bill(refill_amount)
+ elif refill_method == "Lolzteam":
+ bill_message, bill_link, bill_receipt = await (
+ await LolzteamAPI.connect(
+ bot=bot,
+ arSession=arSession,
+ update=cache_message,
+ )
+ ).bill(refill_amount)
+
elif refill_method == "Stars":
bill_message, bill_link, bill_receipt = await (
await StarsAPI.connect(
@@ -106,6 +121,7 @@ async def refill_amount_get(message: Message, bot: Bot, state: FSM, arSession: A
update=cache_message,
)
).bill(refill_amount)
+
else:
return await cache_message.edit_text(
f"❌ Данный способ пополнения не найден. Попробуйте позже: {refill_method}"
@@ -180,6 +196,59 @@ async def refill_check_cryptobot(
)
+# Проверка оплаты - Lolzteam
+@router.callback_query(F.data.startswith("Pay:Lolzteam"))
+async def refill_check_lolzteam(
+ call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
+):
+ pay_method = call.data.split(":")[1]
+ pay_comment = call.data.split(":")[2]
+
+ pay_status, pay_amount = await (
+ await LolzteamAPI.connect(
+ bot=bot,
+ arSession=arSession,
+ update=call,
+ )
+ ).bill_check(pay_comment)
+
+ if pay_status == 0:
+ refill_status = await refill_success(
+ bot=bot,
+ call=call,
+ pay_method=pay_method,
+ pay_amount=pay_amount,
+ pay_comment=pay_comment,
+ )
+
+ if refill_status == "ALREADY":
+ await call.answer("❗ Ваше пополнение уже зачислено.", True, cache_time=60)
+ await delete_refill_message(
+ bot, call.message.chat.id, call.message.message_id
+ )
+ elif refill_status == "USER_NOT_FOUND":
+ await call.answer(
+ "❗ Пользователь не найден. Напишите в поддержку.", True, cache_time=30
+ )
+ elif pay_status == 1:
+ await call.answer(
+ "❗️ Не удалось проверить платёж. Попробуйте позже", True, cache_time=30
+ )
+ elif pay_status == 2:
+ await call.answer(
+ "❗️ Оплата не была найдена. Попробуйте позже", True, cache_time=5
+ )
+ elif pay_status == 3:
+ await call.answer("❗️ Вы не успели оплатить счёт", True, cache_time=5)
+ await call.message.edit_reply_markup()
+ else:
+ await call.answer(
+ f"❗ Неизвестная ошибка {pay_status}. Обратитесь в поддержку.",
+ True,
+ cache_time=5,
+ )
+
+
# Проверка оплаты - Звёзды
@router.callback_query(F.data.startswith("Pay:Stars"))
async def refill_check_stars(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
@@ -357,6 +426,8 @@ async def save_refill_success(
) -> Tuple[str, int]:
if pay_method == "Cryptobot":
text_method = "CryptoBot"
+ elif pay_method == "Lolzteam":
+ text_method = "Lolzteam"
elif pay_method == "Stars":
text_method = "Telegram Stars"
else:
diff --git a/tgbot/services/api_cryptobot.py b/tgbot/services/api_cryptobot.py
index 3ae3f02..6cbb5b0 100644
--- a/tgbot/services/api_cryptobot.py
+++ b/tgbot/services/api_cryptobot.py
@@ -13,20 +13,31 @@ 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']
+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,
+ arSession: ARS,
+ update: Optional[Union[Message, CallbackQuery]] = None,
+ token: str = "None",
+ adding: bool = False,
+ skipping_error: bool = False,
):
self.bot = bot
self.arSession = arSession
@@ -38,12 +49,12 @@ class CryptobotAPI:
# Инициализация данных
@classmethod
async def connect(
- cls,
- bot: Bot,
- arSession: ARS,
- update: Optional[Union[Message, CallbackQuery]] = None,
- token: Optional[str] = None,
- skipping_error: bool = False,
+ 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
@@ -75,21 +86,23 @@ class CryptobotAPI:
await send_admins(
self.bot,
f"🔷 CryptoBot недоступен. Как можно быстрее его замените\n"
- f"❗️ Ошибка: {error_code}"
+ 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"""
+ if status and response["ok"]:
+ return True, ded(
+ f"""
🔷 CryptoBot кошелёк полностью функционирует ✅
➖➖➖➖➖➖➖➖➖➖
▪️ Токен: {self.token}
▪️ Айди: {response['result']['app_id']}
▪️ Имя: {response['result']['name']}
- """)
+ """
+ )
return False, "🔷 Не удалось проверить CryptoBot кошелёк ❌"
@@ -97,17 +110,17 @@ class CryptobotAPI:
async def balance(self) -> str:
status, response = await self._request("getBalance")
- if status and response['ok']:
+ if status and response["ok"]:
save_currencies = []
response_balances = sorted(
- response['result'],
+ response["result"],
reverse=True,
- key=lambda balance: to_number(balance['available']),
+ key=lambda balance: to_number(balance["available"]),
)
for currency in response_balances:
- if currency['currency_code'] in ALLOW_CURRENCIES:
+ if currency["currency_code"] in ALLOW_CURRENCIES:
save_currencies.append(
f"▪️ {currency['currency_code']}: {currency['available']}"
)
@@ -123,24 +136,26 @@ class CryptobotAPI:
return "🔷 Не удалось получить баланс CryptoBot кошелька ❌"
# Создание счета на оплату
- async def bill(self, pay_amount: Union[float, int]) -> Tuple[Union[str, bool], str, str]:
+ 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
+ "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']:
+ if status and response["ok"]:
bill_message = ded(f"""
💰 Пополнение баланса
➖➖➖➖➖➖➖➖➖➖
- ▪️ Для пополнения баланса, нажмите на кнопку ниже
+ ▪️ Для пополнения баланса, нажмите на кнопку ниже
Перейти к оплате и оплатите выставленный вам счёт
▪️ У вас имеется 3 часа на оплату счета
▪️ Сумма пополнения: {pay_amount}₽
@@ -148,46 +163,52 @@ class CryptobotAPI:
❗️ После оплаты, нажмите на Проверить оплату
""")
- return bill_message, response['result']['mini_app_invoice_url'], response['result']['invoice_id']
+ 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]:
+ 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',
+ "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 status and response["ok"]:
+ get_invoice = response["result"]["items"][0]
- if get_invoice['status'] == "active":
+ if get_invoice["status"] == "active":
pay_status = 2
- elif get_invoice['status'] == "expired":
+ elif get_invoice["status"] == "expired":
pay_status = 3
else:
pay_status = 0
- pay_amount = to_number(get_invoice['amount'])
+ pay_amount = to_number(get_invoice["amount"])
return pay_status, pay_amount
# Генерация запроса
async def _request(
- self,
- method: str,
- data: Optional[Dict[str, Any]] = None,
+ 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/'
+ base_url = "https://pay.crypt.bot/api/"
headers = {
- 'Crypto-Pay-API-Token': self.token,
- 'Content-Type': 'application/x-www-form-urlencoded',
+ "Crypto-Pay-API-Token": self.token,
+ "Content-Type": "application/x-www-form-urlencoded",
}
url = base_url + method
@@ -204,7 +225,9 @@ class CryptobotAPI:
if response.status == 200:
return True, response_data
else:
- await self.error_notification(f"{response.status} - {str(response_data)}")
+ await self.error_notification(
+ f"{response.status} - {str(response_data)}"
+ )
return False, response_data
except ClientConnectorCertificateError:
diff --git a/tgbot/services/api_lolzteam.py b/tgbot/services/api_lolzteam.py
new file mode 100644
index 0000000..c24efe8
--- /dev/null
+++ b/tgbot/services/api_lolzteam.py
@@ -0,0 +1,294 @@
+# - *- 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, get_unix, gen_id
+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 = [
+ "rub",
+ "uah",
+ "kzt",
+ "byn",
+ "usd",
+ "eur",
+ "gbp",
+ "cny",
+ "try",
+ "jpy",
+ "brl",
+]
+
+
+# АПИ для работы с Lolzteam
+class LolzteamAPI:
+ # Настройка клиента Lolzteam
+ def __init__(
+ self,
+ bot: Bot,
+ arSession: ARS,
+ update: Optional[Union[Message, CallbackQuery]] = None,
+ token: str = "None",
+ merchant_id: int = "None",
+ adding: bool = False,
+ skipping_error: bool = False,
+ ):
+ self.bot = bot
+ self.arSession = arSession
+ self.update = update
+ self.token = token
+ self.merchant_id = merchant_id
+ 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,
+ merchant_id: Optional[int] = None,
+ skipping_error: bool = False,
+ ) -> "LolzteamAPI":
+ adding = token is not None
+
+ if token is None or merchant_id is None:
+ payments = await Paymentsx().get()
+ adding = False
+
+ if token is None:
+ token = payments.lolzteam_token
+
+ if merchant_id is None:
+ merchant_id = payments.lolzteam_merchant_id
+
+ return cls(
+ bot=bot,
+ arSession=arSession,
+ update=update,
+ token=token,
+ merchant_id=merchant_id,
+ adding=adding,
+ skipping_error=skipping_error,
+ )
+
+ # Уведомления о неработоспособности кассы/кошелька
+ async def error_notification(self, error_code: str = "Unknown"):
+ bot_logger.warning("Lolzteam недоступен: %s", error_code)
+
+ if not self.skipping_error:
+ if self.adding and self.update is not None:
+ await self.update.edit_text(
+ f"🟢 Не удалось добавить Lolzteam кассу ❌\n"
+ f"❗️ Ошибка: {error_code}"
+ )
+ else:
+ await send_admins(
+ self.bot,
+ f"🟢 Lolzteam недоступен. Как можно быстрее его замените\n"
+ f"❗️ Ошибка: {error_code}",
+ )
+
+ # Проверка кассы/кошелька
+ async def check(self) -> Tuple[bool, str]:
+ status, response = await self._request("me", method="GET")
+ merchant_valid = False
+
+ if status and "errors" not in response and self.merchant_id:
+ balances = response["user"]["balances"]
+
+ for balance in balances:
+ if int(self.merchant_id) == balance["merchant_id"]:
+ merchant_valid = True
+
+ if merchant_valid:
+ return True, ded(
+ f"""
+ 🟢 Lolzteam полностью функционирует ✅
+ ➖➖➖➖➖➖➖➖➖➖
+ ▪️ Токен: {self.token}
+ ▪️ Айди: {response['user']['user_id']}
+ ▪️ Айди Мерчанта: {self.merchant_id}
+ """
+ )
+
+ elif status and "errors" not in response and not self.merchant_id:
+ return True, ded(
+ f"""
+ 🟢 Lolzteam полностью функционирует ✅
+ ➖➖➖➖➖➖➖➖➖➖
+ ▪️ Токен: {self.token}
+ ▪️ Айди: {response['user']['user_id']}
+ """
+ )
+
+ return False, "🟢 Не удалось проверить Lolzteam ❌"
+
+ # Получение баланса
+ async def balance(self) -> str:
+ status, response = await self._request("me", method="GET")
+
+ if status and "errors" not in response:
+ merchants = []
+
+ response_balances = sorted(
+ response["user"]["balances"],
+ reverse=True,
+ key=lambda balance: to_number(balance["balance"]),
+ )
+
+ for balance in response_balances:
+ if balance["merchant_id"] == self.merchant_id:
+ title = balance["title"]
+ custom_title = balance["custom_title"]
+
+ merchants.append(
+ f"▪️ {title if not custom_title else custom_title}: {balance['balance']} ₽"
+ )
+
+ merchants = "\n".join(merchants)
+
+ return ded(f"""
+ 🟢 Баланс Lolzteam составляет
+ ➖➖➖➖➖➖➖➖➖➖
+ {merchants}
+ """)
+
+ return "🟢 Не удалось получить баланс Lolzteam ❌"
+
+ # Создание счета на оплату
+ async def bill(
+ self, pay_amount: Union[float, int]
+ ) -> Tuple[Union[str, bool], str, str]:
+
+ pro_payment_id = get_unix()
+ eu_payment_id = gen_id(12)
+
+ payment_id = str(pro_payment_id) + str(eu_payment_id)
+
+ bot_data = await self.bot.get_me()
+ bot_username = bot_data.username
+ bot_url = f"https://telegram.me/{bot_username}"
+
+ payload = {
+ "currency": "rub",
+ "amount": pay_amount,
+ "payment_id": payment_id,
+ "comment": f"Пополнение #{payment_id}",
+ "url_success": bot_url,
+ "merchant_id": self.merchant_id,
+ "lifetime": 10800,
+ "is_test": True, # Тестовый режим для отладки
+ }
+
+ status, response = await self._request("invoice", payload)
+
+ if status and "errors" not in response:
+ bill_message = ded(f"""
+ 💰 Пополнение баланса
+ ➖➖➖➖➖➖➖➖➖➖
+ ▪️ Для пополнения баланса, нажмите на кнопку ниже
+ Перейти к оплате и оплатите выставленный вам счёт
+ ▪️ У вас имеется 3 часа на оплату счета
+ ▪️ Сумма пополнения: {pay_amount}₽
+ ➖➖➖➖➖➖➖➖➖➖
+ ❗️ После оплаты, нажмите на Проверить оплату
+ """)
+
+ return (
+ bill_message,
+ response["invoice"]["url"],
+ response["invoice"]["invoice_id"],
+ )
+
+ return False, "", ""
+
+ # Проверка счета на оплату
+ async def bill_check(
+ self, bill_receipt: Optional[Union[str, int]] = None, records: int = 1
+ ) -> Tuple[int, float]:
+ payload = {"invoice_id": int(bill_receipt)}
+
+ status, response = await self._request("invoice", payload, method="GET")
+
+ pay_status, pay_amount = 1, 0
+
+ if status and "errors" not in response:
+ get_invoice = response["invoice"]
+ status = get_invoice["status"]
+
+ time = response["system_info"]["time"]
+ expires_at = get_invoice["expires_at"]
+
+ # not_paid + time < expires_at
+ if (status == "not_paid") and (time < expires_at):
+ pay_status = 2
+
+ # not_paid + time > expires_at
+ elif (status == "expired") and (time > expires_at):
+ pay_status = 3
+
+ # paid
+ else:
+ pay_status = 0
+ pay_amount = to_number(get_invoice["amount"])
+
+ return pay_status, pay_amount
+
+ # Генерация запроса
+ async def _request(
+ self,
+ path: str,
+ data: Optional[Dict[str, Any]] = None,
+ method: str = "POST",
+ ) -> Tuple[bool, Any]:
+ session = await self.arSession.get_session()
+ method = method.upper()
+
+ base_url = "https://prod-api.lzt.market/"
+ headers = {
+ "Authorization": f"Bearer {self.token}",
+ "Accept": "application/json",
+ }
+
+ url = base_url + path
+
+ try:
+ if method == "POST":
+ response = await session.post(url=url, headers=headers, data=data)
+
+ elif method == "GET":
+ response = await session.get(url=url, headers=headers, params=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 при запросе Lolzteam", exc_info=True)
+ await self.error_notification("CERTIFICATE_VERIFY_FAILED")
+
+ return False, "CERTIFICATE_VERIFY_FAILED"
+
+ except Exception as ex:
+ bot_logger.warning("Ошибка запроса Lolzteam", exc_info=True)
+ await self.error_notification(str(ex))
+
+ return False, str(ex)
diff --git a/tgbot/utils/const_functions.py b/tgbot/utils/const_functions.py
index 7795ea4..871668f 100644
--- a/tgbot/utils/const_functions.py
+++ b/tgbot/utils/const_functions.py
@@ -48,7 +48,7 @@ def ikb(
if copy is not None:
return InlineKeyboardButton(text=text, copy_text=CopyTextButton(text=copy))
if login is not None:
- return InlineKeyboardButton(text=text, url=f"https://t.me/{login}")
+ return InlineKeyboardButton(text=text, url=f"https://telegram.me/{login}")
raise ValueError("Не указано действие для inline-кнопки")