From 851e452292e394dab9de620389ad486623360e5f Mon Sep 17 00:00:00 2001 From: Vladless Date: Thu, 11 Dec 2025 15:49:38 +0300 Subject: [PATCH] All middlewares now work through a single DB session --- middlewares.zip | Bin 0 -> 39666 bytes middlewares/admin.py | 39 +++++++++++++---------- middlewares/ban_checker.py | 47 +++++++++++++--------------- middlewares/direct_start_blocker.py | 12 ++++--- middlewares/maintenance.py | 27 +++++++++++----- middlewares/user.py | 36 +++++++++++---------- 6 files changed, 90 insertions(+), 71 deletions(-) create mode 100644 middlewares.zip diff --git a/middlewares.zip b/middlewares.zip new file mode 100644 index 0000000000000000000000000000000000000000..ebd5e8c69421427fc96225859fb3cf62010fd9b9 GIT binary patch literal 39666 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 Kp-RLkaQ9!klGNn@ literal 0 HcmV?d00001 diff --git a/middlewares/admin.py b/middlewares/admin.py index 123a7228..f0f3271a 100644 --- a/middlewares/admin.py +++ b/middlewares/admin.py @@ -4,19 +4,16 @@ from typing import Any from aiogram import BaseMiddleware from aiogram.types import CallbackQuery, Message, TelegramObject from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession from config import ADMIN_ID from database.models import Admin class AdminMiddleware(BaseMiddleware): - """Middleware для проверки прав администратора. + """Проверяет, является ли пользователь администратором.""" - Добавляет в data['admin'] = True/False в зависимости от того, - является ли пользователь администратором. - """ - - _admin_ids: set[int] = set(ADMIN_ID) if isinstance(ADMIN_ID, list | tuple) else {ADMIN_ID} + _admin_ids: set[int] = set(ADMIN_ID) if isinstance(ADMIN_ID, (list, tuple)) else {ADMIN_ID} async def __call__( self, @@ -24,20 +21,28 @@ class AdminMiddleware(BaseMiddleware): event: TelegramObject, data: dict[str, Any], ) -> Any: - """Обрабатывает событие и добавляет флаг администратора в data.""" - data["admin"] = await self._check_admin_access(event, data.get("session")) + session: AsyncSession | None = data.get("session") + data["admin"] = await self._check_admin_access(event, session) return await handler(event, data) - async def _check_admin_access(self, event: TelegramObject, session) -> bool: - """Проверяет, имеет ли пользователь права администратора.""" + async def _check_admin_access( + self, + event: TelegramObject, + session: AsyncSession | None, + ) -> bool: try: user_id = None + if isinstance(event, Message): - user_id = event.from_user.id if event.from_user else None + if event.from_user: + user_id = event.from_user.id elif isinstance(event, CallbackQuery): - user_id = event.from_user.id if event.from_user else None + if event.from_user: + user_id = event.from_user.id else: - user_id = getattr(getattr(event, "from_user", None), "id", None) + from_user = getattr(event, "from_user", None) + if from_user: + user_id = getattr(from_user, "id", None) if not user_id: return False @@ -45,10 +50,10 @@ class AdminMiddleware(BaseMiddleware): if user_id in self._admin_ids: return True - if session: - result = await session.execute(select(Admin).where(Admin.tg_id == user_id)) - return result.scalar_one_or_none() is not None + if not session: + return False - return False + result = await session.execute(select(Admin).where(Admin.tg_id == user_id)) + return result.scalar_one_or_none() is not None except Exception: return False diff --git a/middlewares/ban_checker.py b/middlewares/ban_checker.py index 7e3f67e8..f749a3c6 100644 --- a/middlewares/ban_checker.py +++ b/middlewares/ban_checker.py @@ -19,7 +19,7 @@ _ban_cache: dict[int, tuple[float, dict | None]] = {} class BanCheckerMiddleware(BaseMiddleware): - def __init__(self, session_factory: Callable[[], AsyncSession]) -> None: + def __init__(self, session_factory: Callable[[], AsyncSession] | None = None) -> None: self.session_factory = session_factory async def __call__( @@ -50,32 +50,27 @@ class BanCheckerMiddleware(BaseMiddleware): if cached and cached[0] > now_ts: ban_info = cached[1] else: - session: AsyncSession | None = ( - data.get("session") if isinstance(data.get("session"), AsyncSession) else None - ) - created_here = False - if session is None: - session = self.session_factory() - created_here = True - try: - q = ( - select(ManualBan.reason, ManualBan.until) - .where( - ManualBan.tg_id == tg_id, - (ManualBan.until.is_(None)) | (ManualBan.until > datetime.utcnow()), - ) - .limit(1) + session = data.get("session") + if not isinstance(session, AsyncSession): + logger.error("[BanChecker] session отсутствует в data") + return await handler(event, data) + + query = ( + select(ManualBan.reason, ManualBan.until) + .where( + ManualBan.tg_id == tg_id, + (ManualBan.until.is_(None)) | (ManualBan.until > datetime.utcnow()), ) - res = await session.execute(q) - row = res.first() - if row: - reason, until = row - ban_info = {"reason": reason or "не указана", "until": until} - else: - ban_info = None - finally: - if created_here: - await session.close() + .limit(1) + ) + result = await session.execute(query) + row = result.first() + if row: + reason, until = row + ban_info = {"reason": reason or "не указана", "until": until} + else: + ban_info = None + _ban_cache[tg_id] = (now_ts + _BAN_CACHE_TTL, ban_info) if not ban_info: diff --git a/middlewares/direct_start_blocker.py b/middlewares/direct_start_blocker.py index e843d4b9..6ba8975b 100644 --- a/middlewares/direct_start_blocker.py +++ b/middlewares/direct_start_blocker.py @@ -1,14 +1,14 @@ import time - from collections.abc import Awaitable, Callable from typing import Any from aiogram import BaseMiddleware from aiogram.types import Message, Update +from sqlalchemy.ext.asyncio import AsyncSession from config import DISABLE_DIRECT_START from core.bootstrap import MODES_CONFIG -from database import async_session_maker, check_user_exists +from database import check_user_exists from logger import logger @@ -40,6 +40,11 @@ class DirectStartBlockerMiddleware(BaseMiddleware): if current_state: return await handler(event, data) + session = data.get("session") + if not isinstance(session, AsyncSession): + logger.error("[DirectStartBlocker] session отсутствует в data") + return await handler(event, data) + tg_id = message.from_user.id text = message.text.strip() now = time.time() @@ -48,8 +53,7 @@ class DirectStartBlockerMiddleware(BaseMiddleware): cached = _cache_user_exists.get(tg_id) if cached and cached[0] > now: return cached[1] - async with async_session_maker() as session: - exists = await check_user_exists(session, tg_id) + exists = await check_user_exists(session, tg_id) _cache_user_exists[tg_id] = (now + _TTL, exists) return exists diff --git a/middlewares/maintenance.py b/middlewares/maintenance.py index e1477a33..e8f04045 100644 --- a/middlewares/maintenance.py +++ b/middlewares/maintenance.py @@ -3,14 +3,20 @@ from typing import Any from aiogram import BaseMiddleware from aiogram.types import CallbackQuery, Message, Update +from sqlalchemy.ext.asyncio import AsyncSession from config import ADMIN_ID from core.bootstrap import MANAGEMENT_CONFIG -from database import async_session_maker from database.models import Admin class MaintenanceModeMiddleware(BaseMiddleware): + def __init__(self) -> None: + if isinstance(ADMIN_ID, (list, tuple, set)): + self._admin_ids = set(ADMIN_ID) + else: + self._admin_ids = {ADMIN_ID} + async def __call__( self, handler: Callable[[Update, dict[str, Any]], Awaitable[Any]], @@ -23,20 +29,25 @@ class MaintenanceModeMiddleware(BaseMiddleware): user_id = None if isinstance(event, Message): - user_id = event.from_user.id + if event.from_user: + user_id = event.from_user.id elif isinstance(event, CallbackQuery): - user_id = event.from_user.id + if event.from_user: + user_id = event.from_user.id if not user_id: return - if user_id in ADMIN_ID: + if user_id in self._admin_ids: return await handler(event, data) - async with async_session_maker() as session: - db_admin = await session.get(Admin, user_id) - if db_admin: - return await handler(event, data) + session = data.get("session") + if not isinstance(session, AsyncSession): + return + + db_admin = await session.get(Admin, user_id) + if db_admin: + return await handler(event, data) if isinstance(event, CallbackQuery): await event.answer("⚙️ Бот временно недоступен. Ведутся технические работы.", show_alert=True) diff --git a/middlewares/user.py b/middlewares/user.py index 47e638b6..b5fb7b2f 100644 --- a/middlewares/user.py +++ b/middlewares/user.py @@ -4,6 +4,7 @@ from typing import Any from aiogram import BaseMiddleware from aiogram.types import TelegramObject, User +from sqlalchemy.ext.asyncio import AsyncSession from database import upsert_user from logger import logger @@ -24,22 +25,23 @@ class UserMiddleware(BaseMiddleware): user: User | None = data.get("event_from_user") if user and not user.is_bot: session = data.get("session") - db_user = await self._process_user(user, session) - if db_user: - data["user"] = db_user + if isinstance(session, AsyncSession): + db_user = await self._process_user(user, session) + if db_user: + data["user"] = db_user except Exception as e: logger.error(f"Ошибка при обработке пользователя: {e}") return await handler(event, data) - async def _process_user(self, user: User, session: Any = None) -> dict | None: + async def _process_user(self, user: User, session: AsyncSession) -> dict | None: uid = user.id - fp = self._fingerprint(user) + fingerprint = self._fingerprint(user) now = monotonic() cached = self._cache.get(uid) if cached: - cached_fp, ts, cached_db_user = cached - if fp == cached_fp and now - ts < self._debounce: + cached_fingerprint, ts, cached_db_user = cached + if fingerprint == cached_fingerprint and now - ts < self._debounce: return cached_db_user logger.debug(f"Обработка пользователя: {uid}") @@ -53,17 +55,19 @@ class UserMiddleware(BaseMiddleware): session=session, only_if_exists=True, ) - self._cache[uid] = (fp, now, db_user) + self._cache[uid] = (fingerprint, now, db_user) if db_user: logger.debug(f"Получены данные пользователя из БД: {uid}") return db_user def _fingerprint(self, user: User) -> str: - return "|".join([ - str(user.id), - user.username or "", - user.first_name or "", - user.last_name or "", - user.language_code or "", - "1" if user.is_bot else "0", - ]) + return "|".join( + [ + str(user.id), + user.username or "", + user.first_name or "", + user.last_name or "", + user.language_code or "", + "1" if user.is_bot else "0", + ] + )