From c03b0c73cb189c01958461b4ce945119ed673f40 Mon Sep 17 00:00:00 2001 From: Dobiichi-Origami <56953648+Dobiichi-Origami@users.noreply.github.com> Date: Wed, 20 Jul 2022 10:21:31 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20Feature:=20on=5Fx=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20expire=5Ftime=20=E5=8F=82=E6=95=B0=20(#1106)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dobiichi-Origami <454470535@qq.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: yanyongyu <42488585+yanyongyu@users.noreply.github.com> --- nonebot/__init__.py | 8 +- nonebot/internal/matcher.py | 14 +- nonebot/plugin/__init__.py | 2 - nonebot/plugin/on.py | 177 +++++++++++++----------- nonebot/plugin/on.pyi | 29 ++++ tests/plugins/matcher/matcher_expire.py | 7 + tests/test_matcher.py | 38 +++++ 7 files changed, 187 insertions(+), 88 deletions(-) create mode 100644 tests/plugins/matcher/matcher_expire.py diff --git a/nonebot/__init__.py b/nonebot/__init__.py index 9c8d62ac..e9195a71 100644 --- a/nonebot/__init__.py +++ b/nonebot/__init__.py @@ -25,10 +25,10 @@ - `load_from_toml` => {ref}``load_from_toml` ` - `load_builtin_plugin` => {ref}``load_builtin_plugin` ` - `load_builtin_plugins` => {ref}``load_builtin_plugins` ` -- `get_plugin` => {ref}``get_plugin` ` -- `get_plugin_by_module_name` => {ref}``get_plugin_by_module_name` ` -- `get_loaded_plugins` => {ref}``get_loaded_plugins` ` -- `get_available_plugin_names` => {ref}``get_available_plugin_names` ` +- `get_plugin` => {ref}``get_plugin` ` +- `get_plugin_by_module_name` => {ref}``get_plugin_by_module_name` ` +- `get_loaded_plugins` => {ref}``get_loaded_plugins` ` +- `get_available_plugin_names` => {ref}``get_available_plugin_names` ` - `export` => {ref}``export` ` - `require` => {ref}``require` ` diff --git a/nonebot/internal/matcher.py b/nonebot/internal/matcher.py index 16ba232f..d95c55cf 100644 --- a/nonebot/internal/matcher.py +++ b/nonebot/internal/matcher.py @@ -1,8 +1,8 @@ from types import ModuleType -from datetime import datetime from contextvars import ContextVar from collections import defaultdict from contextlib import AsyncExitStack +from datetime import datetime, timedelta from typing import ( TYPE_CHECKING, Any, @@ -168,7 +168,7 @@ class Matcher(metaclass=MatcherMeta): *, plugin: Optional["Plugin"] = None, module: Optional[ModuleType] = None, - expire_time: Optional[datetime] = None, + expire_time: Optional[Union[datetime, timedelta]] = None, default_state: Optional[T_State] = None, default_type_updater: Optional[Union[T_TypeUpdater, Dependent[str]]] = None, default_permission_updater: Optional[ @@ -216,7 +216,11 @@ class Matcher(metaclass=MatcherMeta): if handlers else [], "temp": temp, - "expire_time": expire_time, + "expire_time": ( + expire_time + if isinstance(expire_time, datetime) + else expire_time and datetime.now() + expire_time + ), "priority": priority, "block": block, "_default_state": default_state or {}, @@ -682,7 +686,7 @@ class Matcher(metaclass=MatcherMeta): block=True, plugin=self.plugin, module=self.module, - expire_time=datetime.now() + bot.config.session_expire_timeout, + expire_time=bot.config.session_expire_timeout, default_state=self.state, default_type_updater=self.__class__._default_type_updater, default_permission_updater=self.__class__._default_permission_updater, @@ -701,7 +705,7 @@ class Matcher(metaclass=MatcherMeta): block=True, plugin=self.plugin, module=self.module, - expire_time=datetime.now() + bot.config.session_expire_timeout, + expire_time=bot.config.session_expire_timeout, default_state=self.state, default_type_updater=self.__class__._default_type_updater, default_permission_updater=self.__class__._default_permission_updater, diff --git a/nonebot/plugin/__init__.py b/nonebot/plugin/__init__.py index 52b65e07..77d3118d 100644 --- a/nonebot/plugin/__init__.py +++ b/nonebot/plugin/__init__.py @@ -25,8 +25,6 @@ - `load_from_toml` => {ref}``load_from_toml` ` - `load_builtin_plugin` => {ref}``load_builtin_plugin` ` - `load_builtin_plugins` => {ref}``load_builtin_plugins` ` -- `get_plugin` => {ref}``get_plugin` ` -- `get_loaded_plugins` => {ref}``get_loaded_plugins` ` - `export` => {ref}``export` ` - `require` => {ref}``require` ` diff --git a/nonebot/plugin/on.py b/nonebot/plugin/on.py index 40afd83c..54a15078 100644 --- a/nonebot/plugin/on.py +++ b/nonebot/plugin/on.py @@ -7,6 +7,7 @@ FrontMatter: import re import inspect from types import ModuleType +from datetime import datetime, timedelta from typing import Any, Set, Dict, List, Type, Tuple, Union, Optional from nonebot.matcher import Matcher @@ -50,13 +51,13 @@ def on( *, handlers: Optional[List[Union[T_Handler, Dependent]]] = None, temp: bool = False, + expire_time: Optional[Union[datetime, timedelta]] = None, priority: int = 1, block: bool = False, state: Optional[T_State] = None, _depth: int = 0, ) -> Type[Matcher]: - """ - 注册一个基础事件响应器,可自定义类型。 + """注册一个基础事件响应器,可自定义类型。 参数: type: 事件响应器类型 @@ -64,6 +65,7 @@ def on( permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -73,6 +75,7 @@ def on( Rule() & rule, Permission() | permission, temp=temp, + expire_time=expire_time, priority=priority, block=block, handlers=handlers, @@ -89,18 +92,19 @@ def on_metaevent( *, handlers: Optional[List[Union[T_Handler, Dependent]]] = None, temp: bool = False, + expire_time: Optional[Union[datetime, timedelta]] = None, priority: int = 1, block: bool = False, state: Optional[T_State] = None, _depth: int = 0, ) -> Type[Matcher]: - """ - 注册一个元事件响应器。 + """注册一个元事件响应器。 参数: rule: 事件响应规则 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -110,6 +114,7 @@ def on_metaevent( Rule() & rule, Permission(), temp=temp, + expire_time=expire_time, priority=priority, block=block, handlers=handlers, @@ -127,19 +132,20 @@ def on_message( *, handlers: Optional[List[Union[T_Handler, Dependent]]] = None, temp: bool = False, + expire_time: Optional[Union[datetime, timedelta]] = None, priority: int = 1, block: bool = True, state: Optional[T_State] = None, _depth: int = 0, ) -> Type[Matcher]: - """ - 注册一个消息事件响应器。 + """注册一个消息事件响应器。 参数: rule: 事件响应规则 permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -149,6 +155,7 @@ def on_message( Rule() & rule, Permission() | permission, temp=temp, + expire_time=expire_time, priority=priority, block=block, handlers=handlers, @@ -165,18 +172,19 @@ def on_notice( *, handlers: Optional[List[Union[T_Handler, Dependent]]] = None, temp: bool = False, + expire_time: Optional[Union[datetime, timedelta]] = None, priority: int = 1, block: bool = False, state: Optional[T_State] = None, _depth: int = 0, ) -> Type[Matcher]: - """ - 注册一个通知事件响应器。 + """注册一个通知事件响应器。 参数: rule: 事件响应规则 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -186,6 +194,7 @@ def on_notice( Rule() & rule, Permission(), temp=temp, + expire_time=expire_time, priority=priority, block=block, handlers=handlers, @@ -202,18 +211,19 @@ def on_request( *, handlers: Optional[List[Union[T_Handler, Dependent]]] = None, temp: bool = False, + expire_time: Optional[Union[datetime, timedelta]] = None, priority: int = 1, block: bool = False, state: Optional[T_State] = None, _depth: int = 0, ) -> Type[Matcher]: - """ - 注册一个请求事件响应器。 + """注册一个请求事件响应器。 参数: rule: 事件响应规则 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -223,6 +233,7 @@ def on_request( Rule() & rule, Permission(), temp=temp, + expire_time=expire_time, priority=priority, block=block, handlers=handlers, @@ -241,8 +252,7 @@ def on_startswith( _depth: int = 0, **kwargs, ) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息的**文本部分**以指定内容开头时响应。 + """注册一个消息事件响应器,并且当消息的**文本部分**以指定内容开头时响应。 参数: msg: 指定消息开头内容 @@ -251,6 +261,7 @@ def on_startswith( permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -265,8 +276,7 @@ def on_endswith( _depth: int = 0, **kwargs, ) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息的**文本部分**以指定内容结尾时响应。 + """注册一个消息事件响应器,并且当消息的**文本部分**以指定内容结尾时响应。 参数: msg: 指定消息结尾内容 @@ -275,6 +285,7 @@ def on_endswith( permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -289,8 +300,7 @@ def on_fullmatch( _depth: int = 0, **kwargs, ) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。 + """注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。 参数: msg: 指定消息全匹配内容 @@ -299,6 +309,7 @@ def on_fullmatch( permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -312,8 +323,7 @@ def on_keyword( _depth: int = 0, **kwargs, ) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。 + """注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。 参数: keywords: 关键词列表 @@ -321,6 +331,7 @@ def on_keyword( permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -335,8 +346,7 @@ def on_command( _depth: int = 0, **kwargs, ) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息以指定命令开头时响应。 + """注册一个消息事件响应器,并且当消息以指定命令开头时响应。 命令匹配规则参考: `命令形式匹配 `_ @@ -347,6 +357,7 @@ def on_command( permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -367,8 +378,7 @@ def on_shell_command( _depth: int = 0, **kwargs, ) -> Type[Matcher]: - """ - 注册一个支持 `shell_like` 解析参数的命令消息事件响应器。 + """注册一个支持 `shell_like` 解析参数的命令消息事件响应器。 与普通的 `on_command` 不同的是,在添加 `parser` 参数时, 响应器会自动处理消息。 @@ -382,6 +392,7 @@ def on_shell_command( permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -402,8 +413,7 @@ def on_regex( _depth: int = 0, **kwargs, ) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息匹配正则表达式时响应。 + """注册一个消息事件响应器,并且当消息匹配正则表达式时响应。 命令匹配规则参考: `正则匹配 `_ @@ -414,6 +424,7 @@ def on_regex( permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -422,32 +433,42 @@ def on_regex( class CommandGroup: - """命令组,用于声明一组有相同名称前缀的命令。""" + """命令组,用于声明一组有相同名称前缀的命令。 + + 参数: + cmd: 指定命令内容 + rule: 事件响应规则 + permission: 事件响应权限 + handlers: 事件处理函数列表 + temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 + priority: 事件响应器优先级 + block: 是否阻止事件向更低优先级传递 + state: 默认 state + """ def __init__(self, cmd: Union[str, Tuple[str, ...]], **kwargs): - """ - 参数: - cmd: 命令前缀 - **kwargs: `on_command` 的参数默认值,参考 `on_command <#on-command-cmd-rule-none-aliases-none-kwargs>`_ - """ self.basecmd: Tuple[str, ...] = (cmd,) if isinstance(cmd, str) else cmd - """ - 命令前缀 - """ + """命令前缀""" if "aliases" in kwargs: del kwargs["aliases"] self.base_kwargs: Dict[str, Any] = kwargs - """ - 其他传递给 `on_command` 的参数默认值 - """ + """其他传递给 `on_command` 的参数默认值""" def command(self, cmd: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]: - """ - 注册一个新的命令。 + """注册一个新的命令。新参数将会覆盖命令组默认值 参数: - cmd: 命令前缀 - **kwargs: `on_command` 的参数,将会覆盖命令组默认值 + cmd: 指定命令内容 + aliases: 命令别名 + rule: 事件响应规则 + permission: 事件响应权限 + handlers: 事件处理函数列表 + temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 + priority: 事件响应器优先级 + block: 是否阻止事件向更低优先级传递 + state: 默认 state """ sub_cmd = (cmd,) if isinstance(cmd, str) else cmd cmd = self.basecmd + sub_cmd @@ -459,12 +480,20 @@ class CommandGroup: def shell_command( self, cmd: Union[str, Tuple[str, ...]], **kwargs ) -> Type[Matcher]: - """ - 注册一个新的命令。 + """注册一个新的 `shell_like` 命令。新参数将会覆盖命令组默认值 参数: - cmd: 命令前缀 - **kwargs: `on_shell_command` 的参数,将会覆盖命令组默认值 + cmd: 指定命令内容 + rule: 事件响应规则 + aliases: 命令别名 + parser: `nonebot.rule.ArgumentParser` 对象 + permission: 事件响应权限 + handlers: 事件处理函数列表 + temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 + priority: 事件响应器优先级 + block: 是否阻止事件向更低优先级传递 + state: 默认 state """ sub_cmd = (cmd,) if isinstance(cmd, str) else cmd cmd = self.basecmd + sub_cmd @@ -478,21 +507,14 @@ class MatcherGroup: """事件响应器组合,统一管理。为 `Matcher` 创建提供默认属性。""" def __init__(self, **kwargs): - """ - 创建一个事件响应器组合,参数为默认值,与 `on` 一致 - """ + """创建一个事件响应器组合,参数为默认值,与 `on` 一致""" self.matchers: List[Type[Matcher]] = [] - """ - 组内事件响应器列表 - """ + """组内事件响应器列表""" self.base_kwargs: Dict[str, Any] = kwargs - """ - 其他传递给 `on` 的参数默认值 - """ + """其他传递给 `on` 的参数默认值""" def on(self, **kwargs) -> Type[Matcher]: - """ - 注册一个基础事件响应器,可自定义类型。 + """注册一个基础事件响应器,可自定义类型。 参数: type: 事件响应器类型 @@ -500,6 +522,7 @@ class MatcherGroup: permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -511,13 +534,13 @@ class MatcherGroup: return matcher def on_metaevent(self, **kwargs) -> Type[Matcher]: - """ - 注册一个元事件响应器。 + """注册一个元事件响应器。 参数: rule: 事件响应规则 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -531,14 +554,14 @@ class MatcherGroup: return matcher def on_message(self, **kwargs) -> Type[Matcher]: - """ - 注册一个消息事件响应器。 + """注册一个消息事件响应器。 参数: rule: 事件响应规则 permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -551,13 +574,13 @@ class MatcherGroup: return matcher def on_notice(self, **kwargs) -> Type[Matcher]: - """ - 注册一个通知事件响应器。 + """注册一个通知事件响应器。 参数: rule: 事件响应规则 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -570,13 +593,13 @@ class MatcherGroup: return matcher def on_request(self, **kwargs) -> Type[Matcher]: - """ - 注册一个请求事件响应器。 + """注册一个请求事件响应器。 参数: rule: 事件响应规则 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -591,8 +614,7 @@ class MatcherGroup: def on_startswith( self, msg: Union[str, Tuple[str, ...]], **kwargs ) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息的**文本部分**以指定内容开头时响应。 + """注册一个消息事件响应器,并且当消息的**文本部分**以指定内容开头时响应。 参数: msg: 指定消息开头内容 @@ -601,6 +623,7 @@ class MatcherGroup: permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -613,8 +636,7 @@ class MatcherGroup: return matcher def on_endswith(self, msg: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息的**文本部分**以指定内容结尾时响应。 + """注册一个消息事件响应器,并且当消息的**文本部分**以指定内容结尾时响应。 参数: msg: 指定消息结尾内容 @@ -623,6 +645,7 @@ class MatcherGroup: permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -635,8 +658,7 @@ class MatcherGroup: return matcher def on_fullmatch(self, msg: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。 + """注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。 参数: msg: 指定消息全匹配内容 @@ -645,6 +667,7 @@ class MatcherGroup: permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -657,8 +680,7 @@ class MatcherGroup: return matcher def on_keyword(self, keywords: Set[str], **kwargs) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。 + """注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。 参数: keywords: 关键词列表 @@ -666,6 +688,7 @@ class MatcherGroup: permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -683,8 +706,7 @@ class MatcherGroup: aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None, **kwargs, ) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息以指定命令开头时响应。 + """注册一个消息事件响应器,并且当消息以指定命令开头时响应。 命令匹配规则参考: `命令形式匹配 `_ @@ -695,6 +717,7 @@ class MatcherGroup: permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -713,8 +736,7 @@ class MatcherGroup: parser: Optional[ArgumentParser] = None, **kwargs, ) -> Type[Matcher]: - """ - 注册一个支持 `shell_like` 解析参数的命令消息事件响应器。 + """注册一个支持 `shell_like` 解析参数的命令消息事件响应器。 与普通的 `on_command` 不同的是,在添加 `parser` 参数时, 响应器会自动处理消息。 @@ -728,6 +750,7 @@ class MatcherGroup: permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state @@ -744,8 +767,7 @@ class MatcherGroup: def on_regex( self, pattern: str, flags: Union[int, re.RegexFlag] = 0, **kwargs ) -> Type[Matcher]: - """ - 注册一个消息事件响应器,并且当消息匹配正则表达式时响应。 + """注册一个消息事件响应器,并且当消息匹配正则表达式时响应。 命令匹配规则参考: `正则匹配 `_ @@ -756,6 +778,7 @@ class MatcherGroup: permission: 事件响应权限 handlers: 事件处理函数列表 temp: 是否为临时事件响应器(仅执行一次) + expire_time: 事件响应器最终有效时间点,过时即被删除 priority: 事件响应器优先级 block: 是否阻止事件向更低优先级传递 state: 默认 state diff --git a/nonebot/plugin/on.pyi b/nonebot/plugin/on.pyi index 9b2393f1..03f5c73d 100644 --- a/nonebot/plugin/on.pyi +++ b/nonebot/plugin/on.pyi @@ -1,4 +1,5 @@ import re +from datetime import datetime, timedelta from typing import Set, List, Type, Tuple, Union, Optional from nonebot.matcher import Matcher @@ -14,6 +15,7 @@ def on( *, handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -23,6 +25,7 @@ def on_metaevent( *, handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -33,6 +36,7 @@ def on_message( *, handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -42,6 +46,7 @@ def on_notice( *, handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -51,6 +56,7 @@ def on_request( *, handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -63,6 +69,7 @@ def on_startswith( permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -75,6 +82,7 @@ def on_endswith( permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -87,6 +95,7 @@ def on_fullmatch( permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -98,6 +107,7 @@ def on_keyword( permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -110,6 +120,7 @@ def on_command( permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -123,6 +134,7 @@ def on_shell_command( permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -135,6 +147,7 @@ def on_regex( permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -149,6 +162,7 @@ class CommandGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -162,6 +176,7 @@ class CommandGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -176,6 +191,7 @@ class CommandGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -190,6 +206,7 @@ class MatcherGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -202,6 +219,7 @@ class MatcherGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -212,6 +230,7 @@ class MatcherGroup: rule: Optional[Union[Rule, T_RuleChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -223,6 +242,7 @@ class MatcherGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -233,6 +253,7 @@ class MatcherGroup: rule: Optional[Union[Rule, T_RuleChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -243,6 +264,7 @@ class MatcherGroup: rule: Optional[Union[Rule, T_RuleChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -256,6 +278,7 @@ class MatcherGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -269,6 +292,7 @@ class MatcherGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -282,6 +306,7 @@ class MatcherGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -294,6 +319,7 @@ class MatcherGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -307,6 +333,7 @@ class MatcherGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -321,6 +348,7 @@ class MatcherGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., @@ -334,6 +362,7 @@ class MatcherGroup: permission: Optional[Union[Permission, T_PermissionChecker]] = ..., handlers: Optional[List[Union[T_Handler, Dependent]]] = ..., temp: bool = ..., + expire_time: Optional[Union[datetime, timedelta]] = ..., priority: int = ..., block: bool = ..., state: Optional[T_State] = ..., diff --git a/tests/plugins/matcher/matcher_expire.py b/tests/plugins/matcher/matcher_expire.py new file mode 100644 index 00000000..c026d679 --- /dev/null +++ b/tests/plugins/matcher/matcher_expire.py @@ -0,0 +1,7 @@ +from datetime import datetime, timedelta + +from nonebot.matcher import Matcher + +test_temp_matcher = Matcher.new("test", temp=True) +test_datetime_matcher = Matcher.new("test", expire_time=datetime.now()) +test_timedelta_matcher = Matcher.new("test", expire_time=timedelta(seconds=-1)) diff --git a/tests/test_matcher.py b/tests/test_matcher.py index f1d66054..187e5981 100644 --- a/tests/test_matcher.py +++ b/tests/test_matcher.py @@ -157,3 +157,41 @@ async def test_run(app: App): await test_pause().run(bot, event, {}) assert len(matchers[0]) == 1 assert len(matchers[0][0].handlers) == 0 + + +@pytest.mark.asyncio +async def test_expire(app: App, load_plugin): + from nonebot.matcher import matchers + from nonebot.message import _check_matcher + from plugins.matcher.matcher_expire import ( + test_temp_matcher, + test_datetime_matcher, + test_timedelta_matcher, + ) + + event = make_fake_event(_type="test")() + async with app.test_api() as ctx: + bot = ctx.create_bot() + assert test_temp_matcher in matchers[test_temp_matcher.priority] + await _check_matcher( + test_temp_matcher.priority, test_temp_matcher, bot, event, {} + ) + assert test_temp_matcher not in matchers[test_temp_matcher.priority] + + event = make_fake_event()() + async with app.test_api() as ctx: + bot = ctx.create_bot() + assert test_datetime_matcher in matchers[test_datetime_matcher.priority] + await _check_matcher( + test_datetime_matcher.priority, test_datetime_matcher, bot, event, {} + ) + assert test_datetime_matcher not in matchers[test_datetime_matcher.priority] + + event = make_fake_event()() + async with app.test_api() as ctx: + bot = ctx.create_bot() + assert test_timedelta_matcher in matchers[test_timedelta_matcher.priority] + await _check_matcher( + test_timedelta_matcher.priority, test_timedelta_matcher, bot, event, {} + ) + assert test_timedelta_matcher not in matchers[test_timedelta_matcher.priority]