80 lines
2.2 KiB
Python
80 lines
2.2 KiB
Python
"""消息适配器 ABC — 所有渠道适配器的基类。
|
||
|
||
与 KBAdapter 的 authenticate()/close() 生命周期方法对齐:
|
||
verify_signature() 对应 authenticate()。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import abc
|
||
from dataclasses import dataclass, field
|
||
from enum import Enum
|
||
from typing import Any
|
||
|
||
|
||
class ChannelType(str, Enum):
|
||
"""支持的消息平台渠道。"""
|
||
|
||
FEISHU = "feishu"
|
||
DINGTALK = "dingtalk"
|
||
WECOM = "wecom"
|
||
SLACK = "slack"
|
||
|
||
|
||
class MessageDirection(str, Enum):
|
||
"""消息流向。"""
|
||
|
||
INBOUND = "inbound"
|
||
OUTBOUND = "outbound"
|
||
|
||
|
||
@dataclass
|
||
class IncomingMessage:
|
||
"""标准化入站消息 — 所有渠道适配器将平台特定格式转换为此结构。"""
|
||
|
||
channel: ChannelType
|
||
platform_message_id: str
|
||
user_id: str # 平台用户 ID
|
||
chat_id: str # 群组/会话 ID
|
||
content: str # 消息文本
|
||
raw_event: dict[str, Any] = field(default_factory=dict) # 原始事件
|
||
timestamp: str = ""
|
||
|
||
|
||
@dataclass
|
||
class OutgoingMessage:
|
||
"""标准化出站消息。"""
|
||
|
||
channel: ChannelType
|
||
chat_id: str
|
||
content: str
|
||
reply_to_message_id: str | None = None
|
||
|
||
|
||
class MessageAdapter(abc.ABC):
|
||
"""消息适配器 ABC。
|
||
|
||
生命周期:
|
||
__init__ → verify_signature() → receive_message() → send_message() → close()
|
||
|
||
子类必须实现全部抽象方法。verify_signature 失败时调用方应拒绝处理
|
||
(webhook 端点 fail-closed:Redis 不可用或签名校验失败均返回 503/401,
|
||
不可跳过 nonce dedup 直接处理消息)。
|
||
"""
|
||
|
||
@abc.abstractmethod
|
||
async def verify_signature(self, headers: dict[str, str], body: bytes) -> bool:
|
||
"""验证平台签名/token。返回 True 表示请求可信。"""
|
||
|
||
@abc.abstractmethod
|
||
async def receive_message(self, headers: dict[str, str], body: bytes) -> IncomingMessage:
|
||
"""从 webhook 请求中解析标准化消息。"""
|
||
|
||
@abc.abstractmethod
|
||
async def send_message(self, message: OutgoingMessage) -> bool:
|
||
"""向平台发送消息。返回 True 表示发送成功。"""
|
||
|
||
@abc.abstractmethod
|
||
async def close(self) -> None:
|
||
"""释放资源(HTTP 客户端、连接池等)。"""
|