ForcePilot/backend/package/yuxi/channels/adapters/mattermost/attached_results.py
Kris 002d601d1b feat(mattermost): 实现完整的 Mattermost 适配器模块
新增 Mattermost 渠道完整实现,包含适配器核心、消息处理、交互回调、命令支持、安全校验、多账号管理等功能,支持机器人消息发送、交互按钮、命令注册、投票功能以及配置动态修改等特性。
2026-05-12 00:46:12 +08:00

183 lines
5.2 KiB
Python

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,
}