from __future__ import annotations import os from dataclasses import dataclass, field from typing import Any from yuxi.utils.logging_config import logger @dataclass class AttachedResult: text: str = "" media_urls: list[str] = field(default_factory=list) media_local_roots: list[str] = field(default_factory=list) reply_to_id: str = "" thread_id: str = "" @property def has_media(self) -> bool: return bool(self.media_urls or self.media_local_roots) async def send_attached_text( adapter: Any, chat_id: str, text: str, reply_to_id: str = "", thread_id: str = "", ) -> dict: from yuxi.channels.models import ChannelIdentity, ChannelResponse channel_type = getattr(adapter, "channel_type", None) channel_id = getattr(adapter, "channel_id", "mattermost") target_id = reply_to_id or thread_id response = ChannelResponse( identity=ChannelIdentity( channel_id=channel_id, channel_type=channel_type, channel_user_id="system", channel_chat_id=chat_id, ), content=text, reply_to_message_id=target_id, metadata={ "attached_result": True, "reply_to_id": reply_to_id, "thread_id": thread_id, }, ) result = await adapter.send(response) logger.info(f"[Mattermost] Attached text sent to {chat_id}: success={result.success}, msg_id={result.message_id}") return { "success": result.success, "message_id": result.message_id, "error": result.error, } async def send_attached_media( adapter: Any, chat_id: str, media_urls: list[str] | None = None, media_local_roots: list[str] | None = None, reply_to_id: str = "", thread_id: str = "", ) -> list[dict]: results: list[dict] = [] local_files = media_local_roots or [] for file_path in local_files: if not os.path.isfile(file_path): logger.warning(f"[Mattermost] Attached media local file not found: {file_path}") results.append( { "success": False, "error": f"File not found: {file_path}", "file_path": file_path, } ) continue try: with open(file_path, "rb") as f: data = f.read() result = await adapter.send_media(chat_id, "file", data) results.append( { "success": result.success, "file_ids": result.metadata.get("file_ids", []) if result.metadata else [], "error": result.error, "file_path": file_path, } ) except Exception as e: logger.error(f"[Mattermost] Attached media upload failed for {file_path}: {e}") results.append( { "success": False, "error": str(e), "file_path": file_path, } ) urls = media_urls or [] for url in urls: from yuxi.channels.models import ChannelIdentity, ChannelResponse try: channel_type = getattr(adapter, "channel_type", None) channel_id = getattr(adapter, "channel_id", "mattermost") target_id = reply_to_id or thread_id response = ChannelResponse( identity=ChannelIdentity( channel_id=channel_id, channel_type=channel_type, channel_user_id="system", channel_chat_id=chat_id, ), content=url, reply_to_message_id=target_id, metadata={"attached_media_url": True}, ) result = await adapter.send(response) results.append( { "success": result.success, "message_id": result.message_id, "error": result.error, "url": url, } ) except Exception as e: logger.error(f"[Mattermost] Attached media URL send failed for {url}: {e}") results.append( { "success": False, "error": str(e), "url": url, } ) return results async def deliver_attached_results( adapter: Any, chat_id: str, result: AttachedResult, ) -> dict: text_result = ( await send_attached_text( adapter, chat_id, result.text, reply_to_id=result.reply_to_id, thread_id=result.thread_id, ) if result.text else None ) media_results = ( await send_attached_media( adapter, chat_id, media_urls=result.media_urls if result.media_urls else None, media_local_roots=result.media_local_roots if result.media_local_roots else None, reply_to_id=result.reply_to_id, thread_id=result.thread_id, ) if result.has_media else [] ) return { "text_result": text_result, "media_results": media_results, }