Files
NeoBot/models/event.py

192 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
事件模型模块
定义了 Event 类和 MessageSegment 类,用于封装 OneBot 11 的上报事件和消息段。
"""
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, TYPE_CHECKING
from .sender import Sender
if TYPE_CHECKING:
from core.bot import Bot
@dataclass
class MessageSegment:
"""
消息段,对应 OneBot 11 标准中的消息段对象
"""
type: str
"""消息段类型,如 text, image, at 等"""
data: Dict[str, Any]
"""消息段数据"""
@property
def text(self) -> str:
"""
获取文本内容(仅当 type 为 text 时有效)
:return: 文本内容
"""
return self.data.get("text", "") if self.type == "text" else ""
@property
def image_url(self) -> str:
"""
获取图片 URL仅当 type 为 image 时有效)
:return: 图片 URL
"""
return self.data.get("url", "") if self.type == "image" else ""
def is_at(self, user_id: int = None) -> bool:
"""
判断是否为 @某人
:param user_id: 指定的 QQ 号,如果为 None 则只判断是否为 at 类型
:return: 是否匹配
"""
if self.type != "at":
return False
if user_id is None:
return True
return str(self.data.get("qq")) == str(user_id)
def __repr__(self):
return f"[MS:{self.type}:{self.data}]"
@dataclass
class Event:
"""
事件类,封装了 OneBot 11 的上报事件
"""
post_type: str
"""上报类型: message, notice, request, meta_event"""
self_id: int
"""收到消息的机器人 QQ 号"""
time: int
"""事件发生的时间戳"""
# --- 消息事件字段 ---
message_type: Optional[str] = None
"""消息类型: group, private"""
sub_type: Optional[str] = None
"""消息子类型"""
message_id: Optional[int] = None
"""消息 ID"""
user_id: Optional[int] = None
"""发送者 QQ 号"""
raw_message: Optional[str] = None
"""原始消息内容"""
message: List[MessageSegment] = field(default_factory=list)
"""消息内容列表"""
sender: Optional[Sender] = None
"""发送者信息"""
group_id: Optional[int] = None
"""群号"""
target_id: Optional[int] = None
"""目标 QQ 号"""
# --- 通知事件字段 ---
notice_type: Optional[str] = None
"""通知类型"""
operator_id: Optional[int] = None
"""操作者 QQ 号"""
duration: Optional[int] = None
"""时长"""
honor_type: Optional[str] = None
"""荣誉类型"""
# --- 请求事件字段 ---
request_type: Optional[str] = None
"""请求类型"""
flag: Optional[str] = None
"""请求 flag"""
comment: Optional[str] = None
"""验证信息"""
# 注入的 Bot 实例
bot: Optional["Bot"] = field(default=None, init=False)
"""关联的 Bot 实例"""
async def reply(self, message: str) -> dict:
"""
快捷回复消息
:param message: 回复内容
:return: API 响应结果
"""
if not self.bot:
return {"status": "failed", "msg": "Bot instance not attached to event"}
return await self.bot.send(self, message)
@classmethod
def from_dict(cls, data: dict):
"""
从字典创建 Event 对象
:param data: 原始事件数据字典
:return: Event 对象
"""
raw_msg_array = data.get("message")
segments = []
if isinstance(raw_msg_array, list):
segments = [
MessageSegment(type=seg["type"], data=seg["data"])
for seg in raw_msg_array
]
sender_data = data.get("sender")
sender_obj = None
if isinstance(sender_data, dict):
sender_obj = Sender(
**{k: v for k, v in sender_data.items() if k in Sender.__annotations__}
)
# 数据整合
processed_data = data.copy()
processed_data["message"] = segments
processed_data["sender"] = sender_obj
# 字段过滤:只提取 dataclass 中定义的字段
valid_data = {
k: v for k, v in processed_data.items() if k in cls.__annotations__
}
return cls(**valid_data)
# --- 快捷判断工具 ---
@property
def is_message(self) -> bool:
"""是否为消息事件"""
return self.post_type == "message"
@property
def is_notice(self) -> bool:
"""是否为通知事件"""
return self.post_type == "notice"
@property
def is_request(self) -> bool:
"""是否为请求事件"""
return self.post_type == "request"