🚸 add support of rule to_me in mirai adapter

This commit is contained in:
Mix 2021-02-01 16:31:51 +08:00
parent 5a63827f22
commit c0fa137fed
4 changed files with 107 additions and 13 deletions

View File

@ -13,11 +13,12 @@ from nonebot.exception import ApiNotAvailable, RequestDenied
from nonebot.log import logger from nonebot.log import logger
from nonebot.message import handle_event from nonebot.message import handle_event
from nonebot.typing import overrides from nonebot.typing import overrides
from nonebot.utils import escape_tag
from .config import Config as MiraiConfig from .config import Config as MiraiConfig
from .event import Event, FriendMessage, GroupMessage, TempMessage from .event import Event, FriendMessage, GroupMessage, TempMessage
from .message import MessageChain, MessageSegment from .message import MessageChain, MessageSegment
from .utils import catch_network_error, argument_validation from .utils import catch_network_error, argument_validation, check_tome, Log
class SessionManager: class SessionManager:
@ -209,11 +210,22 @@ class Bot(BaseBot):
@overrides(BaseBot) @overrides(BaseBot)
async def handle_message(self, message: dict): async def handle_message(self, message: dict):
await handle_event(bot=self, Log.debug(f'received message {message}')
event=Event.new({ try:
**message, await handle_event(
'self_id': self.self_id, bot=self,
})) event=await check_tome(
bot=self,
event=Event.new({
**message,
'self_id': self.self_id,
}),
),
)
except Exception as e:
logger.opt(colors=True, exception=e).exception(
'Failed to handle message '
f'<r>{escape_tag(str(message))}</r>: ')
@overrides(BaseBot) @overrides(BaseBot)
async def call_api(self, api: str, **data) -> NoReturn: async def call_api(self, api: str, **data) -> NoReturn:

View File

@ -80,8 +80,8 @@ class Event(BaseEvent):
return event_class.parse_obj(data) return event_class.parse_obj(data)
except ValidationError as e: except ValidationError as e:
logger.info( logger.info(
f'Failed to parse {data} to class {event_class.__name__}: {e}. ' f'Failed to parse {data} to class {event_class.__name__}: '
'Fallback to parent class.') f'{e.errors()!r}. Fallback to parent class.')
event_class = event_class.__base__ event_class = event_class.__base__
raise ValueError(f'Failed to serialize {data}.') raise ValueError(f'Failed to serialize {data}.')

View File

@ -33,11 +33,20 @@ class MessageEvent(Event):
class GroupMessage(MessageEvent): class GroupMessage(MessageEvent):
"""群消息事件""" """群消息事件"""
sender: GroupChatInfo sender: GroupChatInfo
to_me: bool = False
@overrides(MessageEvent) @overrides(MessageEvent)
def get_session_id(self) -> str: def get_session_id(self) -> str:
return f'group_{self.sender.group.id}_' + self.get_user_id() return f'group_{self.sender.group.id}_' + self.get_user_id()
@overrides(MessageEvent)
def get_user_id(self) -> str:
return str(self.sender.id)
@overrides(MessageEvent)
def is_tome(self) -> bool:
return self.to_me
class FriendMessage(MessageEvent): class FriendMessage(MessageEvent):
"""好友消息事件""" """好友消息事件"""
@ -47,15 +56,23 @@ class FriendMessage(MessageEvent):
def get_user_id(self) -> str: def get_user_id(self) -> str:
return str(self.sender.id) return str(self.sender.id)
@overrides @overrides(MessageEvent)
def get_session_id(self) -> str: def get_session_id(self) -> str:
return 'friend_' + self.get_user_id() return 'friend_' + self.get_user_id()
@overrides(MessageEvent)
def is_tome(self) -> bool:
return True
class TempMessage(MessageEvent): class TempMessage(MessageEvent):
"""临时会话消息事件""" """临时会话消息事件"""
sender: GroupChatInfo sender: GroupChatInfo
@overrides @overrides(MessageEvent)
def get_session_id(self) -> str: def get_session_id(self) -> str:
return f'temp_{self.sender.group.id}_' + self.get_user_id() return f'temp_{self.sender.group.id}_' + self.get_user_id()
@overrides(MessageEvent)
def is_tome(self) -> bool:
return True

View File

@ -1,17 +1,44 @@
import re
from functools import wraps from functools import wraps
from typing import Callable, Coroutine, TypeVar from typing import TYPE_CHECKING, Any, Callable, Coroutine, Optional, TypeVar
import httpx import httpx
from pydantic import ValidationError, validate_arguments, Extra from pydantic import Extra, ValidationError, validate_arguments
import nonebot.exception as exception import nonebot.exception as exception
from nonebot.log import logger from nonebot.log import logger
from nonebot.utils import escape_tag from nonebot.utils import escape_tag, logger_wrapper
from .event import Event, GroupMessage
from .message import MessageSegment, MessageType
if TYPE_CHECKING:
from .bot import Bot
_AsyncCallable = TypeVar("_AsyncCallable", bound=Callable[..., Coroutine]) _AsyncCallable = TypeVar("_AsyncCallable", bound=Callable[..., Coroutine])
_AnyCallable = TypeVar("_AnyCallable", bound=Callable) _AnyCallable = TypeVar("_AnyCallable", bound=Callable)
class Log:
_log = logger_wrapper('MIRAI')
@classmethod
def info(cls, message: Any):
cls._log('INFO', str(message))
@classmethod
def debug(cls, message: Any):
cls._log('DEBUG', str(message))
@classmethod
def warn(cls, message: Any):
cls._log('WARNING', str(message))
@classmethod
def error(cls, message: Any, exception: Optional[Exception] = None):
cls._log('ERROR', str(message), exception=exception)
class ActionFailed(exception.ActionFailed): class ActionFailed(exception.ActionFailed):
""" """
:说明: :说明:
@ -89,3 +116,41 @@ def argument_validation(function: _AnyCallable) -> _AnyCallable:
raise InvalidArgument raise InvalidArgument
return wrapper # type: ignore return wrapper # type: ignore
async def check_tome(bot: "Bot", event: "Event") -> "Event":
if not isinstance(event, GroupMessage):
return event
def _is_at(event: GroupMessage) -> bool:
for segment in event.message_chain:
segment: MessageSegment
if segment.type != MessageType.AT:
continue
if segment.data['target'] == event.self_id:
return True
return False
def _is_nick(event: GroupMessage) -> bool:
text = event.get_plaintext()
if not text:
return False
nick_regex = '|'.join(
{i.strip() for i in bot.config.nickname if i.strip()})
matched = re.search(rf"^({nick_regex})([\s,]*|$)", text, re.IGNORECASE)
if matched is None:
return False
Log.info(f'User is calling me {matched.group(1)}')
return True
def _is_reply(event: GroupMessage) -> bool:
for segment in event.message_chain:
segment: MessageSegment
if segment.type != MessageType.QUOTE:
continue
if segment.data['senderId'] == event.self_id:
return True
return False
event.to_me = any([_is_at(event), _is_reply(event), _is_nick(event)])
return event