"""MAX Bot API payloads -> core Incoming* types.""" from __future__ import annotations from typing import Any from core.protocol import Attachment, IncomingCallback, IncomingCommand, IncomingMessage def incoming_from_text_commands( *, text: str, 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=max_user_id, platform="max", 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 not stripped.startswith("/"): return IncomingMessage( user_id=max_user_id, platform="max", chat_id=platform_chat_id, text=text, attachments=attachments, reply_to=None, ) 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=platform_chat_id, command=name, args=tail.split() if tail else [], ) def incoming_from_message_callback_payload( *, 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, ) 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