diff --git a/nonebot/adapters/mirai/bot.py b/nonebot/adapters/mirai/bot.py index 0a1262c7..7bd40968 100644 --- a/nonebot/adapters/mirai/bot.py +++ b/nonebot/adapters/mirai/bot.py @@ -1,8 +1,7 @@ from datetime import datetime, timedelta -from functools import wraps from io import BytesIO from ipaddress import IPv4Address -from typing import (Any, Dict, List, NoReturn, Optional, Tuple, Union) +from typing import Any, Dict, List, NoReturn, Optional, Tuple, Union import httpx @@ -10,15 +9,12 @@ from nonebot.adapters import Bot as BaseBot from nonebot.config import Config from nonebot.drivers import Driver, WebSocket from nonebot.exception import ApiNotAvailable, RequestDenied -from nonebot.log import logger -from nonebot.message import handle_event from nonebot.typing import overrides -from nonebot.utils import escape_tag from .config import Config as MiraiConfig from .event import Event, FriendMessage, GroupMessage, TempMessage from .message import MessageChain, MessageSegment -from .utils import catch_network_error, argument_validation, check_tome, Log +from .utils import Log, argument_validation, catch_network_error, process_event class SessionManager: @@ -212,20 +208,15 @@ class Bot(BaseBot): async def handle_message(self, message: dict): Log.debug(f'received message {message}') try: - await handle_event( + await process_event( bot=self, - event=await check_tome( - bot=self, - event=Event.new({ - **message, - 'self_id': self.self_id, - }), - ), + 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'{escape_tag(str(message))}: ') + Log.error(f'Failed to handle message: {message}', e) @overrides(BaseBot) async def call_api(self, api: str, **data) -> NoReturn: diff --git a/nonebot/adapters/mirai/event/__init__.py b/nonebot/adapters/mirai/event/__init__.py index 1cf92096..91f4b127 100644 --- a/nonebot/adapters/mirai/event/__init__.py +++ b/nonebot/adapters/mirai/event/__init__.py @@ -13,7 +13,7 @@ from .request import * __all__ = [ 'Event', 'GroupChatInfo', 'GroupInfo', 'PrivateChatInfo', 'UserPermission', - 'MessageChain', 'MessageEvent', 'GroupMessage', 'FriendMessage', + 'MessageSource', 'MessageEvent', 'GroupMessage', 'FriendMessage', 'TempMessage', 'NoticeEvent', 'MuteEvent', 'BotMuteEvent', 'BotUnmuteEvent', 'MemberMuteEvent', 'MemberUnmuteEvent', 'BotJoinGroupEvent', 'BotLeaveEventActive', 'BotLeaveEventKick', 'MemberJoinEvent', diff --git a/nonebot/adapters/mirai/event/message.py b/nonebot/adapters/mirai/event/message.py index 26d534d4..5dda0857 100644 --- a/nonebot/adapters/mirai/event/message.py +++ b/nonebot/adapters/mirai/event/message.py @@ -1,6 +1,7 @@ -from typing import Any +from datetime import datetime +from typing import Any, Optional -from pydantic import Field +from pydantic import BaseModel, Field from nonebot.typing import overrides @@ -8,9 +9,15 @@ from ..message import MessageChain from .base import Event, GroupChatInfo, PrivateChatInfo +class MessageSource(BaseModel): + id: int + time: datetime + + class MessageEvent(Event): """消息事件基类""" message_chain: MessageChain = Field(alias='messageChain') + source: Optional[MessageSource] = None sender: Any @overrides(Event) diff --git a/nonebot/adapters/mirai/message.py b/nonebot/adapters/mirai/message.py index 26fb198c..2285ceda 100644 --- a/nonebot/adapters/mirai/message.py +++ b/nonebot/adapters/mirai/message.py @@ -306,5 +306,13 @@ class MessageChain(BaseMessage): *map(lambda segment: segment.as_dict(), self.copy()) # type: ignore ] + def extract_first(self, *type: MessageType) -> Optional[MessageSegment]: + if not len(self): + return None + first: MessageSegment = self[0] + if (not type) or (first.type in type): + return self.pop(0) + return None + def __repr__(self) -> str: return f'<{self.__class__.__name__} {[*self.copy()]}>' diff --git a/nonebot/adapters/mirai/utils.py b/nonebot/adapters/mirai/utils.py index db94dfed..bc7aa7dc 100644 --- a/nonebot/adapters/mirai/utils.py +++ b/nonebot/adapters/mirai/utils.py @@ -7,10 +7,11 @@ from pydantic import Extra, ValidationError, validate_arguments import nonebot.exception as exception from nonebot.log import logger +from nonebot.message import handle_event from nonebot.utils import escape_tag, logger_wrapper -from .event import Event, GroupMessage -from .message import MessageSegment, MessageType +from .event import Event, GroupMessage, MessageEvent, MessageSource +from .message import MessageType if TYPE_CHECKING: from .bot import Bot @@ -124,39 +125,52 @@ def argument_validation(function: _AnyCallable) -> _AnyCallable: 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)]) +def process_source(bot: "Bot", event: MessageEvent) -> MessageEvent: + source = event.message_chain.extract_first(MessageType.SOURCE) + if source is not None: + event.source = MessageSource.parse_obj(source.data) return event + + +def process_at(bot: "Bot", event: GroupMessage) -> GroupMessage: + at = event.message_chain.extract_first(MessageType.AT) + if at is not None: + if at.data['target'] == event.self_id: + event.to_me = True + else: + event.message_chain.insert(0, at) + return event + + +def process_nick(bot: "Bot", event: GroupMessage) -> GroupMessage: + plain = event.message_chain.extract_first(MessageType.PLAIN) + if plain is not None: + text = str(plain) + nick_regex = '|'.join(filter(lambda x: x, bot.config.nickname)) + matched = re.search(rf"^({nick_regex})([\s,,]*|$)", text, re.IGNORECASE) + if matched is not None: + nickname = matched.group(1) + Log.info(f'User is calling me {nickname}') + plain.data['text'] = text[matched.end():] + event.message_chain.insert(0, plain) + return event + + +def process_reply(bot: "Bot", event: GroupMessage) -> GroupMessage: + reply = event.message_chain.extract_first(MessageType.QUOTE) + if reply is not None: + if reply.data['sender_id'] == event.self_id: + event.to_me = True + else: + event.message_chain.insert(0, reply) + return event + + +async def process_event(bot: "Bot", event: Event) -> None: + if isinstance(event, MessageEvent): + event = process_source(bot, event) + if isinstance(event, GroupMessage): + event = process_nick(bot, event) + event = process_reply(bot, event) + event = process_at(bot, event) + await handle_event(bot, event) \ No newline at end of file