intra_max_chatbot/bot/middleware.py
2026-03-26 00:48:04 +03:00

119 lines
4.6 KiB
Python

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)