diff --git a/database.py b/database.py index bd5c1c0e..99f35b46 100644 --- a/database.py +++ b/database.py @@ -3,7 +3,7 @@ from typing import Any import asyncpg -from config import BONUS_PERCENT, DATABASE_URL +from config import REFERRAL_BONUS_PERCENTAGES, DATABASE_URL from logger import logger @@ -638,69 +638,91 @@ async def add_referral(referred_tg_id: int, referrer_tg_id: int, session: Any): async def handle_referral_on_balance_update(tg_id: int, amount: float): """ - Обработка реферальной системы при обновлении баланса. + Обработка многоуровневой реферальной системы при обновлении баланса пользователя. - Эта функция проверяет, есть ли у пользователя реферер, - и если есть, начисляет бонус за пополнение баланса. + Метод анализирует цепочку рефералов для указанного пользователя и начисляет + бонусы рефереерам на разных уровнях согласно настроенным процентам. Args: - tg_id (int): Telegram ID пользователя + tg_id (int): Идентификатор Telegram пользователя, пополнившего баланс amount (float): Сумма пополнения баланса - Returns: - None + Raises: + Exception: В случае ошибки при работе с базой данных """ conn = None try: conn = await asyncpg.connect(DATABASE_URL) - logger.info(f"Установлено подключение к базе данных для обработки реферала пользователя {tg_id}") + logger.info(f"Начало обработки реферальной системы для пользователя {tg_id}") - referral = await conn.fetchrow( - """ - SELECT referrer_tg_id FROM referrals WHERE referred_tg_id = $1 - """, - tg_id, - ) + # Максимальное количество уровней рефералов + MAX_REFERRAL_LEVELS = len(REFERRAL_BONUS_PERCENTAGES.keys()) - if referral: - referrer_tg_id = referral["referrer_tg_id"] - logger.info(f"Найден реферер {referrer_tg_id} для пользователя {tg_id}") + # Текущий уровень для отслеживания + current_tg_id = tg_id + referral_chain = [] - bonus = amount * BONUS_PERCENT + # Собираем цепочку рефералов + for level in range(1, MAX_REFERRAL_LEVELS + 1): + # Получаем информацию о рефере текущего уровня + referral = await conn.fetchrow( + """ + SELECT referrer_tg_id + FROM referrals + WHERE referred_tg_id = $1 + """, + current_tg_id, + ) + + # Если реферер не найден, прекращаем поиск + if not referral: + break + + referrer_tg_id = referral['referrer_tg_id'] + referral_chain.append({ + 'tg_id': referrer_tg_id, + 'level': level + }) + + # Переходим к следующему уровню + current_tg_id = referrer_tg_id + + # Начисляем бонусы по цепочке рефералов + for referral in referral_chain: + referrer_tg_id = referral['tg_id'] + level = referral['level'] + + # Расчет бонуса для текущего уровня + bonus_percent = REFERRAL_BONUS_PERCENTAGES.get(level, 0) + bonus = amount * bonus_percent bonus = max(bonus, 0) # Гарантируем неотрицательный бонус - logger.info(f"Начисление бонуса {bonus} рублей рефереру {referrer_tg_id}") - await update_balance(referrer_tg_id, bonus) - - await conn.execute( - """ - UPDATE referrals SET reward_issued = TRUE - WHERE referrer_tg_id = $1 AND referred_tg_id = $2 - """, - referrer_tg_id, - tg_id, - ) - logger.info(f"Обновлен статус реферала для {referrer_tg_id}") + if bonus > 0: + logger.info(f"Начисление бонуса {bonus} рублей рефереру {referrer_tg_id} на уровне {level}") + + # Обновляем баланс реферера + await update_balance(referrer_tg_id, bonus) except Exception as e: - logger.error(f"Ошибка при обработке реферала для пользователя {tg_id}: {e}") + logger.error(f"Ошибка при обработке многоуровневой реферальной системы для {tg_id}: {e}") finally: if conn: await conn.close() - logger.info("Закрытие подключения к базе данных") async def get_referral_stats(referrer_tg_id: int): """ - Получение статистики рефералов для указанного пользователя. + Получение подробной статистики рефералов для указанного пользователя. Args: referrer_tg_id (int): Telegram ID пользователя, для которого запрашивается статистика рефералов. Returns: - dict: Словарь со статистикой рефералов, содержащий: + dict: Словарь с детальной статистикой рефералов, содержащий: - total_referrals (int): Общее количество рефералов - active_referrals (int): Количество активных рефералов (с начисленным бонусом) + - referrals_by_level (dict): Количество рефералов по каждому уровню + - total_referral_bonus (float): Общая сумма бонусов от рефералов Raises: Exception: В случае ошибки при подключении к базе данных или выполнении запроса @@ -712,6 +734,7 @@ async def get_referral_stats(referrer_tg_id: int): f"Установлено подключение к базе данных для получения статистики рефералов пользователя {referrer_tg_id}" ) + # Общее количество рефералов total_referrals = await conn.fetchval( """ SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1 @@ -720,6 +743,7 @@ async def get_referral_stats(referrer_tg_id: int): ) logger.debug(f"Получено общее количество рефералов: {total_referrals}") + # Активные рефералы active_referrals = await conn.fetchval( """ SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1 AND reward_issued = TRUE @@ -728,9 +752,61 @@ async def get_referral_stats(referrer_tg_id: int): ) logger.debug(f"Получено количество активных рефералов: {active_referrals}") + # Рефералы по уровням + referrals_by_level_records = await conn.fetch( + """ + WITH RECURSIVE referral_levels AS ( + SELECT referred_tg_id, referrer_tg_id, 1 AS level + FROM referrals + WHERE referrer_tg_id = $1 + + UNION + + SELECT r.referred_tg_id, r.referrer_tg_id, rl.level + 1 + FROM referrals r + JOIN referral_levels rl ON r.referrer_tg_id = rl.referred_tg_id + WHERE rl.level < 5 + ) + SELECT level, + COUNT(*) AS level_count, + COUNT(CASE WHEN reward_issued = TRUE THEN 1 END) AS active_level_count + FROM referral_levels rl + JOIN referrals r ON rl.referred_tg_id = r.referred_tg_id + GROUP BY level + ORDER BY level + """, + referrer_tg_id, + ) + + # Преобразование результатов в словарь + referrals_by_level = { + record['level']: { + 'total': record['level_count'], + 'active': record['active_level_count'] + } for record in referrals_by_level_records + } + logger.debug(f"Получена статистика рефералов по уровням: {referrals_by_level}") + + # Общая сумма бонусов от рефералов + total_referral_bonus = await conn.fetchval( + """ + SELECT COALESCE(SUM(amount), 0) + FROM payments + WHERE tg_id IN ( + SELECT referred_tg_id + FROM referrals + WHERE referrer_tg_id = $1 + ) AND status = 'success' + """, + referrer_tg_id, + ) + logger.debug(f"Получена общая сумма бонусов от рефералов: {total_referral_bonus}") + return { "total_referrals": total_referrals, "active_referrals": active_referrals, + "referrals_by_level": referrals_by_level, + "total_referral_bonus": total_referral_bonus, } except Exception as e: diff --git a/handlers/admin/admin_user_editor.py b/handlers/admin/admin_user_editor.py index 878834f2..c485963f 100644 --- a/handlers/admin/admin_user_editor.py +++ b/handlers/admin/admin_user_editor.py @@ -243,7 +243,7 @@ async def process_key_edit(callback_query: CallbackQuery, session: Any): return response_message = ( - f"🔑 Ключ:
{key_details['key']}\n"
+ f"🔑 Ключ: {key_details['key']}\n"
f"⏰ Дата истечения: {key_details['expiry_date']}\n"
f"💰 Баланс пользователя: {key_details['balance']}\n"
f"🌐 Сервер: {key_details['server_name']}"
@@ -295,7 +295,7 @@ async def handle_key_name_input(message: types.Message, state: FSMContext, sessi
return
response_message = (
- f"🔑 Ключ: {key_details['key']}\n"
+ f"🔑 Ключ: {key_details['key']}\n"
f"⏰ Дата истечения: {key_details['expiry_date']}\n"
f"💰 Баланс пользователя: {key_details['balance']}\n"
f"🌐 Сервер: {key_details['server_name']}"
diff --git a/handlers/notifications.py b/handlers/notifications.py
index 5432ecc5..ea4b3f97 100644
--- a/handlers/notifications.py
+++ b/handlers/notifications.py
@@ -8,7 +8,7 @@ from py3xui import AsyncApi
from client import delete_client
from config import ADMIN_PASSWORD, ADMIN_USERNAME, CLUSTERS, DATABASE_URL, TOTAL_GB, TRIAL_TIME
-from database import delete_key, get_balance, update_balance, update_key_expiry
+from database import delete_key, get_balance, update_balance, update_key_expiry#,add_notification,check_notification_time
from handlers.keys.key_utils import renew_key_in_cluster
from handlers.texts import KEY_EXPIRY_10H, KEY_EXPIRY_24H, KEY_RENEWED, RENEWAL_PLANS
from logger import logger
diff --git a/handlers/start.py b/handlers/start.py
index b199fc9a..a4b1a6f9 100644
--- a/handlers/start.py
+++ b/handlers/start.py
@@ -75,7 +75,7 @@ async def handle_connect_vpn(callback_query: CallbackQuery, session: Any):
key_message = (
f"🔑 Ваш персональный ключ доступа:\n"
- f"{trial_key_info['key']}\n\n"
+ f"{trial_key_info['key']}\n\n"
f"📋 Быстрая инструкция по подключению:\n{INSTRUCTIONS_TRIAL}"
)