diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 06987111..8dfeb364 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -9,7 +9,7 @@ from nonebot.adapters import Bot as BaseBot from nonebot.drivers import Driver, HTTPRequest, HTTPResponse from .config import Config as FeishuConfig -from .event import Event +from .event import Event, get_event_model from .message import Message, MessageSegment from .utils import log, AESCipher @@ -17,6 +17,48 @@ if TYPE_CHECKING: from nonebot.config import Config +async def _check_reply(bot: "Bot", event: "Event"): + """ + :说明: + + 检查消息中存在的回复,去除并赋值 ``event.reply``, ``event.to_me`` + + :参数: + + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + """ + ... + + +def _check_at_me(bot: "Bot", event: "Event"): + """ + :说明: + + 检查消息开头或结尾是否存在 @机器人,去除并赋值 ``event.to_me`` + + :参数: + + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + """ + ... + + +def _check_nickname(bot: "Bot", event: "Event"): + """ + :说明: + + 检查消息开头是否存在,去除并赋值 ``event.to_me`` + + :参数: + + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + """ + ... + + class Bot(BaseBot): """ 飞书 协议 Bot 适配。继承属性参考 `BaseBot <./#class-basebot>`_ 。 @@ -52,7 +94,7 @@ class Bot(BaseBot): challenge = data.get("challenge") if challenge: - return None, HTTPResponse( + return data.get("token"), HTTPResponse( 200, json.dumps({ "challenge": challenge @@ -85,8 +127,22 @@ class Bot(BaseBot): 处理事件并转换为 `Event <#class-event>`_ """ data = json.loads(message) + print(data) + if data.get("type") == "url_verification": + return + try: - event = Event.parse_obj(message) + header = data["header"] + event_type = header["event_type"] + models = get_event_model(event_type) + for model in models: + try: + event = model.parse_obj(data) + break + except Exception as e: + log("DEBUG", "Event Parser Error", e) + else: + event = Event.parse_obj(data) await handle_event(self, event) except Exception as e: logger.opt(colors=True, exception=e).error( diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 5d7f6dfe..a1743a58 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -1,5 +1,10 @@ -from typing import List, Literal -from pydantic import BaseModel, root_validator +import inspect +import json +from nonebot import message + +from typing import Any, List, Literal, Optional, Type, Union +from pygtrie import StringTrie +from pydantic import BaseModel, root_validator, Field from nonebot.adapters import Event as BaseEvent from nonebot.typing import overrides @@ -7,6 +12,15 @@ from nonebot.typing import overrides from .message import Message, MessageSegment +class EventHeader(BaseModel): + event_id: str + event_type: str + create_time: str + token: str + app_id: str + tenant_key: str + + class Event(BaseEvent): """ 飞书协议事件。各事件字段参考 `飞书文档`_ @@ -14,16 +28,18 @@ class Event(BaseEvent): .. _飞书事件列表文档: https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-list """ - app_id: int - event_type: str + __event__ = "" + schema_: str = Field("", alias='schema') + header: EventHeader + event: Any @overrides(BaseEvent) def get_type(self) -> str: - return self.event_type + return self.header.event_type @overrides(BaseEvent) def get_event_name(self) -> str: - return self.event_type + return self.header.event_type @overrides(BaseEvent) def get_event_description(self) -> str: @@ -69,39 +85,31 @@ class Mention(BaseModel): tenant_key: str -class MessageBody(BaseModel): +class EventMessage(BaseModel): message_id: str - root_id: str - parent_id: str + root_id: Optional[str] + parent_id: Optional[str] create_time: str chat_id: str chat_type: str message_type: str content: Message - mentions: List[Mention] - - plaintext: str + mentions: Optional[List[Mention]] @root_validator(pre=True) - def gen_message(cls, values: dict): - content = [] - for piece in values["content"]: - for segment in piece: - content.append( - MessageSegment(segment["tag"], segment.pop('name', segment))) - - values["content"] = Message(content) + def parse_message(cls, values: dict): + values["content"] = json.loads(values["content"]) return values - @root_validator - def gen_plaintext(cls, values: dict): - values["plaintext"] = str(values["content"]) - return values + +class MessageEventDetail(BaseModel): + sender: Sender + message: EventMessage class MessageEvent(Event): - sender: Sender - message: MessageBody + __event__ = "im.message.receive_v1" + event: MessageEventDetail @overrides(Event) def get_type(self) -> Literal["message", "notice", "meta_event"]: @@ -115,32 +123,40 @@ class MessageEvent(Event): def get_event_description(self) -> str: return ( f"Message[{super().get_type()}]" - f" {self.message.message_id} from {self.sender.sender_id.user_id}" - f" {self.message.content}") + f" {self.event.message.message_id} from {self.event.sender.sender_id.user_id}" + f" {self.event.message.content}") @overrides(Event) def get_message(self) -> Message: - return self.message.content + return self.event.message.content @overrides(Event) def get_plaintext(self) -> str: - return self.message.plaintext + return str(self.event.message.content) @overrides(Event) def get_user_id(self) -> str: - return self.sender.sender_id.user_id + return self.event.sender.sender_id.user_id @overrides(Event) def get_session_id(self) -> str: - return self.sender.sender_id.user_id + return self.event.sender.sender_id.user_id -class PrivateMessageEvent(MessageEvent): - ... +class MessageReader(BaseModel): + reader_id: UserId + read_time: str + tenant_key: str -class GroupMessageEvent(MessageEvent): - ... +class MessageReadEventDetail(BaseModel): + reader: MessageReader + message_id_list: List[str] + + +class MessageReadEvent(Event): + __event__ = "im.message.message_read_v1" + event: MessageReadEventDetail class NoticeEvent(Event): @@ -149,3 +165,26 @@ class NoticeEvent(Event): class MetaEvent(Event): ... + + +_t = StringTrie(separator=".") + +# define `model` first to avoid globals changing while `for` +model = None +for model in globals().values(): + if not inspect.isclass(model) or not issubclass(model, Event): + continue + _t["." + model.__event__] = model + + +def get_event_model(event_name) -> List[Type[Event]]: + """ + :说明: + + 根据事件名获取对应 ``Event Model`` 及 ``FallBack Event Model`` 列表 + + :返回: + + - ``List[Type[Event]]`` + """ + return [model.value for model in _t.prefixes("." + event_name)][::-1]