diff --git a/database/statistics.py b/database/statistics.py index bea5295b..c60d3cc3 100644 --- a/database/statistics.py +++ b/database/statistics.py @@ -34,11 +34,15 @@ async def count_active_keys(session: AsyncSession) -> int: async def count_trial_keys(session: AsyncSession) -> int: - subquery_success_payments = ( - select(Payment.tg_id).where(and_(Payment.tg_id == Key.tg_id, Payment.status == "success")).exists() + trial_tariffs_subquery = ( + select(Tariff.id).where(Tariff.group_code == "trial") + ) + + return await session.scalar( + select(func.count()) + .select_from(Key) + .where(Key.tariff_id.in_(trial_tariffs_subquery)) ) - - return await session.scalar(select(func.count()).select_from(Key).where(not_(subquery_success_payments))) async def get_tariff_distribution( diff --git a/web/__init__.py b/web/__init__.py index 8dd0f32b..83180644 100644 --- a/web/__init__.py +++ b/web/__init__.py @@ -1,10 +1,7 @@ from aiohttp.web_urldispatcher import UrlDispatcher -from config import TBLOCKER_WEBHOOK_PATH - from .heleket_payment import heleket_payment_webhook from .kassai_payment import kassai_payment_webhook -from .tblocker import tblocker_webhook from .wata_payment import wata_payment_webhook from utils.modules_loader import load_module_webhooks @@ -15,7 +12,6 @@ HELEKET_WEBHOOK_PATH = "/heleket/webhook" async def register_web_routes(router: UrlDispatcher) -> None: - router.add_post(TBLOCKER_WEBHOOK_PATH, tblocker_webhook) router.add_post(WATA_WEBHOOK_PATH, wata_payment_webhook) router.add_post(KASSAI_WEBHOOK_PATH, kassai_payment_webhook) router.add_post(HELEKET_WEBHOOK_PATH, heleket_payment_webhook) diff --git a/web/tblocker.py b/web/tblocker.py deleted file mode 100644 index 420f2d11..00000000 --- a/web/tblocker.py +++ /dev/null @@ -1,128 +0,0 @@ -import datetime - -from datetime import datetime - -from aiogram.exceptions import TelegramBadRequest, TelegramForbiddenError -from aiogram.types import InlineKeyboardButton -from aiogram.utils.keyboard import InlineKeyboardBuilder -from aiohttp import web - -from bot import bot -from config import BLOCK_DURATION, SERVER_COUNTRIES, TIMESTAMP_TTL -from database import get_key_details -from handlers.buttons import MAIN_MENU -from handlers.texts import TORRENT_BLOCKED_MSG, TORRENT_UNBLOCKED_MSG -from logger import logger - - -last_unblock_data = {} - - -def get_country_from_server(server: str) -> str: - """ - Определяет страну сервера по его имени или домену. - Ищет совпадение части домена в полных доменах. - """ - server_part = server.split(".")[0] - - for full_domain, country in SERVER_COUNTRIES.items(): - if server_part in full_domain: - return country - return server - - -def handle_telegram_errors(func): - async def wrapper(*args, **kwargs): - try: - return await func(*args, **kwargs) - except TelegramForbiddenError: - tg_id = kwargs.get("tg_id") or args[1] - logger.warning(f"🚫 Бот заблокирован пользователем {tg_id}.") - return False - except TelegramBadRequest: - tg_id = kwargs.get("tg_id") or args[1] - logger.warning(f"🚫 Чат не найден для пользователя {tg_id}.") - return False - except Exception as e: - tg_id = kwargs.get("tg_id") or args[1] - logger.error(f"❌ Ошибка отправки сообщения пользователю {tg_id}: {e}") - return False - - return wrapper - - -@handle_telegram_errors -async def send_notification(tg_id: int, username: str, ip: str, server: str, action: str, timestamp: str): - country = get_country_from_server(server) - - builder = InlineKeyboardBuilder() - builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile")) - - if action == "block": - message = TORRENT_BLOCKED_MSG.format(username=username, country=country, duration=BLOCK_DURATION) - else: - message = TORRENT_UNBLOCKED_MSG.format(username=username, country=country) - - await bot.send_message(chat_id=tg_id, text=message, parse_mode="HTML", reply_markup=builder.as_markup()) - logger.info(f"Отправлено уведомление пользователю {tg_id} о {action} для подписки {username}") - return True - - -async def tblocker_webhook(request: web.Request): - try: - data = await request.json() - logger.info(f"Получен запрос от tblocker: {data}") - - username = data.get("username") - ip = data.get("ip") - server = data.get("server") - action = data.get("action") - timestamp = data.get("timestamp") - - if not all([username, ip, server, action, timestamp]): - logger.error("Неполные данные в вебхуке") - return web.json_response({"error": "Missing required fields"}, status=400) - - global last_unblock_data - current_time = datetime.now().timestamp() - - last_unblock_data = { - k: v for k, v in last_unblock_data.items() if current_time - v["received_at"] <= TIMESTAMP_TTL - } - - cache_key = f"{username}:{server}" - if action == "unblock" and cache_key in last_unblock_data: - if timestamp == last_unblock_data[cache_key]["timestamp"]: - return web.json_response({"status": "ok", "message": "duplicate unblock skipped"}) - - if action == "unblock": - last_unblock_data[cache_key] = { - "timestamp": timestamp, - "received_at": current_time, - } - - sessionmaker = request.app["sessionmaker"] - async with sessionmaker() as session: - key_info = await get_key_details(session, username) - - if not key_info: - logger.error(f"Ключ не найден для email {username}") - return web.json_response({"error": "Key not found"}, status=404) - - success = await send_notification( - tg_id=key_info["tg_id"], - username=username, - ip=ip, - server=server, - action=action, - timestamp=timestamp, - ) - - if not success: - logger.warning(f"Не удалось отправить уведомление пользователю {key_info['tg_id']}") - - return web.json_response({"status": "ok"}) - - except Exception as e: - logger.error(f"Ошибка при обработке вебхука: {str(e)}", exc_info=True) - return web.json_response({"error": str(e)}, status=500)