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
+46 -12
View File
@@ -2,8 +2,19 @@
from aiogram import Dispatcher
from tgbot.routers import main_errors, main_start, main_missed
from tgbot.routers.admin import admin_menu, admin_functions, admin_payments, admin_products, admin_settings
from tgbot.routers.user import user_menu, user_transactions, user_products
from tgbot.routers.admin import (
admin_menu,
admin_functions,
admin_payments,
admin_products,
admin_settings,
)
from tgbot.routers.user import (
user_menu,
user_referral,
user_transactions,
user_products,
)
from tgbot.utils.misc.bot_filters import IsAdmin, IsPrivate
@@ -23,17 +34,39 @@ def register_all_routers(dp: Dispatcher):
user_products.router.callback_query.filter(IsPrivate())
user_transactions.router.message.filter(IsPrivate())
user_transactions.router.callback_query.filter(IsPrivate())
user_referral.router.message.filter(IsPrivate())
user_referral.router.callback_query.filter(IsPrivate())
admin_menu.router.message.filter(IsPrivate(), IsAdmin()) # Работа message роутера только для админов
admin_menu.router.callback_query.filter(IsPrivate(), IsAdmin()) # Работа callback роутера только для админов
admin_functions.router.message.filter(IsPrivate(), IsAdmin()) # Работа message роутера только для админов
admin_functions.router.callback_query.filter(IsPrivate(), IsAdmin()) # Работа callback роутера только для админов
admin_payments.router.message.filter(IsPrivate(), IsAdmin()) # Работа message роутера только для админов
admin_payments.router.callback_query.filter(IsPrivate(), IsAdmin()) # Работа callback роутера только для админов
admin_settings.router.message.filter(IsPrivate(), IsAdmin()) # Работа message роутера только для админов
admin_settings.router.callback_query.filter(IsPrivate(), IsAdmin()) # Работа callback роутера только для админов
admin_products.router.message.filter(IsPrivate(), IsAdmin()) # Работа message роутера только для админов
admin_products.router.callback_query.filter(IsPrivate(), IsAdmin()) # Работа callback роутера только для админов
admin_menu.router.message.filter(
IsPrivate(), IsAdmin()
) # Работа message роутера только для админов
admin_menu.router.callback_query.filter(
IsPrivate(), IsAdmin()
) # Работа callback роутера только для админов
admin_functions.router.message.filter(
IsPrivate(), IsAdmin()
) # Работа message роутера только для админов
admin_functions.router.callback_query.filter(
IsPrivate(), IsAdmin()
) # Работа callback роутера только для админов
admin_payments.router.message.filter(
IsPrivate(), IsAdmin()
) # Работа message роутера только для админов
admin_payments.router.callback_query.filter(
IsPrivate(), IsAdmin()
) # Работа callback роутера только для админов
admin_settings.router.message.filter(
IsPrivate(), IsAdmin()
) # Работа message роутера только для админов
admin_settings.router.callback_query.filter(
IsPrivate(), IsAdmin()
) # Работа callback роутера только для админов
admin_products.router.message.filter(
IsPrivate(), IsAdmin()
) # Работа message роутера только для админов
admin_products.router.callback_query.filter(
IsPrivate(), IsAdmin()
) # Работа callback роутера только для админов
# Подключение обязательных роутеров
dp.include_router(main_errors.router) # Роутер ошибки
@@ -44,6 +77,7 @@ def register_all_routers(dp: Dispatcher):
dp.include_router(admin_menu.router) # Админ роутер
dp.include_router(user_products.router) # Юзер роутер
dp.include_router(user_transactions.router) # Юзер роутер
dp.include_router(user_referral.router) # Юзер роутер
dp.include_router(admin_functions.router) # Админ роутер
dp.include_router(admin_payments.router) # Админ роутер
dp.include_router(admin_settings.router) # Админ роутер
+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>")
+94 -1
View File
@@ -1,11 +1,13 @@
# - *- coding: utf- 8 - *-
from math import isfinite
from aiogram import Router, Bot, F
from aiogram.filters import StateFilter
from aiogram.types import CallbackQuery, Message
from tgbot.database import Settingsx, Userx
from tgbot.keyboards.inline_admin import settings_status_finl, settings_finl
from tgbot.utils.const_functions import ded
from tgbot.utils.const_functions import ded, is_number, to_number
from tgbot.utils.misc.bot_logging import bot_logger
from tgbot.utils.misc.bot_models import FSM, ARS
from tgbot.utils.misc_functions import send_admins, insert_tags
@@ -116,6 +118,31 @@ async def settings_status_refill(
await call.message.edit_reply_markup(reply_markup=await settings_status_finl())
# Включение/выключение реферальной системы
@router.callback_query(F.data.startswith("settings_status_referral:"))
async def settings_status_referral(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_status = call.data.split(":")[1]
get_user = await Userx().get_required(user_id=call.from_user.id)
await Settingsx().update(status_referral=get_status)
if get_status == "True":
send_text = "🟢 Включил реферальную систему в боте"
else:
send_text = "🔴 Выключил реферальную систему в боте"
await send_admins(
bot,
f"👤 Администратор <a href='tg://user?id={get_user.user_id}'>{get_user.user_name}</a>\n"
f"{send_text}",
not_me=get_user.user_id,
)
await call.message.edit_reply_markup(reply_markup=await settings_status_finl())
# Включение/выключение уведомлений о покупках
@router.callback_query(F.data.startswith("settings_notification_buy:"))
async def settings_notification_buy(
@@ -267,6 +294,72 @@ async def settings_faq_get(message: Message, bot: Bot, state: FSM, arSession: AR
# Принятие поддержки
@router.callback_query(F.data == "settings_edit_referral_bonus")
async def settings_edit_referral_bonus(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
await state.clear()
await state.set_state("here_settings_referral_bonus")
await call.message.edit_text(
"<b>🤝 Введите фиксированный бонус за приглашение в рублях</b>"
)
@router.callback_query(F.data == "settings_edit_referral_percent")
async def settings_edit_referral_percent(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
await state.clear()
await state.set_state("here_settings_referral_percent")
await call.message.edit_text(
"<b>📈 Введите процент дохода с пополнений от 0 до 100</b>"
)
@router.message(F.text, StateFilter("here_settings_referral_bonus"))
async def settings_referral_bonus_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
if not is_number(message.text):
return await message.answer(
"<b>❌ Введите неотрицательное число в рублях</b>"
)
referral_bonus = float(to_number(message.text))
if not isfinite(referral_bonus) or referral_bonus < 0:
return await message.answer(
"<b>❌ Введите неотрицательное число в рублях</b>"
)
await Settingsx().update(referral_bonus_rub=round(referral_bonus, 2))
await state.clear()
await message.answer(
"<b>🖍 Изменение данных бота</b>",
reply_markup=await settings_finl(),
)
@router.message(F.text, StateFilter("here_settings_referral_percent"))
async def settings_referral_percent_get(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
if not is_number(message.text):
return await message.answer("<b>❌ Введите число от 0 до 100</b>")
referral_percent = float(to_number(message.text))
if not isfinite(referral_percent) or not 0 <= referral_percent <= 100:
return await message.answer("<b>❌ Введите число от 0 до 100</b>")
await Settingsx().update(referral_refill_percent=round(referral_percent, 2))
await state.clear()
await message.answer(
"<b>🖍 Изменение данных бота</b>",
reply_markup=await settings_finl(),
)
@router.message(F.text, StateFilter("here_settings_support"))
async def settings_support_get(message: Message, bot: Bot, state: FSM, arSession: ARS):
get_support = message.text
+3 -1
View File
@@ -124,7 +124,9 @@ async def main_start(message: Message, bot: Bot, state: FSM, arSession: ARS):
async def main_start_deeplink(message: Message, bot: Bot, state: FSM, arSession: ARS):
deepling_args = message.text[7:]
if deepling_args.startswith("p_"):
if deepling_args.startswith("r_"):
await main_start(message, bot, state, arSession)
elif deepling_args.startswith("p_"):
position_id_raw = deepling_args[2:]
if not position_id_raw.isdigit():
+359
View File
@@ -0,0 +1,359 @@
# - *- coding: utf- 8 - *-
from math import isfinite
from urllib.parse import urlparse
import re
from aiogram import Bot, F, Router
from aiogram.filters import StateFilter
from aiogram.types import CallbackQuery, Message
from tgbot.database import ReferralWithdrawalx, Referralx, Settingsx, Userx
from tgbot.keyboards.inline_admin import referral_withdrawal_actions_finl
from tgbot.keyboards.inline_user import (
referral_menu_finl,
referral_transfer_method_finl,
referral_withdrawal_method_finl,
)
from tgbot.utils.const_functions import clear_html, ded, is_number, to_number
from tgbot.utils.misc.bot_models import ARS, FSM
from tgbot.utils.misc_functions import send_admins
router = Router(name=__name__)
def is_valid_withdrawal_recipient(withdrawal_method: str, recipient: str) -> bool:
if withdrawal_method == "Cryptobot":
return bool(re.fullmatch(r"@[A-Za-z][A-Za-z0-9_]{4,31}", recipient))
if withdrawal_method == "Lolzteam":
parsed_url = urlparse(recipient)
return parsed_url.scheme in ("http", "https") and parsed_url.hostname in (
"zelenka.guru",
"lolz.team",
"lolz.live",
"lolz.guru",
)
return False
def withdrawal_recipient_hint(withdrawal_method: str) -> str:
if withdrawal_method == "Cryptobot":
return (
"<b>📥 Введите @username получателя в CryptoBot</b>\n"
"➖➖➖➖➖➖➖➖➖➖\n"
"<i>Пример: @username</i>"
)
return (
"<b>📥 Введите ссылку на профиль получателя Lolzteam</b>\n"
"➖➖➖➖➖➖➖➖➖➖\n"
"<i>Подойдут ссылки с zelenka.guru, lolz.team, lolz.live или lolz.guru</i>"
)
def withdrawal_method_title(withdrawal_method: str) -> str:
if withdrawal_method == "Cryptobot":
return "CryptoBot"
return withdrawal_method
async def get_referral_menu_data(user_id: int, bot: Bot):
get_settings = await Settingsx().get()
if get_settings.status_referral != "True":
return None
get_user = await Userx().get_required(user_id=user_id)
get_referrals = await Referralx().gets(referrer_id=user_id)
bot_username = get_settings.misc_bot
if bot_username == "None":
bot_username = (await bot.get_me()).username or ""
referral_link = f"https://t.me/{bot_username}?start=r_{user_id}"
available_balance = round(
get_user.user_referral_balance - get_user.user_referral_hold,
2,
)
referral_text = ded(f"""
<b>🤝 Реферальная система</b>
➖➖➖➖➖➖➖➖➖➖
▪️ Приглашено пользователей: <code>{len(get_referrals)}</code>
▪️ Реферальный баланс: <code>{get_user.user_referral_balance}₽</code>
▪️ Доступно: <code>{available_balance}₽</code>
➖➖➖➖➖➖➖➖➖➖
▪️ Бонус за приглашение: <code>{get_settings.referral_bonus_rub}₽</code>
▪️ Доход с пополнений: <code>{get_settings.referral_refill_percent}%</code>
""")
return referral_text, referral_menu_finl(referral_link)
@router.message(F.text == "🤝 Реферальная система")
async def show_referral_menu(
message: Message,
bot: Bot,
state: FSM,
arSession: ARS,
user_id: int | None = None,
):
await state.clear()
user_id = user_id or message.from_user.id
referral_menu_data = await get_referral_menu_data(user_id, bot)
if referral_menu_data is None:
return await message.answer("<b>⛔ Реферальная система временно отключена</b>")
referral_text, referral_keyboard = referral_menu_data
await message.answer(
referral_text,
reply_markup=referral_keyboard,
disable_web_page_preview=True,
)
@router.callback_query(F.data == "referral_menu")
async def user_referral_menu_return(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
await state.clear()
referral_menu_data = await get_referral_menu_data(call.from_user.id, bot)
if referral_menu_data is None:
return await call.answer("⛔ Реферальная система временно отключена", True)
referral_text, referral_keyboard = referral_menu_data
await call.message.edit_text(
referral_text,
reply_markup=referral_keyboard,
disable_web_page_preview=True,
)
@router.callback_query(F.data == "referral_transfer")
async def user_referral_transfer_start(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_settings = await Settingsx().get()
if get_settings.status_referral != "True":
await state.clear()
return await call.answer("⛔ Реферальная система временно отключена", True)
await state.clear()
await state.set_state("here_referral_transfer_amount")
await call.message.edit_text(
"<b>💸 Введите сумму для перевода на основной баланс</b>",
reply_markup=referral_transfer_method_finl(),
)
@router.callback_query(F.data == "referral_withdrawal")
async def user_referral_withdrawal_start(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
get_settings = await Settingsx().get()
if get_settings.status_referral != "True":
await state.clear()
return await call.answer("⛔ Реферальная система временно отключена", True)
await state.clear()
await call.message.edit_text(
"<b>📥 Выберите способ вывода средств</b>",
reply_markup=referral_withdrawal_method_finl(),
)
@router.callback_query(F.data.startswith("referral_withdrawal_method:"))
async def user_referral_withdrawal_method(
call: CallbackQuery, bot: Bot, state: FSM, arSession: ARS
):
withdrawal_method = call.data.split(":", 1)[1]
if withdrawal_method not in ("Cryptobot", "Lolzteam"):
return await call.answer("❌ Способ вывода не найден", True)
get_settings = await Settingsx().get()
if get_settings.status_referral != "True":
await state.clear()
return await call.answer("⛔ Реферальная система временно отключена", True)
await state.clear()
await state.update_data(here_referral_withdrawal_method=withdrawal_method)
await state.set_state("here_referral_withdrawal_amount")
await call.message.edit_text("<b>📥 Введите сумму для вывода</b>")
@router.message(F.text, StateFilter("here_referral_withdrawal_amount"))
async def user_referral_withdrawal_amount(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
get_settings = await Settingsx().get()
if get_settings.status_referral != "True":
await state.clear()
return await message.answer("<b>⛔ Реферальная система временно отключена</b>")
if not is_number(message.text):
return await message.answer("<b>❌ Введите положительную сумму в рублях</b>")
withdrawal_amount = float(to_number(message.text))
if not isfinite(withdrawal_amount) or withdrawal_amount <= 0:
return await message.answer("<b>❌ Введите положительную сумму в рублях</b>")
get_user = await Userx().get_required(user_id=message.from_user.id)
available_balance = round(
get_user.user_referral_balance - get_user.user_referral_hold, 2
)
if withdrawal_amount > available_balance:
return await message.answer(
"<b>❌ Недостаточно доступных средств на реферальном балансе</b>"
)
withdrawal_data = await state.get_data()
withdrawal_method = withdrawal_data.get("here_referral_withdrawal_method")
if withdrawal_method not in ("Cryptobot", "Lolzteam"):
await state.clear()
return await message.answer("<b>❌ Данные вывода устарели. Начните заново</b>")
await state.update_data(here_referral_withdrawal_amount=withdrawal_amount)
await state.set_state("here_referral_withdrawal_recipient")
await message.answer(withdrawal_recipient_hint(withdrawal_method))
@router.message(F.text, StateFilter("here_referral_withdrawal_recipient"))
async def user_referral_withdrawal_recipient(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
get_settings = await Settingsx().get()
if get_settings.status_referral != "True":
await state.clear()
return await message.answer("<b>⛔ Реферальная система временно отключена</b>")
withdrawal_data = await state.get_data()
withdrawal_method = withdrawal_data.get("here_referral_withdrawal_method")
withdrawal_amount = withdrawal_data.get("here_referral_withdrawal_amount")
if withdrawal_method is None or withdrawal_amount is None:
await state.clear()
return await message.answer("<b>❌ Данные вывода устарели. Начните заново</b>")
withdrawal_recipient = clear_html(message.text).strip()
if not withdrawal_recipient:
return await message.answer(
"<b>❌ Введите реквизиты</b>\n"
f"{withdrawal_recipient_hint(withdrawal_method)}"
)
if not is_valid_withdrawal_recipient(withdrawal_method, withdrawal_recipient):
return await message.answer(
"<b>❌ Некорректные реквизиты</b>\n"
f"{withdrawal_recipient_hint(withdrawal_method)}"
)
withdrawal_status, withdrawal = await ReferralWithdrawalx().create_pending(
user_id=message.from_user.id,
withdrawal_amount=withdrawal_amount,
withdrawal_method=withdrawal_method,
withdrawal_recipient=withdrawal_recipient,
)
if withdrawal_status == "INSUFFICIENT_FUNDS":
return await message.answer(
"<b>❌ Недостаточно доступных средств на реферальном балансе</b>"
)
if withdrawal_status in ("INVALID_AMOUNT", "INVALID_RECIPIENT", "INVALID_METHOD"):
return await message.answer("<b>❌ Некорректные данные для вывода</b>")
if withdrawal_status == "USER_NOT_FOUND":
await state.clear()
return await message.answer(
"<b>❌ Пользователь не найден. Обратитесь в поддержку</b>"
)
if withdrawal_status != "ok" or withdrawal is None:
return await message.answer(
"<b>❌ Не удалось создать заявку. Попробуйте позже</b>"
)
await state.clear()
get_user = await Userx().get_required(user_id=message.from_user.id)
method_title = withdrawal_method_title(withdrawal.withdrawal_method)
await message.answer(
ded(f"""
<b>📥 Заявка на вывод создана</b>
➖➖➖➖➖➖➖➖➖➖
▪️ Номер заявки: <code>#{withdrawal.increment}</code>
▪️ Сумма: <code>{withdrawal.withdrawal_amount}₽</code>
▪️ Способ: <code>{method_title}</code>
"""),
)
await send_admins(
bot,
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>
"""),
keyboard=referral_withdrawal_actions_finl(withdrawal.increment),
)
@router.message(F.text, StateFilter("here_referral_transfer_amount"))
async def user_referral_transfer_amount(
message: Message, bot: Bot, state: FSM, arSession: ARS
):
get_settings = await Settingsx().get()
if get_settings.status_referral != "True":
await state.clear()
return await message.answer("<b>⛔ Реферальная система временно отключена</b>")
if not is_number(message.text):
return await message.answer("<b>❌ Введите положительную сумму в рублях</b>")
transfer_amount = float(to_number(message.text))
if not isfinite(transfer_amount) or transfer_amount <= 0:
return await message.answer("<b>❌ Введите положительную сумму в рублях</b>")
transfer_status = await Referralx().transfer_to_main_balance(
user_id=message.from_user.id,
amount=transfer_amount,
)
if transfer_status == "INSUFFICIENT_FUNDS":
return await message.answer(
"<b>❌ Недостаточно доступных средств на реферальном балансе</b>"
)
if transfer_status == "INVALID_AMOUNT":
return await message.answer("<b>❌ Введите положительную сумму в рублях</b>")
if transfer_status == "USER_NOT_FOUND":
await state.clear()
return await message.answer(
"<b>❌ Пользователь не найден. Обратитесь в поддержку</b>"
)
if transfer_status != "ok":
return await message.answer(
"<b>❌ Не удалось выполнить перевод. Попробуйте позже</b>"
)
await state.clear()
get_user = await Userx().get_required(user_id=message.from_user.id)
await message.answer(
ded(f"""
<b>💸 Средства переведены на основной баланс</b>
➖➖➖➖➖➖➖➖➖➖
▪️ Переведено: <code>{round(transfer_amount, 2)}₽</code>
▪️ Основной баланс: <code>{get_user.user_balance}₽</code>
▪️ Реферальный баланс: <code>{get_user.user_referral_balance}₽</code>
"""),
)
+23 -1
View File
@@ -5,7 +5,7 @@ from aiogram import Router, Bot, F
from aiogram.filters import StateFilter
from aiogram.types import CallbackQuery, Message, PreCheckoutQuery
from tgbot.database import Paymentsx, Refillx, Userx, Settingsx
from tgbot.database import Paymentsx, ReferralTransactionx, 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
@@ -459,4 +459,26 @@ async def save_refill_success(
"""),
)
referral_reward = await ReferralTransactionx().get_refill_reward(
f"refill:{pay_comment or pay_receipt}"
)
if referral_reward is not None:
try:
await bot.send_message(
referral_reward.user_id,
ded(f"""
<b>🤝 Реферальное начисление</b>
▪️ Ваш реферал: <a href='tg://user?id={get_user.user_id}'>{get_user.user_name}</a>
▪️ Пополнение: <code>{pay_amount}₽</code>
▪️ Начислено: <code>{referral_reward.amount}₽</code>
"""),
)
except Exception:
bot_logger.warning(
"Не удалось отправить уведомление о реферальном начислении",
exc_info=True,
)
return response_success, pay_receipt