""" 事件响应器 ========== 该模块实现事件响应器的创建与运行,并提供一些快捷方法来帮助用户更好的与机器人进行对话 。 """ from types import ModuleType from datetime import datetime from contextvars import ContextVar from collections import defaultdict from contextlib import AsyncExitStack from typing import ( TYPE_CHECKING, Any, Dict, List, Type, Union, Callable, NoReturn, Optional, ) from nonebot import params from nonebot.rule import Rule from nonebot.log import logger from nonebot.handler import Handler from nonebot.dependencies import DependsWrapper from nonebot.permission import USER, Permission from nonebot.adapters import ( Bot, Event, Message, MessageSegment, MessageTemplate, ) from nonebot.exception import ( PausedException, StopPropagation, SkippedException, FinishedException, RejectedException, ) from nonebot.typing import ( T_State, T_Handler, T_ArgsParser, T_TypeUpdater, T_StateFactory, T_DependencyCache, T_PermissionUpdater, ) if TYPE_CHECKING: from nonebot.plugin import Plugin matchers: Dict[int, List[Type["Matcher"]]] = defaultdict(list) """ :类型: ``Dict[int, List[Type[Matcher]]]`` :说明: 用于存储当前所有的事件响应器 """ current_bot: ContextVar[Bot] = ContextVar("current_bot") current_event: ContextVar[Event] = ContextVar("current_event") current_state: ContextVar[T_State] = ContextVar("current_state") class MatcherMeta(type): if TYPE_CHECKING: module: Optional[str] plugin_name: Optional[str] module_name: Optional[str] module_prefix: Optional[str] type: str rule: Rule permission: Permission handlers: List[T_Handler] priority: int block: bool temp: bool expire_time: Optional[datetime] def __repr__(self) -> str: return ( f"" ) def __str__(self) -> str: return repr(self) class Matcher(metaclass=MatcherMeta): """事件响应器类""" plugin: Optional["Plugin"] = None """ :类型: ``Optional[Plugin]`` :说明: 事件响应器所在插件 """ module: Optional[ModuleType] = None """ :类型: ``Optional[ModuleType]`` :说明: 事件响应器所在插件模块 """ plugin_name: Optional[str] = None """ :类型: ``Optional[str]`` :说明: 事件响应器所在插件名 """ module_name: Optional[str] = None """ :类型: ``Optional[str]`` :说明: 事件响应器所在点分割插件模块路径 """ type: str = "" """ :类型: ``str`` :说明: 事件响应器类型 """ rule: Rule = Rule() """ :类型: ``Rule`` :说明: 事件响应器匹配规则 """ permission: Permission = Permission() """ :类型: ``Permission`` :说明: 事件响应器触发权限 """ handlers: List[Handler] = [] """ :类型: ``List[Handler]`` :说明: 事件响应器拥有的事件处理函数列表 """ priority: int = 1 """ :类型: ``int`` :说明: 事件响应器优先级 """ block: bool = False """ :类型: ``bool`` :说明: 事件响应器是否阻止事件传播 """ temp: bool = False """ :类型: ``bool`` :说明: 事件响应器是否为临时 """ expire_time: Optional[datetime] = None """ :类型: ``Optional[datetime]`` :说明: 事件响应器过期时间点 """ _default_state: T_State = {} """ :类型: ``T_State`` :说明: 事件响应器默认状态 """ _default_state_factory: Optional[T_StateFactory] = None """ :类型: ``Optional[T_State]`` :说明: 事件响应器默认工厂函数 """ _default_parser: Optional[T_ArgsParser] = None """ :类型: ``Optional[T_ArgsParser]`` :说明: 事件响应器默认参数解析函数 """ _default_type_updater: Optional[T_TypeUpdater] = None """ :类型: ``Optional[T_TypeUpdater]`` :说明: 事件响应器类型更新函数 """ _default_permission_updater: Optional[T_PermissionUpdater] = None """ :类型: ``Optional[T_PermissionUpdater]`` :说明: 事件响应器权限更新函数 """ HANDLER_PARAM_TYPES = [ params.BotParam, params.EventParam, params.StateParam, params.MatcherParam, params.DefaultParam, ] def __init__(self): """实例化 Matcher 以便运行""" self.handlers = self.handlers.copy() self.state = self._default_state.copy() def __repr__(self) -> str: return ( f"" ) def __str__(self) -> str: return repr(self) @classmethod def new( cls, type_: str = "", rule: Optional[Rule] = None, permission: Optional[Permission] = None, handlers: Optional[ Union[List[T_Handler], List[Handler], List[Union[T_Handler, Handler]]] ] = None, temp: bool = False, priority: int = 1, block: bool = False, *, plugin: Optional["Plugin"] = None, module: Optional[ModuleType] = None, expire_time: Optional[datetime] = None, default_state: Optional[T_State] = None, default_state_factory: Optional[T_StateFactory] = None, default_parser: Optional[T_ArgsParser] = None, default_type_updater: Optional[T_TypeUpdater] = None, default_permission_updater: Optional[T_PermissionUpdater] = None, ) -> Type["Matcher"]: """ :说明: 创建一个新的事件响应器,并存储至 `matchers <#matchers>`_ :参数: * ``type_: str``: 事件响应器类型,与 ``event.get_type()`` 一致时触发,空字符串表示任意 * ``rule: Optional[Rule]``: 匹配规则 * ``permission: Optional[Permission]``: 权限 * ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表 * ``temp: bool``: 是否为临时事件响应器,即触发一次后删除 * ``priority: int``: 响应优先级 * ``block: bool``: 是否阻止事件向更低优先级的响应器传播 * ``plugin: Optional[Plugin]``: 事件响应器所在插件 * ``module: Optional[ModuleType]``: 事件响应器所在模块 * ``default_state: Optional[T_State]``: 默认状态 ``state`` * ``default_state_factory: Optional[T_StateFactory]``: 默认状态 ``state`` 的工厂函数 * ``expire_time: Optional[datetime]``: 事件响应器最终有效时间点,过时即被删除 :返回: - ``Type[Matcher]``: 新的事件响应器类 """ NewMatcher = type( "Matcher", (Matcher,), { "plugin": plugin, "module": module, "plugin_name": plugin and plugin.name, "module_name": module and module.__name__, "type": type_, "rule": rule or Rule(), "permission": permission or Permission(), "handlers": [ handler if isinstance(handler, Handler) else Handler(handler, allow_types=cls.HANDLER_PARAM_TYPES) for handler in handlers ] if handlers else [], "temp": temp, "expire_time": expire_time, "priority": priority, "block": block, "_default_state": default_state or {}, "_default_state_factory": staticmethod(default_state_factory) if default_state_factory else None, "_default_parser": default_parser, "_default_type_updater": default_type_updater, "_default_permission_updater": default_permission_updater, }, ) matchers[priority].append(NewMatcher) return NewMatcher @classmethod async def check_perm( cls, bot: Bot, event: Event, stack: Optional[AsyncExitStack] = None, dependency_cache: Optional[Dict[Callable[..., Any], Any]] = None, ) -> bool: """ :说明: 检查是否满足触发权限 :参数: * ``bot: Bot``: Bot 对象 * ``event: Event``: 上报事件 :返回: - ``bool``: 是否满足权限 """ event_type = event.get_type() return event_type == (cls.type or event_type) and await cls.permission( bot, event, stack, dependency_cache ) @classmethod async def check_rule( cls, bot: Bot, event: Event, state: T_State, stack: Optional[AsyncExitStack] = None, dependency_cache: Optional[Dict[Callable[..., Any], Any]] = None, ) -> bool: """ :说明: 检查是否满足匹配规则 :参数: * ``bot: Bot``: Bot 对象 * ``event: Event``: 上报事件 * ``state: T_State``: 当前状态 :返回: - ``bool``: 是否满足匹配规则 """ event_type = event.get_type() return event_type == (cls.type or event_type) and await cls.rule( bot, event, state, stack, dependency_cache ) @classmethod def args_parser(cls, func: T_ArgsParser) -> T_ArgsParser: """ :说明: 装饰一个函数来更改当前事件响应器的默认参数解析函数 :参数: * ``func: T_ArgsParser``: 参数解析函数 """ cls._default_parser = func return func @classmethod def type_updater(cls, func: T_TypeUpdater) -> T_TypeUpdater: """ :说明: 装饰一个函数来更改当前事件响应器的默认响应事件类型更新函数 :参数: * ``func: T_TypeUpdater``: 响应事件类型更新函数 """ cls._default_type_updater = func return func @classmethod def permission_updater(cls, func: T_PermissionUpdater) -> T_PermissionUpdater: """ :说明: 装饰一个函数来更改当前事件响应器的默认会话权限更新函数 :参数: * ``func: T_PermissionUpdater``: 会话权限更新函数 """ cls._default_permission_updater = func return func @classmethod def append_handler( cls, handler: T_Handler, dependencies: Optional[List[DependsWrapper]] = None ) -> Handler: handler_ = Handler( handler, dependencies=dependencies, allow_types=cls.HANDLER_PARAM_TYPES ) cls.handlers.append(handler_) return handler_ @classmethod def handle( cls, dependencies: Optional[List[DependsWrapper]] = None ) -> Callable[[T_Handler], T_Handler]: """ :说明: 装饰一个函数来向事件响应器直接添加一个处理函数 :参数: * ``dependencies: Optional[List[DependsWrapper]]``: 非参数类型依赖列表 """ def _decorator(func: T_Handler) -> T_Handler: cls.append_handler(func, dependencies=dependencies) return func return _decorator @classmethod def receive( cls, dependencies: Optional[List[DependsWrapper]] = None ) -> Callable[[T_Handler], T_Handler]: """ :说明: 装饰一个函数来指示 NoneBot 在接收用户新的一条消息后继续运行该函数 :参数: * ``dependencies: Optional[List[DependsWrapper]]``: 非参数类型依赖列表 """ async def _receive(state: T_State) -> Union[None, NoReturn]: if state.get(_receive): return state[_receive] = True del state["_current_key"] raise RejectedException _dependencies = [DependsWrapper(_receive), *(dependencies or [])] def _decorator(func: T_Handler) -> T_Handler: if cls.handlers and cls.handlers[-1].func is func: func_handler = cls.handlers[-1] for depend in reversed(_dependencies): func_handler.prepend_dependency(depend) else: cls.append_handler( func, dependencies=_dependencies if cls.handlers else dependencies, ) return func return _decorator @classmethod def got( cls, key: str, prompt: Optional[Union[str, Message, MessageSegment, MessageTemplate]] = None, args_parser: Optional[T_ArgsParser] = None, dependencies: Optional[List[DependsWrapper]] = None, ) -> Callable[[T_Handler], T_Handler]: """ :说明: 装饰一个函数来指示 NoneBot 当要获取的 ``key`` 不存在时接收用户新的一条消息并经过 ``ArgsParser`` 处理后再运行该函数,如果 ``key`` 已存在则直接继续运行 :参数: * ``key: str``: 参数名 * ``prompt: Optional[Union[str, Message, MessageSegment, MessageFormatter]]``: 在参数不存在时向用户发送的消息 * ``args_parser: Optional[T_ArgsParser]``: 可选参数解析函数,空则使用默认解析函数 * ``dependencies: Optional[List[DependsWrapper]]``: 非参数类型依赖列表 """ async def _key_getter(bot: Bot, event: Event, state: T_State): if state.get(f"_{key}_prompted"): return state["_current_key"] = key state[f"_{key}_prompted"] = True if key not in state: if prompt is not None: if isinstance(prompt, MessageTemplate): _prompt = prompt.format(**state) else: _prompt = prompt await bot.send(event=event, message=_prompt) raise RejectedException else: state[f"_{key}_parsed"] = True async def _key_parser(bot: Bot, event: Event, state: T_State): if key in state and state.get(f"_{key}_parsed"): return parser = args_parser or cls._default_parser if parser: await parser(bot, event, state) else: state[key] = str(event.get_message()) state[f"_{key}_parsed"] = True _dependencies = [ DependsWrapper(_key_getter), DependsWrapper(_key_parser), *(dependencies or []), ] def _decorator(func: T_Handler) -> T_Handler: if cls.handlers and cls.handlers[-1].func is func: func_handler = cls.handlers[-1] for depend in reversed(_dependencies): func_handler.prepend_dependency(depend) else: cls.append_handler(func, dependencies=_dependencies) return func return _decorator @classmethod async def send( cls, message: Union[str, Message, MessageSegment, MessageTemplate], **kwargs ) -> Any: """ :说明: 发送一条消息给当前交互用户 :参数: * ``message: Union[str, Message, MessageSegment]``: 消息内容 * ``**kwargs``: 其他传递给 ``bot.send`` 的参数,请参考对应 adapter 的 bot 对象 api """ bot = current_bot.get() event = current_event.get() state = current_state.get() if isinstance(message, MessageTemplate): _message = message.format(**state) else: _message = message return await bot.send(event=event, message=_message, **kwargs) @classmethod async def finish( cls, message: Optional[Union[str, Message, MessageSegment, MessageTemplate]] = None, **kwargs, ) -> NoReturn: """ :说明: 发送一条消息给当前交互用户并结束当前事件响应器 :参数: * ``message: Union[str, Message, MessageSegment]``: 消息内容 * ``**kwargs``: 其他传递给 ``bot.send`` 的参数,请参考对应 adapter 的 bot 对象 api """ bot = current_bot.get() event = current_event.get() state = current_state.get() if isinstance(message, MessageTemplate): _message = message.format(**state) else: _message = message if _message is not None: await bot.send(event=event, message=_message, **kwargs) raise FinishedException @classmethod async def pause( cls, prompt: Optional[Union[str, Message, MessageSegment, MessageTemplate]] = None, **kwargs, ) -> NoReturn: """ :说明: 发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后继续下一个处理函数 :参数: * ``prompt: Union[str, Message, MessageSegment]``: 消息内容 * ``**kwargs``: 其他传递给 ``bot.send`` 的参数,请参考对应 adapter 的 bot 对象 api """ bot = current_bot.get() event = current_event.get() state = current_state.get() if isinstance(prompt, MessageTemplate): _prompt = prompt.format(**state) else: _prompt = prompt if _prompt is not None: await bot.send(event=event, message=_prompt, **kwargs) raise PausedException @classmethod async def reject( cls, prompt: Optional[Union[str, Message, MessageSegment]] = None, **kwargs ) -> NoReturn: """ :说明: 发送一条消息给当前交互用户并暂停事件响应器,在接收用户新的一条消息后重新运行当前处理函数 :参数: * ``prompt: Union[str, Message, MessageSegment]``: 消息内容 * ``**kwargs``: 其他传递给 ``bot.send`` 的参数,请参考对应 adapter 的 bot 对象 api """ bot = current_bot.get() event = current_event.get() state = current_state.get() if "_current_key" in state and f"_{state['_current_key']}_parsed" in state: del state[f"_{state['_current_key']}_parsed"] if isinstance(prompt, MessageTemplate): _prompt = prompt.format(**state) else: _prompt = prompt if _prompt is not None: await bot.send(event=event, message=_prompt, **kwargs) raise RejectedException def stop_propagation(self): """ :说明: 阻止事件传播 """ self.block = True # 运行handlers async def run( self, bot: Bot, event: Event, state: T_State, stack: Optional[AsyncExitStack] = None, dependency_cache: Optional[T_DependencyCache] = None, ): b_t = current_bot.set(bot) e_t = current_event.set(event) s_t = current_state.set(self.state) try: # Refresh preprocess state self.state = ( await self._default_state_factory(bot, event) if self._default_state_factory else self.state ) self.state.update(state) while self.handlers: handler = self.handlers.pop(0) logger.debug(f"Running handler {handler}") try: await handler( matcher=self, bot=bot, event=event, state=self.state, _stack=stack, _dependency_cache=dependency_cache, ) except SkippedException as e: logger.debug( f"Handler {handler} param {e.param.name} value {e.value} " f"mismatch type {e.param._type_display()}, skipped" ) except RejectedException: self.handlers.insert(0, handler) # type: ignore updater = self.__class__._default_type_updater if updater: type_ = await updater(bot, event, self.state, self.type) # type: ignore else: type_ = "message" updater = self.__class__._default_permission_updater if updater: permission = await updater(bot, event, self.state, self.permission) else: permission = USER(event.get_session_id(), perm=self.permission) Matcher.new( type_, Rule(), permission, self.handlers, temp=True, priority=0, block=True, plugin=self.plugin, module=self.module, expire_time=datetime.now() + bot.config.session_expire_timeout, default_state=self.state, default_parser=self.__class__._default_parser, default_type_updater=self.__class__._default_type_updater, default_permission_updater=self.__class__._default_permission_updater, ) except PausedException: updater = self.__class__._default_type_updater if updater: type_ = await updater(bot, event, self.state, self.type) # type: ignore else: type_ = "message" updater = self.__class__._default_permission_updater if updater: permission = await updater(bot, event, self.state, self.permission) else: permission = USER(event.get_session_id(), perm=self.permission) Matcher.new( type_, Rule(), permission, self.handlers, temp=True, priority=0, block=True, plugin=self.plugin, module=self.module, expire_time=datetime.now() + bot.config.session_expire_timeout, default_state=self.state, default_parser=self.__class__._default_parser, default_type_updater=self.__class__._default_type_updater, default_permission_updater=self.__class__._default_permission_updater, ) except FinishedException: pass except StopPropagation: self.block = True finally: logger.info(f"Matcher {self} running complete") current_bot.reset(b_t) current_event.reset(e_t) current_state.reset(s_t)