# -*- coding: utf-8 -*- """ =================================== Discord 平台适配器 =================================== 负责: 1. 验证 Discord Webhook 请求 2. 解析 Discord 消息为统一格式 3. 将响应转换为 Discord 格式 """ import logging from typing import Dict, Any, Optional from bot.platforms.base import BotPlatform from bot.models import BotMessage, WebhookResponse logger = logging.getLogger(__name__) class DiscordPlatform(BotPlatform): """Discord 平台适配器""" @property def platform_name(self) -> str: """平台标识名称""" return "discord" def verify_request(self, headers: Dict[str, str], body: bytes) -> bool: """验证 Discord Webhook 请求签名 Discord Webhook 签名验证: 1. 从请求头获取 X-Signature-Ed25519 和 X-Signature-Timestamp 2. 使用公钥验证签名 Args: headers: HTTP 请求头 body: 请求体原始字节 Returns: 签名是否有效 """ # TODO: 实现 Discord Webhook 签名验证 # 当前暂时返回 True,后续需要完善 return True def parse_message(self, data: Dict[str, Any]) -> Optional[BotMessage]: """解析 Discord 消息为统一格式 Args: data: 解析后的 JSON 数据 Returns: BotMessage 对象,或 None(不需要处理) """ # 检查是否是消息事件 if data.get("type") != 1 and data.get("type") != 2: return None # 提取消息内容 content = data.get("content", "").strip() if not content: return None # 提取用户信息 author = data.get("author", {}) user_id = author.get("id", "") user_name = author.get("username", "unknown") # 提取频道信息 channel_id = data.get("channel_id", "") guild_id = data.get("guild_id", "") # 提取消息 ID message_id = data.get("id", "") # 提取附件信息(如果有) attachments = data.get("attachments", []) attachment_urls = [att["url"] for att in attachments if "url" in att] # 构建 BotMessage 对象 message = BotMessage( platform="discord", message_id=message_id, user_id=user_id, user_name=user_name, content=content, attachment_urls=attachment_urls, channel_id=channel_id, group_id=guild_id, # 从 data 中提取其他相关信息 timestamp=data.get("timestamp"), mention_everyone=data.get("mention_everyone", False), mentions=data.get("mentions", []), # 添加 Discord 特定的原始数据 raw_data={ "message_id": message_id, "channel_id": channel_id, "guild_id": guild_id, "author": author, "content": content, "timestamp": data.get("timestamp"), "attachments": attachments, "mentions": data.get("mentions", []), "mention_roles": data.get("mention_roles", []), "mention_everyone": data.get("mention_everyone", False), "type": data.get("type"), } ) return message def format_response(self, response: Any, message: BotMessage) -> WebhookResponse: """将统一响应转换为 Discord 格式 Args: response: 统一响应对象 message: 原始消息对象 Returns: WebhookResponse 对象 """ # 构建 Discord 响应格式 discord_response = { "content": response.text if hasattr(response, "text") else str(response), "tts": False, "embeds": [], "allowed_mentions": { "parse": ["users", "roles", "everyone"] } } return WebhookResponse.success(discord_response) def handle_challenge(self, data: Dict[str, Any]) -> Optional[WebhookResponse]: """处理 Discord 验证请求 Discord 在配置 Webhook 时会发送验证请求 Args: data: 请求数据 Returns: 验证响应,或 None(不是验证请求) """ # Discord Webhook 验证请求类型是 1 if data.get("type") == 1: return WebhookResponse.success({ "type": 1 }) # Discord 命令交互验证 if "challenge" in data: return WebhookResponse.success({ "challenge": data["challenge"] }) return None