feat(referral): add referral system with withdrawals

This commit is contained in:
2026-07-18 10:58:14 +05:00
parent 5e5bab91f2
commit a04734b8a7
17 changed files with 1750 additions and 60 deletions
+175 -23
View File
@@ -5,18 +5,137 @@ from aiogram import Router, Bot, F
from aiogram.filters import StateFilter
from aiogram.types import CallbackQuery, Message
from tgbot.database import Purchasesx, Refillx, Userx
from tgbot.database import Purchasesx, Refillx, ReferralWithdrawalx, Userx
from tgbot.keyboards.inline_admin import profile_edit_return_finl, mail_confirm_finl
from tgbot.services.api_hosting_text import HostingAPI
from tgbot.utils.const_functions import is_number, to_number, del_message, ded, clear_html, convert_date
from tgbot.utils.const_functions import (
is_number,
to_number,
del_message,
ded,
clear_html,
convert_date,
)
from tgbot.utils.misc.bot_logging import bot_logger
from tgbot.utils.misc.bot_models import FSM, ARS
from tgbot.utils.misc_functions import functions_mail_make
from tgbot.utils.text_functions import open_profile_admin, refill_open_admin, purchase_open_admin
from tgbot.utils.text_functions import (
open_profile_admin,
refill_open_admin,
purchase_open_admin,
)
router = Router(name=__name__)
def withdrawal_method_title(withdrawal_method: str) -> str:
if withdrawal_method == "Cryptobot":
return "CryptoBot"
return withdrawal_method
@router.callback_query(F.data.startswith("referral_withdrawal_complete:"))
async def referral_withdrawal_complete(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
withdrawal_id_raw = call.data.split(":", 1)[1]
if not withdrawal_id_raw.isdigit():
return await call.answer("❌ Заявка не найдена", True)
status, withdrawal = await ReferralWithdrawalx().complete(
withdrawal_id=int(withdrawal_id_raw),
admin_id=call.from_user.id,
)
if status == "ALREADY_PROCESSED":
return await call.answer("❌ Заявка уже обработана", True)
if status != "ok" or withdrawal is None:
return await call.answer("❌ Не удалось обработать заявку", True)
method_title = withdrawal_method_title(withdrawal.withdrawal_method)
get_user = await Userx().get_required(user_id=withdrawal.user_id)
await call.message.edit_text(
ded(f"""
<b>✅ Заявка на вывод выполнена</b>
▪️ Номер заявки: <code>#{withdrawal.increment}</code>
▪️ Пользователь: <b>@{get_user.user_login}</b> | <a href='tg://user?id={get_user.user_id}'>{get_user.user_name}</a> | <code>{get_user.user_id}</code>
▪️ Сумма: <code>{withdrawal.withdrawal_amount}₽</code>
▪️ Способ: <code>{method_title}</code>
▪️ Реквизиты: <code>{withdrawal.withdrawal_recipient}</code>
"""),
reply_markup=None,
)
try:
await bot.send_message(
withdrawal.user_id,
ded(f"""
<b>✅ Заявка на вывод выполнена</b>
▪️ Номер заявки: <code>#{withdrawal.increment}</code>
▪️ Сумма: <code>{withdrawal.withdrawal_amount}₽</code>
▪️ Способ: <code>{method_title}</code>
"""),
)
except Exception:
bot_logger.warning(
"Не удалось отправить пользователю уведомление о выполненном выводе",
exc_info=True,
)
await call.answer("✅ Заявка выполнена", True)
@router.callback_query(F.data.startswith("referral_withdrawal_reject:"))
async def referral_withdrawal_reject(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
withdrawal_id_raw = call.data.split(":", 1)[1]
if not withdrawal_id_raw.isdigit():
return await call.answer("❌ Заявка не найдена", True)
status, withdrawal = await ReferralWithdrawalx().reject(
withdrawal_id=int(withdrawal_id_raw),
admin_id=call.from_user.id,
)
if status == "ALREADY_PROCESSED":
return await call.answer("❌ Заявка уже обработана", True)
if status != "ok" or withdrawal is None:
return await call.answer("❌ Не удалось обработать заявку", True)
get_user = await Userx().get_required(user_id=withdrawal.user_id)
method_title = withdrawal_method_title(withdrawal.withdrawal_method)
await call.message.edit_text(
ded(f"""
<b>❌ Заявка на вывод отклонена</b>
▪️ Номер заявки: <code>#{withdrawal.increment}</code>
▪️ Пользователь: <b>@{get_user.user_login}</b> | <a href='tg://user?id={get_user.user_id}'>{get_user.user_name}</a> | <code>{get_user.user_id}</code>
▪️ Сумма возвращена на реферальный баланс: <code>{withdrawal.withdrawal_amount}₽</code>
▪️ Способ: <code>{method_title}</code>
▪️ Реквизиты: <code>{withdrawal.withdrawal_recipient}</code>
"""),
reply_markup=None,
)
try:
await bot.send_message(
withdrawal.user_id,
ded(f"""
<b>❌ Заявка на вывод отклонена</b>
▪️ Номер заявки: <code>#{withdrawal.increment}</code>
▪️ Сумма возвращена на реферальный баланс: <code>{withdrawal.withdrawal_amount}₽</code>
"""),
)
except Exception:
bot_logger.warning(
"Не удалось отправить пользователю уведомление об отклонённом выводе",
exc_info=True,
)
await call.answer("❌ Заявка отклонена", True)
# Поиск чеков и профилей
@router.message(F.text == "🔍 Поиск")
async def functions_find(message: Message, bot: Bot, state: FSM, arSession: ARS):
@@ -55,17 +174,23 @@ async def functions_mail_get(message: Message, bot: Bot, state: FSM, arSession:
# Подтверждение отправки рассылки
@router.callback_query(F.data.startswith("mail_confirm:"), StateFilter("here_mail_confirm"))
async def functions_mail_confirm(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
@router.callback_query(
F.data.startswith("mail_confirm:"), StateFilter("here_mail_confirm")
)
async def functions_mail_confirm(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_status = call.data.split(":")[1]
send_message = (await state.get_data())['here_mail_message']
send_message = (await state.get_data())["here_mail_message"]
await state.clear()
if get_status == "Yes":
get_users = await Userx().get_all()
await call.message.edit_text(f"<b>📢 Рассылка началась... (0/{len(get_users)})</b>")
await call.message.edit_text(
f"<b>📢 Рассылка началась... (0/{len(get_users)})</b>"
)
await asyncio.create_task(functions_mail_make(bot, send_message, call))
else:
@@ -76,7 +201,7 @@ async def functions_mail_confirm(call: CallbackQuery, bot: Bot, state: FSM, arSe
##################################### ПОИСК ####################################
# Принятие айди/логина пользователя или чека для поиска
@router.message(F.text, StateFilter("here_find"))
@router.message(F.text.lower().startswith(('.find', 'find')))
@router.message(F.text.lower().startswith((".find", "find")))
async def functions_find_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
find_data = message.text.lower()
@@ -116,14 +241,18 @@ async def functions_find_get(message: Message, bot: Bot, state: FSM, arSession:
return await refill_open_admin(bot, message.from_user.id, get_refill)
if get_purchase is not None:
return await purchase_open_admin(bot, arSession, message.from_user.id, get_purchase)
return await purchase_open_admin(
bot, arSession, message.from_user.id, get_purchase
)
################################################################################
############################## УПРАВЛЕНИЕ ПРОФИЛЕМ #############################
# Обновление профиля пользователя
@router.callback_query(F.data.startswith("admin_user_refresh:"))
async def functions_user_refresh(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def functions_user_refresh(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
user_id = int(call.data.split(":")[1])
get_user = await Userx().get_required(user_id=user_id)
@@ -136,7 +265,9 @@ async def functions_user_refresh(call: CallbackQuery, bot: Bot, state: FSM, arSe
# Покупки пользователя
@router.callback_query(F.data.startswith("admin_user_purchases:"))
async def functions_user_purchases(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def functions_user_purchases(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
user_id = int(call.data.split(":")[1])
get_user = await Userx().get_required(user_id=user_id)
@@ -174,7 +305,9 @@ async def functions_user_purchases(call: CallbackQuery, bot: Bot, state: FSM, ar
# Выдача баланса пользователю
@router.callback_query(F.data.startswith("admin_user_balance_add:"))
async def functions_user_balance_add(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def functions_user_balance_add(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
user_id = int(call.data.split(":")[1])
await state.update_data(here_user=user_id)
@@ -188,8 +321,10 @@ async def functions_user_balance_add(call: CallbackQuery, bot: Bot, state: FSM,
# Принятие суммы для выдачи баланса пользователю
@router.message(F.text, StateFilter("here_user_add"))
async def functions_user_balance_add_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
user_id = (await state.get_data())['here_user']
async def functions_user_balance_add_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
user_id = (await state.get_data())["here_user"]
if not is_number(message.text):
return await message.answer(
@@ -222,7 +357,11 @@ async def functions_user_balance_add_get(message: Message, bot: Bot, state: FSM,
f"<b>💰 Вам было выдано <code>{message.text}₽</code></b>",
)
except Exception:
bot_logger.debug("Не удалось уведомить пользователя %s о выдаче баланса", user_id, exc_info=True)
bot_logger.debug(
"Не удалось уведомить пользователя %s о выдаче баланса",
user_id,
exc_info=True,
)
await message.answer(
f"👤 Пользователь: <a href='tg://user?id={get_user.user_id}'>{get_user.user_name}</a>\n"
@@ -235,7 +374,9 @@ async def functions_user_balance_add_get(message: Message, bot: Bot, state: FSM,
# Изменение баланса пользователю
@router.callback_query(F.data.startswith("admin_user_balance_set:"))
async def functions_user_balance_set(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def functions_user_balance_set(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
user_id = int(call.data.split(":")[1])
await state.update_data(here_user=user_id)
@@ -249,8 +390,10 @@ async def functions_user_balance_set(call: CallbackQuery, bot: Bot, state: FSM,
# Принятие суммы для изменения баланса пользователя
@router.message(F.text, StateFilter("here_user_set"))
async def functions_user_balance_set_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
user_id = (await state.get_data())['here_user']
async def functions_user_balance_set_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
user_id = (await state.get_data())["here_user"]
if not is_number(message.text):
return await message.answer(
@@ -294,7 +437,9 @@ async def functions_user_balance_set_get(message: Message, bot: Bot, state: FSM,
# Отправка сообщения пользователю
@router.callback_query(F.data.startswith("admin_user_message:"))
async def functions_user_user_message(call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS):
async def functions_user_user_message(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
user_id = int(call.data.split(":")[1])
await state.update_data(here_user_id=user_id)
@@ -309,17 +454,24 @@ async def functions_user_user_message(call: CallbackQuery, bot: Bot, state: FSM,
# Принятие сообщения для отправки пользователю
@router.message(F.text, StateFilter("here_user_message"))
async def functions_user_user_message_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
user_id = (await state.get_data())['here_user_id']
async def functions_user_user_message_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
user_id = (await state.get_data())["here_user_id"]
await state.clear()
get_message = "<b>💌 Сообщение от администратора:</b>\n" + f"<code>{clear_html(message.text)}</code>"
get_message = (
"<b>💌 Сообщение от администратора:</b>\n"
+ f"<code>{clear_html(message.text)}</code>"
)
get_user = await Userx().get_required(user_id=user_id)
try:
await bot.send_message(user_id, get_message)
except Exception:
bot_logger.debug("Не удалось отправить сообщение пользователю %s", user_id, exc_info=True)
bot_logger.debug(
"Не удалось отправить сообщение пользователю %s", user_id, exc_info=True
)
await message.reply("<b>❌ Не удалось отправить сообщение</b>")
else:
await message.reply("<b>✅ Сообщение было успешно доставлено</b>")