fix max-bot, add tests

This commit is contained in:
Александра Пронина 2026-05-15 10:22:43 +03:00
parent 7abbaf7e7a
commit 2ad1438e1c
17 changed files with 1621 additions and 494 deletions

View file

@ -1,88 +1,151 @@
"""MAX event to internal protocol converter."""
from typing import Union, List
from core.protocol import (
IncomingMessage,
IncomingCommand,
IncomingCallback,
Attachment,
)
"""MAX Bot API payloads -> core Incoming* types."""
from __future__ import annotations
from typing import Any
from core.protocol import Attachment, IncomingCallback, IncomingCommand, IncomingMessage
def _extract_command(text: str) -> Union[IncomingCommand, IncomingCallback, None]:
if not text.startswith("!"):
return None
parts = text.strip().split(maxsplit=1)
cmd = parts[0].lower()
args = parts[1] if len(parts) > 1 else ""
if cmd == "!yes":
return IncomingCallback(
user_id="",
platform="max",
chat_id="",
action="confirm",
)
elif cmd == "!no":
return IncomingCallback(
user_id="",
platform="max",
chat_id="",
action="cancel",
)
else:
return IncomingCommand(
user_id="",
platform="max",
chat_id="",
command=cmd.lstrip("!"),
args=args.split() if args else [],
)
def max_message_to_incoming(
def incoming_from_text_commands(
*,
text: str,
user_id: str,
chat_id: str,
attachments: List[Attachment] = None,
callback_data: str = None,
message_id: str = None,
) -> Union[IncomingMessage, IncomingCommand, IncomingCallback]:
if callback_data:
max_user_id: str,
platform_chat_id: str,
attachments: list[Attachment],
) -> IncomingMessage | IncomingCommand | IncomingCallback:
"""Парсинг текста: только slash-команды (как в Telegram), обычное сообщение иначе."""
stripped = text.strip()
proto = stripped.lower()
if proto in {"/yes"}:
return IncomingCallback(
user_id=user_id,
user_id=max_user_id,
platform="max",
chat_id=chat_id,
action=callback_data,
payload={"message_id": message_id} if message_id else {},
chat_id=platform_chat_id,
action="confirm",
payload={},
)
if proto in {"/no"}:
return IncomingCallback(
user_id=max_user_id,
platform="max",
chat_id=platform_chat_id,
action="cancel",
payload={},
)
if text:
cmd = _extract_command(text)
if cmd is not None:
cmd.user_id = user_id
cmd.chat_id = chat_id
return cmd
if not stripped.startswith("/"):
return IncomingMessage(
user_id=max_user_id,
platform="max",
chat_id=platform_chat_id,
text=text,
attachments=attachments,
reply_to=None,
)
return IncomingMessage(
user_id=user_id,
raw = stripped[1:]
parts = raw.split(maxsplit=1)
name = (parts[0] or "").lower()
tail = parts[1] if len(parts) > 1 else ""
return IncomingCommand(
user_id=max_user_id,
platform="max",
chat_id=chat_id,
text=text or "",
attachments=attachments or [],
chat_id=platform_chat_id,
command=name,
args=tail.split() if tail else [],
)
def max_attachment_to_internal(
def incoming_from_message_callback_payload(
*,
filename: str,
mime_type: str,
download_url: str,
) -> Attachment:
return Attachment(
type="document",
url=download_url,
max_user_id: str,
platform_chat_id: str,
payload_raw: str | None,
callback_message_id: str | None,
) -> IncomingCallback | None:
if not payload_raw:
return None
if payload_raw in {"confirm", "cancel", "toggle_skill"}:
return IncomingCallback(
user_id=max_user_id,
platform="max",
chat_id=platform_chat_id,
action=payload_raw,
payload={"message_id": callback_message_id or ""},
)
return IncomingCallback(
user_id=max_user_id,
platform="max",
chat_id=platform_chat_id,
action="max_callback",
payload={"payload": payload_raw, "message_id": callback_message_id},
)
def attachment_from_max_dict(raw: dict[str, Any]) -> tuple[Attachment, dict[str, Any]] | None:
"""Return core Attachment placeholder + raw attachment for download."""
kind = raw.get("type")
payload = raw.get("payload")
if not isinstance(kind, str) or not isinstance(payload, dict):
return None
url = payload.get("url")
if not isinstance(url, str):
url = ""
token = payload.get("token")
filename = "attachment.bin"
mapped = "document"
mime: str | None = None
if kind == "image":
mapped = "image"
filename = "image.jpg"
elif kind == "video":
mapped = "video"
filename = "video.mp4"
elif kind == "audio":
mapped = "audio"
mime = payload.get("mime_type") if isinstance(payload.get("mime_type"), str) else "audio/mpeg"
filename = "audio.bin"
elif kind == "file":
fname = payload.get("filename")
filename = fname if isinstance(fname, str) and fname else "file.bin"
mapped = "document"
else:
return None
attachment = Attachment(
type=mapped,
url=url or None,
content=None,
filename=filename,
mime_type=mime_type,
)
mime_type=mime,
)
meta = dict(raw)
if token:
meta["_download_token_hint"] = token
return attachment, meta
def collect_max_attachments(message_body: dict[str, Any]) -> tuple[list[Attachment], list[dict[str, Any]]]:
attachments = message_body.get("attachments")
if not isinstance(attachments, list):
return [], []
core_list: list[Attachment] = []
raw_list: list[dict[str, Any]] = []
for item in attachments:
if isinstance(item, dict):
parsed = attachment_from_max_dict(item)
if parsed is None:
continue
core_a, raw_a = parsed
core_list.append(core_a)
raw_list.append(raw_a)
return core_list, raw_list