新增 Mattermost 渠道完整实现,包含适配器核心、消息处理、交互回调、命令支持、安全校验、多账号管理等功能,支持机器人消息发送、交互按钮、命令注册、投票功能以及配置动态修改等特性。
96 lines
3.1 KiB
Python
96 lines
3.1 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import hmac
|
|
import os
|
|
from typing import Any
|
|
|
|
from yuxi.channels.models import ChannelIdentity, ChannelMessage, ChannelType, EventType, MessageType
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
|
|
def verify_slash_hmac(body: bytes, signature: str, secret: str) -> bool:
|
|
if not secret or not signature:
|
|
return False
|
|
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
|
|
return hmac.compare_digest(expected, signature)
|
|
|
|
|
|
async def handle_slash_command(
|
|
body: bytes,
|
|
headers: dict,
|
|
adapter: Any,
|
|
) -> dict:
|
|
signing_secret = os.getenv("MATTERMOST_SIGNING_SECRET", "")
|
|
signature = headers.get("X-Mattermost-Signature", "") or headers.get("x-mattermost-signature", "")
|
|
|
|
if not verify_slash_hmac(body, signature, signing_secret):
|
|
return {"response_type": "ephemeral", "text": "签名验证失败"}
|
|
|
|
import json
|
|
|
|
try:
|
|
data = json.loads(body)
|
|
except json.JSONDecodeError:
|
|
return {"response_type": "ephemeral", "text": "无效的请求数据"}
|
|
|
|
command = data.get("command", "").strip()
|
|
text = data.get("text", "").strip()
|
|
user_id = data.get("user_id", "")
|
|
channel_id = data.get("channel_id", "")
|
|
team_id = data.get("team_id", "")
|
|
|
|
logger.info(f"[Mattermost] Slash command received: {command} '{text}' from {user_id} in channel {channel_id}")
|
|
|
|
if command in ("/forcepilot", "/fp"):
|
|
msg = ChannelMessage(
|
|
identity=ChannelIdentity(
|
|
channel_id="mattermost",
|
|
channel_type=ChannelType.MATTERMOST,
|
|
channel_user_id=user_id,
|
|
channel_chat_id=f"channel_{channel_id}",
|
|
),
|
|
event_type=EventType.MESSAGE_RECEIVED,
|
|
message_type=MessageType.COMMAND,
|
|
content=text or "你好",
|
|
metadata={"slash_command": command, "team_id": team_id},
|
|
)
|
|
await adapter._handle_message(msg)
|
|
return {
|
|
"response_type": "in_channel",
|
|
"text": f"已收到命令 `{command}`,正在处理…",
|
|
}
|
|
|
|
if command == "/help":
|
|
return {
|
|
"response_type": "ephemeral",
|
|
"text": (
|
|
"**ForcePilot Mattermost 命令**\n\n"
|
|
"| 命令 | 说明 |\n"
|
|
"|------|------|\n"
|
|
"| `/forcepilot <msg>` | 与 AI 助手对话 |\n"
|
|
"| `/fp <msg>` | 快捷对话 |\n"
|
|
"| `/model` | 切换 AI 模型 |\n"
|
|
"| `/clear` | 清除对话上下文 |\n"
|
|
"| `/help` | 显示帮助信息 |"
|
|
),
|
|
}
|
|
|
|
if command == "/clear":
|
|
return {
|
|
"response_type": "ephemeral",
|
|
"text": "对话上下文已清除 ✅",
|
|
}
|
|
|
|
if command == "/model":
|
|
from .model_picker import build_model_picker_attachment
|
|
|
|
attachments = [build_model_picker_attachment()]
|
|
return {
|
|
"response_type": "in_channel",
|
|
"text": "选择一个 AI 模型:",
|
|
"attachments": attachments,
|
|
}
|
|
|
|
return {"response_type": "ephemeral", "text": f"未知命令: {command}"}
|