implement event parser

This commit is contained in:
StarHeartHunt 2021-07-04 14:19:10 +08:00
parent 20c3c24a6d
commit 7a8f881b04
2 changed files with 134 additions and 39 deletions

View File

@ -9,7 +9,7 @@ from nonebot.adapters import Bot as BaseBot
from nonebot.drivers import Driver, HTTPRequest, HTTPResponse from nonebot.drivers import Driver, HTTPRequest, HTTPResponse
from .config import Config as FeishuConfig from .config import Config as FeishuConfig
from .event import Event from .event import Event, get_event_model
from .message import Message, MessageSegment from .message import Message, MessageSegment
from .utils import log, AESCipher from .utils import log, AESCipher
@ -17,6 +17,48 @@ if TYPE_CHECKING:
from nonebot.config import Config 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): class Bot(BaseBot):
""" """
飞书 协议 Bot 适配继承属性参考 `BaseBot <./#class-basebot>`_ 。 飞书 协议 Bot 适配继承属性参考 `BaseBot <./#class-basebot>`_ 。
@ -52,7 +94,7 @@ class Bot(BaseBot):
challenge = data.get("challenge") challenge = data.get("challenge")
if challenge: if challenge:
return None, HTTPResponse( return data.get("token"), HTTPResponse(
200, 200,
json.dumps({ json.dumps({
"challenge": challenge "challenge": challenge
@ -85,8 +127,22 @@ class Bot(BaseBot):
处理事件并转换为 `Event <#class-event>`_ 处理事件并转换为 `Event <#class-event>`_
""" """
data = json.loads(message) data = json.loads(message)
print(data)
if data.get("type") == "url_verification":
return
try: 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) await handle_event(self, event)
except Exception as e: except Exception as e:
logger.opt(colors=True, exception=e).error( logger.opt(colors=True, exception=e).error(

View File

@ -1,5 +1,10 @@
from typing import List, Literal import inspect
from pydantic import BaseModel, root_validator 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.adapters import Event as BaseEvent
from nonebot.typing import overrides from nonebot.typing import overrides
@ -7,6 +12,15 @@ from nonebot.typing import overrides
from .message import Message, MessageSegment 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): class Event(BaseEvent):
""" """
飞书协议事件各事件字段参考 `飞书文档`_ 飞书协议事件各事件字段参考 `飞书文档`_
@ -14,16 +28,18 @@ class Event(BaseEvent):
.. _飞书事件列表文档: .. _飞书事件列表文档:
https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-list https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-list
""" """
app_id: int __event__ = ""
event_type: str schema_: str = Field("", alias='schema')
header: EventHeader
event: Any
@overrides(BaseEvent) @overrides(BaseEvent)
def get_type(self) -> str: def get_type(self) -> str:
return self.event_type return self.header.event_type
@overrides(BaseEvent) @overrides(BaseEvent)
def get_event_name(self) -> str: def get_event_name(self) -> str:
return self.event_type return self.header.event_type
@overrides(BaseEvent) @overrides(BaseEvent)
def get_event_description(self) -> str: def get_event_description(self) -> str:
@ -69,39 +85,31 @@ class Mention(BaseModel):
tenant_key: str tenant_key: str
class MessageBody(BaseModel): class EventMessage(BaseModel):
message_id: str message_id: str
root_id: str root_id: Optional[str]
parent_id: str parent_id: Optional[str]
create_time: str create_time: str
chat_id: str chat_id: str
chat_type: str chat_type: str
message_type: str message_type: str
content: Message content: Message
mentions: List[Mention] mentions: Optional[List[Mention]]
plaintext: str
@root_validator(pre=True) @root_validator(pre=True)
def gen_message(cls, values: dict): def parse_message(cls, values: dict):
content = [] values["content"] = json.loads(values["content"])
for piece in values["content"]:
for segment in piece:
content.append(
MessageSegment(segment["tag"], segment.pop('name', segment)))
values["content"] = Message(content)
return values return values
@root_validator
def gen_plaintext(cls, values: dict): class MessageEventDetail(BaseModel):
values["plaintext"] = str(values["content"]) sender: Sender
return values message: EventMessage
class MessageEvent(Event): class MessageEvent(Event):
sender: Sender __event__ = "im.message.receive_v1"
message: MessageBody event: MessageEventDetail
@overrides(Event) @overrides(Event)
def get_type(self) -> Literal["message", "notice", "meta_event"]: def get_type(self) -> Literal["message", "notice", "meta_event"]:
@ -115,32 +123,40 @@ class MessageEvent(Event):
def get_event_description(self) -> str: def get_event_description(self) -> str:
return ( return (
f"Message[{super().get_type()}]" f"Message[{super().get_type()}]"
f" {self.message.message_id} from {self.sender.sender_id.user_id}" f" {self.event.message.message_id} from {self.event.sender.sender_id.user_id}"
f" {self.message.content}") f" {self.event.message.content}")
@overrides(Event) @overrides(Event)
def get_message(self) -> Message: def get_message(self) -> Message:
return self.message.content return self.event.message.content
@overrides(Event) @overrides(Event)
def get_plaintext(self) -> str: def get_plaintext(self) -> str:
return self.message.plaintext return str(self.event.message.content)
@overrides(Event) @overrides(Event)
def get_user_id(self) -> str: def get_user_id(self) -> str:
return self.sender.sender_id.user_id return self.event.sender.sender_id.user_id
@overrides(Event) @overrides(Event)
def get_session_id(self) -> str: 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): class NoticeEvent(Event):
@ -149,3 +165,26 @@ class NoticeEvent(Event):
class MetaEvent(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]