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.

154 lines
4.3 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 -*-
"""
===================================
平台适配器基类
===================================
定义平台适配器的抽象基类,各平台必须继承此类。
"""
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional, Tuple
from bot.models import BotMessage, BotResponse, WebhookResponse
class BotPlatform(ABC):
"""
平台适配器抽象基类
负责:
1. 验证 Webhook 请求签名
2. 解析平台消息为统一格式
3. 将响应转换为平台格式
使用示例:
class MyPlatform(BotPlatform):
@property
def platform_name(self) -> str:
return "myplatform"
def verify_request(self, headers, body) -> bool:
# 验证签名逻辑
return True
def parse_message(self, data) -> Optional[BotMessage]:
# 解析消息逻辑
return BotMessage(...)
def format_response(self, response, message) -> WebhookResponse:
# 格式化响应逻辑
return WebhookResponse.success({"text": response.text})
"""
@property
@abstractmethod
def platform_name(self) -> str:
"""
平台标识名称
用于路由匹配和日志标识,如 "feishu", "dingtalk"
"""
pass
@abstractmethod
def verify_request(self, headers: Dict[str, str], body: bytes) -> bool:
"""
验证请求签名
各平台有不同的签名验证机制,需要单独实现。
Args:
headers: HTTP 请求头
body: 请求体原始字节
Returns:
签名是否有效
"""
pass
@abstractmethod
def parse_message(self, data: Dict[str, Any]) -> Optional[BotMessage]:
"""
解析平台消息为统一格式
将平台特定的消息格式转换为 BotMessage。
如果不是需要处理的消息类型(如事件回调),返回 None。
Args:
data: 解析后的 JSON 数据
Returns:
BotMessage 对象,或 None不需要处理
"""
pass
@abstractmethod
def format_response(
self,
response: BotResponse,
message: BotMessage
) -> WebhookResponse:
"""
将统一响应转换为平台格式
Args:
response: 统一响应对象
message: 原始消息对象(用于获取回复目标等信息)
Returns:
WebhookResponse 对象
"""
pass
def handle_challenge(self, data: Dict[str, Any]) -> Optional[WebhookResponse]:
"""
处理平台验证请求
部分平台在配置 Webhook 时会发送验证请求,需要返回特定响应。
子类可重写此方法。
Args:
data: 请求数据
Returns:
验证响应,或 None不是验证请求
"""
return None
def handle_webhook(
self,
headers: Dict[str, str],
body: bytes,
data: Dict[str, Any]
) -> Tuple[Optional[BotMessage], Optional[WebhookResponse]]:
"""
处理 Webhook 请求
这是主入口方法,协调验证、解析等流程。
Args:
headers: HTTP 请求头
body: 请求体原始字节
data: 解析后的 JSON 数据
Returns:
(BotMessage, WebhookResponse) 元组
- 如果是验证请求:(None, challenge_response)
- 如果是普通消息:(message, None) - 响应将在命令处理后生成
- 如果验证失败或无需处理:(None, error_response 或 None)
"""
# 1. 检查是否是验证请求
challenge_response = self.handle_challenge(data)
if challenge_response:
return None, challenge_response
# 2. 验证请求签名
if not self.verify_request(headers, body):
return None, WebhookResponse.error("Invalid signature", 403)
# 3. 解析消息
message = self.parse_message(data)
return message, None