Files
AstrBot/src-new/astrbot_sdk/api/message/components.py
whatevertogo cdf72b75a4 feat: 实现 v4 API 兼容层,支持旧版插件导入路径
新增模块:
- api/basic/: AstrBotConfig, BaseConversationManager, Conversation
- api/event/: AstrMessageEvent, AstrBotMessage, EventType, MessageType,
  MessageSession, EventResult 等核心事件类型
- api/message/: MessageChain 及所有消息组件 (Plain, At, Image, File 等)
- api/platform/: PlatformMetadata 平台元数据类型
- api/provider/: LLMResponse 提供者响应实体
- api/star/star.py: StarMetadata 插件元数据类型

更新模块:
- api/__init__.py: 导出所有子模块
- api/components/: 扩展 Command 兼容性
- api/event/filter.py: 增强 filter 装饰器兼容性
- context.py, decorators.py, events.py: 顶层兼容入口

测试更新:
- test_api_event_filter.py: 验证 filter 兼容性
- test_api_modules.py: 验证新模块可导入
- test_handler_dispatcher.py: 验证处理器分发

文档更新:
- AGENTS.md/CLAUDE.md: 添加兼容层设计说明,避免重复造轮子

设计原则:
- 兼容层通过 thin re-export 方式暴露旧版 API
- 不复制独立运行时逻辑,保持架构清晰
- 新版推荐使用顶层模块导入路径
2026-03-13 03:17:03 +08:00

208 lines
4.2 KiB
Python

"""旧版消息组件兼容类型。"""
from __future__ import annotations
from enum import Enum
from typing import Literal
from pydantic import BaseModel, Field
class ComponentType(str, Enum):
Plain = "Plain"
Image = "Image"
Record = "Record"
Video = "Video"
File = "File"
Face = "Face"
At = "At"
Node = "Node"
Nodes = "Nodes"
Poke = "Poke"
Reply = "Reply"
Forward = "Forward"
RPS = "RPS"
Dice = "Dice"
Shake = "Shake"
Share = "Share"
Contact = "Contact"
Location = "Location"
Music = "Music"
Json = "Json"
Unknown = "Unknown"
WechatEmoji = "WechatEmoji"
CompT = ComponentType
class BaseMessageComponent(BaseModel):
type: CompT
def to_dict(self) -> dict:
return self.model_dump()
class Plain(BaseMessageComponent):
type: Literal[CompT.Plain] = CompT.Plain
text: str
class Image(BaseMessageComponent):
type: Literal[CompT.Image] = CompT.Image
file: str
class Record(BaseMessageComponent):
type: Literal[CompT.Record] = CompT.Record
file: str
class Video(BaseMessageComponent):
type: Literal[CompT.Video] = CompT.Video
file: str
class File(BaseMessageComponent):
type: Literal[CompT.File] = CompT.File
file_name: str
mime_type: str | None = None
file: str
class At(BaseMessageComponent):
type: Literal[CompT.At] = CompT.At
user_id: str | None = None
user_name: str | None = None
class AtAll(At):
user_id: str = "all"
class Reply(BaseMessageComponent):
type: Literal[CompT.Reply] = CompT.Reply
id: str | int
chain: list[BaseMessageComponent] = Field(default_factory=list)
sender_id: int | str | None = 0
sender_nickname: str | None = ""
time: int | None = 0
message_str: str | None = ""
class Node(BaseMessageComponent):
type: Literal[CompT.Node] = CompT.Node
sender_id: str
nickname: str | None = None
content: list[BaseMessageComponent] = Field(default_factory=list)
class Nodes(BaseMessageComponent):
type: Literal[CompT.Nodes] = CompT.Nodes
nodes: list[Node] = Field(default_factory=list)
class Face(BaseMessageComponent):
type: Literal[CompT.Face] = CompT.Face
id: int
class RPS(BaseMessageComponent):
type: Literal[CompT.RPS] = CompT.RPS
class Dice(BaseMessageComponent):
type: Literal[CompT.Dice] = CompT.Dice
class Shake(BaseMessageComponent):
type: Literal[CompT.Shake] = CompT.Shake
class Share(BaseMessageComponent):
type: Literal[CompT.Share] = CompT.Share
url: str
title: str
content: str | None = ""
image: str | None = ""
class Contact(BaseMessageComponent):
type: Literal[CompT.Contact] = CompT.Contact
_type: str
id: int | None = 0
class Location(BaseMessageComponent):
type: Literal[CompT.Location] = CompT.Location
lat: float
lon: float
title: str | None = ""
content: str | None = ""
class Music(BaseMessageComponent):
type: Literal[CompT.Music] = CompT.Music
_type: str
id: int | None = 0
url: str | None = ""
audio: str | None = ""
title: str | None = ""
content: str | None = ""
image: str | None = ""
class Poke(BaseMessageComponent):
type: Literal[CompT.Poke] = CompT.Poke
id: int | None = 0
qq: int | None = 0
class Forward(BaseMessageComponent):
type: Literal[CompT.Forward] = CompT.Forward
id: str
class Json(BaseMessageComponent):
type: Literal[CompT.Json] = CompT.Json
data: dict
class Unknown(BaseMessageComponent):
type: Literal[CompT.Unknown] = CompT.Unknown
text: str
class WechatEmoji(BaseMessageComponent):
type: Literal[CompT.WechatEmoji] = CompT.WechatEmoji
md5: str | None = ""
md5_len: int | None = 0
cdnurl: str | None = ""
ComponentTypes = {
"plain": Plain,
"text": Plain,
"image": Image,
"record": Record,
"video": Video,
"file": File,
"face": Face,
"at": At,
"rps": RPS,
"dice": Dice,
"shake": Shake,
"share": Share,
"contact": Contact,
"location": Location,
"music": Music,
"reply": Reply,
"poke": Poke,
"forward": Forward,
"node": Node,
"nodes": Nodes,
"json": Json,
"unknown": Unknown,
"WechatEmoji": WechatEmoji,
}