新增 Mattermost 渠道完整实现,包含适配器核心、消息处理、交互回调、命令支持、安全校验、多账号管理等功能,支持机器人消息发送、交互按钮、命令注册、投票功能以及配置动态修改等特性。
183 lines
5.2 KiB
Python
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,
|
|
}
|