mirror of
https://github.com/nonebot/nonebot2.git
synced 2024-11-28 04:36:44 +08:00
🚧 basically completed event serialize
This commit is contained in:
parent
5a9798121c
commit
0bb0d16d93
@ -2,9 +2,13 @@ from pprint import pprint
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from nonebot.adapters import Bot as BaseBot
|
from nonebot.adapters import Bot as BaseBot
|
||||||
|
from nonebot.adapters import Event as BaseEvent
|
||||||
from nonebot.drivers import Driver, WebSocket
|
from nonebot.drivers import Driver, WebSocket
|
||||||
|
from nonebot.message import handle_event
|
||||||
from nonebot.typing import overrides
|
from nonebot.typing import overrides
|
||||||
|
|
||||||
|
from .event import Event
|
||||||
|
|
||||||
|
|
||||||
class MiraiBot(BaseBot):
|
class MiraiBot(BaseBot):
|
||||||
|
|
||||||
@ -28,12 +32,13 @@ class MiraiBot(BaseBot):
|
|||||||
|
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def handle_message(self, message: dict):
|
async def handle_message(self, message: dict):
|
||||||
pprint(message)
|
event = Event.new(message)
|
||||||
|
await handle_event(self, event)
|
||||||
|
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def call_api(self, api: str, **data):
|
async def call_api(self, api: str, **data):
|
||||||
return super().call_api(api, **data)
|
return super().call_api(api, **data)
|
||||||
|
|
||||||
@overrides(BaseBot)
|
@overrides(BaseBot)
|
||||||
async def send(self, event: "Event", message: str, **kwargs):
|
async def send(self, event: "BaseEvent", message: str, **kwargs):
|
||||||
return super().send(event, message, **kwargs)
|
return super().send(event, message, **kwargs)
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
from .base import Event, SenderInfo, PrivateSenderInfo, SenderGroup
|
||||||
|
from .message import *
|
||||||
|
from .notice import *
|
||||||
|
from .request import *
|
@ -1,13 +1,12 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from typing import Dict, Any, Optional, Type
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field, ValidationError
|
||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
from nonebot.adapters import Event as BaseEvent
|
from nonebot.adapters import Event as BaseEvent
|
||||||
from nonebot.adapters import Message as BaseMessage
|
from nonebot.adapters import Message as BaseMessage
|
||||||
from nonebot.typing import overrides
|
from nonebot.typing import overrides
|
||||||
|
from nonebot.log import logger
|
||||||
from .constants import EVENT_TYPES
|
|
||||||
|
|
||||||
|
|
||||||
class SenderPermission(str, Enum):
|
class SenderPermission(str, Enum):
|
||||||
@ -29,12 +28,54 @@ class SenderInfo(BaseModel):
|
|||||||
group: SenderGroup
|
group: SenderGroup
|
||||||
|
|
||||||
|
|
||||||
|
class PrivateSenderInfo(BaseModel):
|
||||||
|
id: int
|
||||||
|
nickname: str
|
||||||
|
remark: str
|
||||||
|
|
||||||
|
|
||||||
class Event(BaseEvent):
|
class Event(BaseEvent):
|
||||||
type: str
|
type: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(cls, data: Dict[str, Any]) -> "Event":
|
||||||
|
type = data['type']
|
||||||
|
|
||||||
|
def all_subclasses(cls: Type[Event]):
|
||||||
|
return set(cls.__subclasses__()).union(
|
||||||
|
[s for c in cls.__subclasses__() for s in all_subclasses(c)])
|
||||||
|
|
||||||
|
event_class: Optional[Type[Event]] = None
|
||||||
|
for subclass in all_subclasses(cls):
|
||||||
|
if subclass.__name__ != type:
|
||||||
|
continue
|
||||||
|
event_class = subclass
|
||||||
|
|
||||||
|
if event_class is None:
|
||||||
|
return Event.parse_obj(data)
|
||||||
|
|
||||||
|
while issubclass(event_class, Event):
|
||||||
|
try:
|
||||||
|
return event_class.parse_obj(data)
|
||||||
|
except ValidationError as e:
|
||||||
|
logger.info(
|
||||||
|
f'Failed to parse {data} to class {event_class.__name__}: {e}. '
|
||||||
|
'Fallback to parent class.')
|
||||||
|
event_class = event_class.__base__
|
||||||
|
|
||||||
|
raise ValueError(f'Failed to serialize {data}.')
|
||||||
|
|
||||||
@overrides(BaseEvent)
|
@overrides(BaseEvent)
|
||||||
def get_type(self) -> Literal["message", "notice", "request", "meta_event"]:
|
def get_type(self) -> Literal["message", "notice", "request", "meta_event"]:
|
||||||
return EVENT_TYPES.get(self.type, 'meta_event')
|
from . import message, notice, request
|
||||||
|
if isinstance(self, message.MessageEvent):
|
||||||
|
return 'message'
|
||||||
|
elif isinstance(self, notice.NoticeEvent):
|
||||||
|
return 'notice'
|
||||||
|
elif isinstance(self, request.RequestEvent):
|
||||||
|
return 'request'
|
||||||
|
else:
|
||||||
|
return 'meta_event'
|
||||||
|
|
||||||
@overrides(BaseEvent)
|
@overrides(BaseEvent)
|
||||||
def get_event_name(self) -> str:
|
def get_event_name(self) -> str:
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
from typing import List, Dict
|
|
||||||
from typing_extensions import Literal
|
|
||||||
|
|
||||||
EventType = Literal["message", "notice", "request", "meta_event"]
|
|
||||||
|
|
||||||
_EVENT_CLASSIFY: Dict[EventType, List[str]] = {
|
|
||||||
# XXX Reference: https://github.com/project-mirai/mirai-api-http/blob/v1.9.7/docs/EventType.md
|
|
||||||
'meta_event': [
|
|
||||||
'BotOnlineEvent', 'BotOfflineEventActive', 'BotOfflineEventForce',
|
|
||||||
'BotOfflineEventDropped', 'BotReloginEvent'
|
|
||||||
],
|
|
||||||
'notice': [
|
|
||||||
'BotGroupPermissionChangeEvent', 'BotMuteEvent', 'BotUnmuteEvent',
|
|
||||||
'BotJoinGroupEvent', 'BotLeaveEventActive', 'BotLeaveEventKick',
|
|
||||||
'GroupRecallEvent', 'FriendRecallEvent', 'GroupNameChangeEvent',
|
|
||||||
'GroupEntranceAnnouncementChangeEvent', 'GroupMuteAllEvent',
|
|
||||||
'GroupAllowAnonymousChatEvent', 'GroupAllowConfessTalkEvent',
|
|
||||||
'GroupAllowMemberInviteEvent', 'MemberJoinEvent',
|
|
||||||
'MemberLeaveEventKick', 'MemberLeaveEventQuit', 'MemberCardChangeEvent',
|
|
||||||
'MemberSpecialTitleChangeEvent', 'MemberPermissionChangeEvent',
|
|
||||||
'MemberMuteEvent', 'MemberUnmuteEvent'
|
|
||||||
],
|
|
||||||
'request': [
|
|
||||||
'NewFriendRequestEvent', 'MemberJoinRequestEvent',
|
|
||||||
'BotInvitedJoinGroupRequestEvent'
|
|
||||||
],
|
|
||||||
'message': ['GroupMessage', 'FriendMessage', 'TempMessage']
|
|
||||||
}
|
|
||||||
EVENT_TYPES: Dict[str, EventType] = {}
|
|
||||||
for event_type, events in _EVENT_CLASSIFY.items():
|
|
||||||
_EVENT_TYPES.update({k: event_type for k in events}) # type: ignore
|
|
40
nonebot/adapters/mirai/event/message.py
Normal file
40
nonebot/adapters/mirai/event/message.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
from .base import Event, SenderInfo, PrivateSenderInfo
|
||||||
|
|
||||||
|
from ..message import MessageChain
|
||||||
|
from nonebot.typing import overrides
|
||||||
|
|
||||||
|
|
||||||
|
class MessageEvent(Event):
|
||||||
|
message_chain: MessageChain = Field(alias='messageChain')
|
||||||
|
sender: SenderInfo
|
||||||
|
|
||||||
|
@overrides(Event)
|
||||||
|
def get_message(self) -> MessageChain:
|
||||||
|
return self.message_chain
|
||||||
|
|
||||||
|
@overrides(Event)
|
||||||
|
def get_plaintext(self) -> str:
|
||||||
|
return self.message_chain.__str__()
|
||||||
|
|
||||||
|
@overrides(Event)
|
||||||
|
def get_user_id(self) -> str:
|
||||||
|
return str(self.sender.id)
|
||||||
|
|
||||||
|
@overrides(Event)
|
||||||
|
def get_session_id(self) -> str:
|
||||||
|
return self.get_user_id()
|
||||||
|
|
||||||
|
|
||||||
|
class GroupMessage(MessageEvent):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FriendMessage(MessageEvent):
|
||||||
|
sender: PrivateSenderInfo
|
||||||
|
|
||||||
|
|
||||||
|
class TempMessage(MessageEvent):
|
||||||
|
pass
|
@ -5,34 +5,34 @@ from pydantic import Field
|
|||||||
from .base import Event, SenderGroup, SenderInfo, SenderPermission
|
from .base import Event, SenderGroup, SenderInfo, SenderPermission
|
||||||
|
|
||||||
|
|
||||||
class BaseNoticeEvent(Event):
|
class NoticeEvent(Event):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BaseMuteEvent(BaseNoticeEvent):
|
class MuteEvent(NoticeEvent):
|
||||||
operator: SenderInfo
|
operator: SenderInfo
|
||||||
|
|
||||||
|
|
||||||
class BotMuteEvent(BaseMuteEvent):
|
class BotMuteEvent(MuteEvent):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BotUnmuteEvent(BaseMuteEvent):
|
class BotUnmuteEvent(MuteEvent):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MemberMuteEvent(BaseMuteEvent):
|
class MemberMuteEvent(MuteEvent):
|
||||||
duration_seconds: int = Field(alias='durationSeconds')
|
duration_seconds: int = Field(alias='durationSeconds')
|
||||||
member: SenderInfo
|
member: SenderInfo
|
||||||
operator: Optional[SenderInfo] = None
|
operator: Optional[SenderInfo] = None
|
||||||
|
|
||||||
|
|
||||||
class MemberUnmuteEvent(BaseMuteEvent):
|
class MemberUnmuteEvent(MuteEvent):
|
||||||
member: SenderInfo
|
member: SenderInfo
|
||||||
operator: Optional[SenderInfo] = None
|
operator: Optional[SenderInfo] = None
|
||||||
|
|
||||||
|
|
||||||
class BotJoinGroupEvent(BaseNoticeEvent):
|
class BotJoinGroupEvent(NoticeEvent):
|
||||||
group: SenderGroup
|
group: SenderGroup
|
||||||
|
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ class BotLeaveEventKick(BotJoinGroupEvent):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MemberJoinEvent(BaseNoticeEvent):
|
class MemberJoinEvent(NoticeEvent):
|
||||||
member: SenderInfo
|
member: SenderInfo
|
||||||
|
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ class MemberLeaveEventKick(MemberJoinEvent):
|
|||||||
operator: Optional[SenderInfo] = None
|
operator: Optional[SenderInfo] = None
|
||||||
|
|
||||||
|
|
||||||
class FriendRecallEvent(BaseNoticeEvent):
|
class FriendRecallEvent(NoticeEvent):
|
||||||
author_id: int = Field(alias='authorId')
|
author_id: int = Field(alias='authorId')
|
||||||
message_id: int = Field(alias='messageId')
|
message_id: int = Field(alias='messageId')
|
||||||
time: int
|
time: int
|
||||||
@ -68,7 +68,7 @@ class GroupRecallEvent(FriendRecallEvent):
|
|||||||
operator: Optional[SenderInfo] = None
|
operator: Optional[SenderInfo] = None
|
||||||
|
|
||||||
|
|
||||||
class GroupStateChangeEvent(BaseNoticeEvent):
|
class GroupStateChangeEvent(NoticeEvent):
|
||||||
origin: Any
|
origin: Any
|
||||||
current: Any
|
current: Any
|
||||||
group: SenderGroup
|
group: SenderGroup
|
||||||
@ -105,7 +105,7 @@ class GroupAllowMemberInviteEvent(GroupStateChangeEvent):
|
|||||||
current: bool
|
current: bool
|
||||||
|
|
||||||
|
|
||||||
class MemberStateChangeEvent(BaseNoticeEvent):
|
class MemberStateChangeEvent(NoticeEvent):
|
||||||
member: SenderInfo
|
member: SenderInfo
|
||||||
operator: Optional[SenderInfo] = None
|
operator: Optional[SenderInfo] = None
|
||||||
|
|
||||||
|
@ -1 +1,26 @@
|
|||||||
|
from pydantic import Field
|
||||||
|
|
||||||
from .base import Event
|
from .base import Event
|
||||||
|
|
||||||
|
|
||||||
|
class RequestEvent(Event):
|
||||||
|
event_id: int = Field(alias='eventId')
|
||||||
|
message: str
|
||||||
|
nick: str
|
||||||
|
|
||||||
|
|
||||||
|
class NewFriendRequestEvent(RequestEvent):
|
||||||
|
from_id: int = Field(alias='fromId')
|
||||||
|
group_id: int = Field(0, alias='groupId')
|
||||||
|
|
||||||
|
|
||||||
|
class MemberJoinRequestEvent(RequestEvent):
|
||||||
|
from_id: int = Field(alias='fromId')
|
||||||
|
group_id: int = Field(alias='groupId')
|
||||||
|
group_name: str = Field(alias='groupName')
|
||||||
|
|
||||||
|
|
||||||
|
class BotInvitedJoinGroupRequestEvent(RequestEvent):
|
||||||
|
from_id: int = Field(alias='fromId')
|
||||||
|
group_id: int = Field(alias='groupId')
|
||||||
|
group_name: str = Field(alias='groupName')
|
||||||
|
@ -1 +1,100 @@
|
|||||||
from nonebot.adapters import Message
|
from enum import Enum
|
||||||
|
from typing import Any, Dict, List, Union, Iterable
|
||||||
|
|
||||||
|
from pydantic import validate_arguments
|
||||||
|
|
||||||
|
from nonebot.adapters import Message as BaseMessage
|
||||||
|
from nonebot.adapters import MessageSegment as BaseMessageSegment
|
||||||
|
from nonebot.typing import overrides
|
||||||
|
|
||||||
|
|
||||||
|
class MessageType(str, Enum):
|
||||||
|
SOURCE = 'Source'
|
||||||
|
QUOTE = 'Quote'
|
||||||
|
AT = 'At'
|
||||||
|
AT_ALL = 'AtAll'
|
||||||
|
FACE = 'Face'
|
||||||
|
PLAIN = 'Plain'
|
||||||
|
IMAGE = 'Image'
|
||||||
|
FLASH_IMAGE = 'FlashImage'
|
||||||
|
VOICE = 'Voice'
|
||||||
|
XML = 'Xml'
|
||||||
|
JSON = 'Json'
|
||||||
|
APP = 'App'
|
||||||
|
POKE = 'Poke'
|
||||||
|
|
||||||
|
|
||||||
|
class MessageSegment(BaseMessageSegment):
|
||||||
|
type: MessageType
|
||||||
|
data: Dict[str, Any]
|
||||||
|
|
||||||
|
@overrides(BaseMessageSegment)
|
||||||
|
@validate_arguments
|
||||||
|
def __init__(self, type: MessageType, **data):
|
||||||
|
super().__init__(type=type, data=data)
|
||||||
|
|
||||||
|
@overrides(BaseMessageSegment)
|
||||||
|
def __str__(self) -> str:
|
||||||
|
if self.is_text():
|
||||||
|
return self.data.get('text', '')
|
||||||
|
return '[mirai:%s]' % ','.join([
|
||||||
|
self.type.value,
|
||||||
|
*map(
|
||||||
|
lambda s: '%s=%r' % s,
|
||||||
|
self.data.items(),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
@overrides(BaseMessageSegment)
|
||||||
|
def __add__(self, other) -> "MessageChain":
|
||||||
|
return MessageChain(self) + other
|
||||||
|
|
||||||
|
@overrides(BaseMessageSegment)
|
||||||
|
def __radd__(self, other) -> "MessageChain":
|
||||||
|
return MessageChain(other) + self
|
||||||
|
|
||||||
|
@overrides(BaseMessageSegment)
|
||||||
|
def is_text(self) -> bool:
|
||||||
|
return self.type == MessageType.PLAIN
|
||||||
|
|
||||||
|
def as_dict(self) -> Dict[str, Any]:
|
||||||
|
return {'type': self.type.value, **self.data}
|
||||||
|
|
||||||
|
|
||||||
|
class MessageChain(BaseMessage):
|
||||||
|
|
||||||
|
@overrides(BaseMessage)
|
||||||
|
def __init__(self, message: Union[List[Dict[str, Any]], MessageSegment],
|
||||||
|
**kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
if isinstance(message, MessageSegment):
|
||||||
|
self.append(message)
|
||||||
|
elif isinstance(message, Iterable):
|
||||||
|
self.extend(self._construct(message))
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f'Type {type(message).__name__} is not supported in mirai adapter.'
|
||||||
|
)
|
||||||
|
|
||||||
|
@overrides(BaseMessage)
|
||||||
|
def _construct(
|
||||||
|
self, message: Iterable[Union[Dict[str, Any], MessageSegment]]
|
||||||
|
) -> List[MessageSegment]:
|
||||||
|
if isinstance(message, str):
|
||||||
|
raise ValueError(
|
||||||
|
"String operation is not supported in mirai adapter")
|
||||||
|
return [
|
||||||
|
*map(
|
||||||
|
lambda segment: segment if isinstance(segment, MessageSegment)
|
||||||
|
else MessageSegment(**segment), message)
|
||||||
|
]
|
||||||
|
|
||||||
|
def export(self) -> List[Dict[str, Any]]:
|
||||||
|
chain: List[Dict[str, Any]] = []
|
||||||
|
for segment in self.copy():
|
||||||
|
segment: MessageSegment
|
||||||
|
chain.append({'type': segment.type.value, **segment.data})
|
||||||
|
return chain
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'<{self.__class__.__name__} {[*self.copy()]}>'
|
||||||
|
Loading…
Reference in New Issue
Block a user