You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
4.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# -*- 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