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 ` | 与 AI 助手对话 |\n" "| `/fp ` | 快捷对话 |\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}"}