from collections.abc import Awaitable, Callable from typing import Any from maxapi.filters.middleware import BaseMiddleware from maxapi.types.updates.bot_started import BotStarted from maxapi.types.updates.message_callback import MessageCallback from maxapi.types.updates.message_created import MessageCreated from bot.logging_setup import get_logger from database.queries.bot_users import get_bot_user_by_max_id from database.queries.message_log import log_message logger = get_logger("bot.middleware") def _user_label(bot_user: dict | None, user_id: int | None) -> str: if bot_user: name = bot_user.get("first_name") or bot_user.get("username") or "" return f"user_id={bot_user['max_user_id']} ({name})" if name else f"user_id={bot_user['max_user_id']}" return f"user_id={user_id}" class AuthMiddleware(BaseMiddleware): async def __call__( self, handler: Callable[[Any, dict[str, Any]], Awaitable[Any]], event: Any, data: dict[str, Any], ) -> Any: update_type = getattr(event, "update_type", type(event).__name__) # BotStarted — всегда пропускаем if isinstance(event, BotStarted): logger.info("▶ bot_started | user_id=%s chat_id=%s", event.user.user_id, event.chat_id) return await handler(event, data) # /start — пропускаем без авторизации if isinstance(event, MessageCreated): body = event.message.body text = (body.text or "").strip() if body else "" if text.lower().startswith("/start"): ids = event.get_ids() logger.info("▶ /start | user_id=%s", ids[1]) return await handler(event, data) # Получаем user_id ids = event.get_ids() user_id = ids[1] if ids else None chat_id = ids[0] if ids else None if user_id is None: return bot_user = await get_bot_user_by_max_id(user_id) if bot_user is None: logger.debug("⚠ unknown user_id=%s | %s — ignored", user_id, update_type) return if bot_user["is_blocked"]: logger.info("🚫 blocked %s | %s — ignored", _user_label(bot_user, user_id), update_type) return if not bot_user["is_authorized"]: # Пропускаем шаринг контакта — это шаг авторизации if isinstance(event, MessageCreated): body = event.message.body if body and body.attachments: from maxapi.enums.attachment import AttachmentType for att in body.attachments: if getattr(att, "type", None) == AttachmentType.CONTACT: logger.info( "📱 contact shared | %s", _user_label(bot_user, user_id), ) data["bot_user"] = bot_user return await handler(event, data) logger.debug("⚠ not authorized %s | %s — ignored", _user_label(bot_user, user_id), update_type) return data["bot_user"] = bot_user # Логируем входящие сообщения if isinstance(event, MessageCreated): body = event.message.body text_preview = "" atts = [] if body: text_preview = (body.text or "")[:80] if body.attachments: for a in body.attachments: atts.append({"type": str(getattr(a, "type", ""))}) logger.info( "💬 message | %s | chat=%s | text=%r | attachments=%s", _user_label(bot_user, user_id), chat_id, text_preview, [a["type"] for a in atts] or None, ) await log_message( direction="in", max_user_id=user_id, max_chat_id=chat_id, message_id=body.mid if body else None, text=body.text if body else None, attachments=atts or None, ) elif isinstance(event, MessageCallback): payload = event.callback.payload or "" logger.info( "🔘 callback | %s | chat=%s | payload=%r", _user_label(bot_user, user_id), chat_id, payload, ) else: logger.info( "📨 %s | %s | chat=%s", update_type, _user_label(bot_user, user_id), chat_id, ) return await handler(event, data)