ForcePilot/backend/package/yuxi/channels/adapters/mattermost/agent_tools.py
Kris d7fe152dae feat(mattermost): 完成 Mattermost 适配器多账号支持与功能增强
本次提交对 Mattermost 适配器进行了全面升级:
1.  重构为多账号架构,支持同时管理多个 Mattermost 机器人账号
2.  更新安全策略默认配置为配对模式和白名单模式
3.  新增 WebSocket 心跳、重连配置项与连接监控
4.  扩展 Agent 工具支持 pin/unpin、获取反应、搜索消息等操作
5.  重构交互按钮构建逻辑,新增分页与提供商筛选功能
6.  优化 SSRF 防护代码,复用公共工具库实现
7.  新增配置兼容性迁移与可变白名单项检测
8.  完善错误处理与日志输出,添加重复消息去重统计
9.  新增发送临时消息(ephemeral)支持
10. 修复提及检测逻辑,正确处理用户名大小写
2026-05-13 16:12:02 +08:00

208 lines
7.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
from typing import Any
def describe_mattermost_message_tool() -> dict:
return {
"type": "function",
"function": {
"name": "mattermost_send_message",
"description": "通过 Mattermost 渠道发送消息、编辑消息、删除消息、添加反应、固定/取消固定消息",
"parameters": {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["send", "react", "edit", "delete", "pin", "unpin"],
"description": "要执行的操作类型",
},
"chat_id": {
"type": "string",
"description": "目标频道或用户的 chat_id",
},
"content": {
"type": "string",
"description": "消息内容send 和 edit 操作需要)",
},
"msg_id": {
"type": "string",
"description": "消息 IDedit、delete、react、pin 和 unpin 操作需要)",
},
"emoji": {
"type": "string",
"description": "表情符号名称react 操作需要)",
},
},
"required": ["action", "chat_id"],
},
},
}
def describe_mattermost_get_reactions_tool() -> dict:
return {
"type": "function",
"function": {
"name": "mattermost_get_reactions",
"description": "获取指定消息的所有 Reaction 列表",
"parameters": {
"type": "object",
"properties": {
"msg_id": {
"type": "string",
"description": "要查询的消息 ID",
},
},
"required": ["msg_id"],
},
},
}
def describe_mattermost_list_pins_tool() -> dict:
return {
"type": "function",
"function": {
"name": "mattermost_list_pins",
"description": "获取频道中的固定消息列表",
"parameters": {
"type": "object",
"properties": {
"channel_id": {
"type": "string",
"description": "频道 ID",
},
},
"required": ["channel_id"],
},
},
}
def describe_mattermost_search_messages_tool() -> dict:
return {
"type": "function",
"function": {
"name": "mattermost_search_messages",
"description": "在 Mattermost 中搜索消息",
"parameters": {
"type": "object",
"properties": {
"team_id": {
"type": "string",
"description": "Team ID",
},
"terms": {
"type": "string",
"description": "搜索关键词",
},
},
"required": ["team_id", "terms"],
},
},
}
class MattermostAgentToolFactory:
def __init__(self, adapter: Any):
self._adapter = adapter
def list_tools(self) -> list[dict]:
return [
describe_mattermost_message_tool(),
describe_mattermost_get_reactions_tool(),
describe_mattermost_list_pins_tool(),
describe_mattermost_search_messages_tool(),
]
async def execute_tool(self, tool_name: str, params: dict) -> Any:
if tool_name == "mattermost_send_message":
return await self._execute_message_action(params)
if tool_name == "mattermost_get_reactions":
return await self._execute_get_reactions(params)
if tool_name == "mattermost_list_pins":
return await self._execute_list_pins(params)
if tool_name == "mattermost_search_messages":
return await self._execute_search_messages(params)
raise ValueError(f"Unknown tool: {tool_name}")
async def _execute_message_action(self, params: dict) -> dict:
action = params.get("action", "send")
chat_id = params.get("chat_id", "")
if action == "send":
from yuxi.channels.models import (
ChannelIdentity,
ChannelResponse,
ChannelType,
)
response = ChannelResponse(
identity=ChannelIdentity(
channel_id="mattermost",
channel_type=ChannelType.MATTERMOST,
channel_user_id="",
channel_chat_id=chat_id,
),
content=params.get("content", ""),
)
result = await self._adapter.send(response)
return {"success": result.success, "message_id": result.message_id, "error": result.error}
if action == "react":
result = await self._adapter.send_reaction(
chat_id=chat_id,
msg_id=params.get("msg_id", ""),
emoji=params.get("emoji", ""),
)
return {"success": result.success, "error": result.error}
if action == "edit":
result = await self._adapter.edit_message(
chat_id=chat_id,
msg_id=params.get("msg_id", ""),
content=params.get("content", ""),
)
return {"success": result.success, "error": result.error}
if action == "delete":
result = await self._adapter.delete_message(
chat_id=chat_id,
msg_id=params.get("msg_id", ""),
)
return {"success": result.success, "error": result.error}
if action == "pin":
result = await self._adapter.pin_message(
post_id=params.get("msg_id", ""),
)
return {"success": result, "error": "" if result else "Pin failed"}
if action == "unpin":
result = await self._adapter.unpin_message(
post_id=params.get("msg_id", ""),
)
return {"success": result, "error": "" if result else "Unpin failed"}
return {"success": False, "error": f"Unknown action: {action}"}
async def _execute_get_reactions(self, params: dict) -> dict:
result = await self._adapter.get_reactions(
post_id=params.get("msg_id", ""),
)
return {"success": True, "reactions": result}
async def _execute_list_pins(self, params: dict) -> dict:
result = await self._adapter.list_pinned_messages(
channel_id=params.get("channel_id", ""),
)
return {"success": True, "pins": result}
async def _execute_search_messages(self, params: dict) -> dict:
result = await self._adapter.search_messages(
team_id=params.get("team_id", ""),
terms=params.get("terms", ""),
)
return {"success": True, "results": result}