mirror of
https://github.com/nonebot/nonebot2.git
synced 2024-11-27 18:45:05 +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 nonebot.adapters import Bot as BaseBot
|
||||
from nonebot.adapters import Event as BaseEvent
|
||||
from nonebot.drivers import Driver, WebSocket
|
||||
from nonebot.message import handle_event
|
||||
from nonebot.typing import overrides
|
||||
|
||||
from .event import Event
|
||||
|
||||
|
||||
class MiraiBot(BaseBot):
|
||||
|
||||
@ -28,12 +32,13 @@ class MiraiBot(BaseBot):
|
||||
|
||||
@overrides(BaseBot)
|
||||
async def handle_message(self, message: dict):
|
||||
pprint(message)
|
||||
event = Event.new(message)
|
||||
await handle_event(self, event)
|
||||
|
||||
@overrides(BaseBot)
|
||||
async def call_api(self, api: str, **data):
|
||||
return super().call_api(api, **data)
|
||||
|
||||
@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)
|
||||
|
@ -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 pydantic import BaseModel, Field
|
||||
from typing import Dict, Any, Optional, Type
|
||||
from pydantic import BaseModel, Field, ValidationError
|
||||
from typing_extensions import Literal
|
||||
|
||||
from nonebot.adapters import Event as BaseEvent
|
||||
from nonebot.adapters import Message as BaseMessage
|
||||
from nonebot.typing import overrides
|
||||
|
||||
from .constants import EVENT_TYPES
|
||||
from nonebot.log import logger
|
||||
|
||||
|
||||
class SenderPermission(str, Enum):
|
||||
@ -29,12 +28,54 @@ class SenderInfo(BaseModel):
|
||||
group: SenderGroup
|
||||
|
||||
|
||||
class PrivateSenderInfo(BaseModel):
|
||||
id: int
|
||||
nickname: str
|
||||
remark: str
|
||||
|
||||
|
||||
class Event(BaseEvent):
|
||||
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)
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
class BaseNoticeEvent(Event):
|
||||
class NoticeEvent(Event):
|
||||
pass
|
||||
|
||||
|
||||
class BaseMuteEvent(BaseNoticeEvent):
|
||||
class MuteEvent(NoticeEvent):
|
||||
operator: SenderInfo
|
||||
|
||||
|
||||
class BotMuteEvent(BaseMuteEvent):
|
||||
class BotMuteEvent(MuteEvent):
|
||||
pass
|
||||
|
||||
|
||||
class BotUnmuteEvent(BaseMuteEvent):
|
||||
class BotUnmuteEvent(MuteEvent):
|
||||
pass
|
||||
|
||||
|
||||
class MemberMuteEvent(BaseMuteEvent):
|
||||
class MemberMuteEvent(MuteEvent):
|
||||
duration_seconds: int = Field(alias='durationSeconds')
|
||||
member: SenderInfo
|
||||
operator: Optional[SenderInfo] = None
|
||||
|
||||
|
||||
class MemberUnmuteEvent(BaseMuteEvent):
|
||||
class MemberUnmuteEvent(MuteEvent):
|
||||
member: SenderInfo
|
||||
operator: Optional[SenderInfo] = None
|
||||
|
||||
|
||||
class BotJoinGroupEvent(BaseNoticeEvent):
|
||||
class BotJoinGroupEvent(NoticeEvent):
|
||||
group: SenderGroup
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ class BotLeaveEventKick(BotJoinGroupEvent):
|
||||
pass
|
||||
|
||||
|
||||
class MemberJoinEvent(BaseNoticeEvent):
|
||||
class MemberJoinEvent(NoticeEvent):
|
||||
member: SenderInfo
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ class MemberLeaveEventKick(MemberJoinEvent):
|
||||
operator: Optional[SenderInfo] = None
|
||||
|
||||
|
||||
class FriendRecallEvent(BaseNoticeEvent):
|
||||
class FriendRecallEvent(NoticeEvent):
|
||||
author_id: int = Field(alias='authorId')
|
||||
message_id: int = Field(alias='messageId')
|
||||
time: int
|
||||
@ -68,7 +68,7 @@ class GroupRecallEvent(FriendRecallEvent):
|
||||
operator: Optional[SenderInfo] = None
|
||||
|
||||
|
||||
class GroupStateChangeEvent(BaseNoticeEvent):
|
||||
class GroupStateChangeEvent(NoticeEvent):
|
||||
origin: Any
|
||||
current: Any
|
||||
group: SenderGroup
|
||||
@ -105,7 +105,7 @@ class GroupAllowMemberInviteEvent(GroupStateChangeEvent):
|
||||
current: bool
|
||||
|
||||
|
||||
class MemberStateChangeEvent(BaseNoticeEvent):
|
||||
class MemberStateChangeEvent(NoticeEvent):
|
||||
member: SenderInfo
|
||||
operator: Optional[SenderInfo] = None
|
||||
|
||||
|
@ -1 +1,26 @@
|
||||
from pydantic import Field
|
||||
|
||||
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