mirror of
https://github.com/nonebot/nonebot2.git
synced 2024-11-27 18:45:05 +08:00
✨ add full match Matcher
This commit is contained in:
parent
6ceaf51af7
commit
9f12404338
@ -11,6 +11,7 @@
|
||||
- `on_request` => {ref}``on_request` <nonebot.plugin.on.on_request>`
|
||||
- `on_startswith` => {ref}``on_startswith` <nonebot.plugin.on.on_startswith>`
|
||||
- `on_endswith` => {ref}``on_endswith` <nonebot.plugin.on.on_endswith>`
|
||||
- `on_fullmatch` => {ref}``on_fullmatch` <nonebot.plugin.on.on_fullmatch>`
|
||||
- `on_keyword` => {ref}``on_keyword` <nonebot.plugin.on.on_keyword>`
|
||||
- `on_command` => {ref}``on_command` <nonebot.plugin.on.on_command>`
|
||||
- `on_shell_command` => {ref}``on_shell_command` <nonebot.plugin.on.on_shell_command>`
|
||||
@ -274,6 +275,7 @@ from nonebot.plugin import CommandGroup as CommandGroup
|
||||
from nonebot.plugin import MatcherGroup as MatcherGroup
|
||||
from nonebot.plugin import load_plugins as load_plugins
|
||||
from nonebot.plugin import on_metaevent as on_metaevent
|
||||
from nonebot.plugin import on_fullmatch as on_fullmatch
|
||||
from nonebot.plugin import on_startswith as on_startswith
|
||||
from nonebot.plugin import load_from_json as load_from_json
|
||||
from nonebot.plugin import load_from_toml as load_from_toml
|
||||
|
@ -11,6 +11,7 @@
|
||||
- `on_request` => {ref}``on_request` <nonebot.plugin.on.on_request>`
|
||||
- `on_startswith` => {ref}``on_startswith` <nonebot.plugin.on.on_startswith>`
|
||||
- `on_endswith` => {ref}``on_endswith` <nonebot.plugin.on.on_endswith>`
|
||||
- `on_fullmatch` => {ref}``on_fullmatch` <nonebot.plugin.on.on_fullmatch>`
|
||||
- `on_keyword` => {ref}``on_keyword` <nonebot.plugin.on.on_keyword>`
|
||||
- `on_command` => {ref}``on_command` <nonebot.plugin.on.on_command>`
|
||||
- `on_shell_command` => {ref}``on_shell_command` <nonebot.plugin.on.on_shell_command>`
|
||||
@ -61,6 +62,7 @@ from .on import MatcherGroup as MatcherGroup
|
||||
from .on import on_metaevent as on_metaevent
|
||||
from .plugin import get_plugin as get_plugin
|
||||
from .load import load_plugins as load_plugins
|
||||
from .on import on_fullmatch as on_fullmatch
|
||||
from .on import on_startswith as on_startswith
|
||||
from .load import load_from_json as load_from_json
|
||||
from .load import load_from_toml as load_from_toml
|
||||
|
@ -21,6 +21,7 @@ from nonebot.rule import (
|
||||
command,
|
||||
keyword,
|
||||
endswith,
|
||||
fullmatch,
|
||||
startswith,
|
||||
shell_command,
|
||||
)
|
||||
@ -283,6 +284,30 @@ def on_endswith(
|
||||
return on_message(endswith(msg, ignorecase) & rule, **kwargs, _depth=_depth + 1)
|
||||
|
||||
|
||||
def on_fullmatch(
|
||||
msg: Union[str, Tuple[str, ...]],
|
||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None,
|
||||
ignorecase: bool = False,
|
||||
_depth: int = 0,
|
||||
**kwargs,
|
||||
) -> Matcher:
|
||||
"""
|
||||
注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。
|
||||
|
||||
参数:
|
||||
msg: 指定消息全匹配内容
|
||||
rule: 事件响应规则
|
||||
ignorecase: 是否忽略大小写
|
||||
permission: 事件响应权限
|
||||
handlers: 事件处理函数列表
|
||||
temp: 是否为临时事件响应器(仅执行一次)
|
||||
priority: 事件响应器优先级
|
||||
block: 是否阻止事件向更低优先级传递
|
||||
state: 默认 state
|
||||
"""
|
||||
return on_message(fullmatch(msg, ignorecase) & rule, **kwargs, _depth=_depth + 1)
|
||||
|
||||
|
||||
def on_keyword(
|
||||
keywords: Set[str],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = None,
|
||||
@ -611,6 +636,28 @@ class MatcherGroup:
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_fullmatch(self, msg: Union[str, Tuple[str, ...]], **kwargs) -> Type[Matcher]:
|
||||
"""
|
||||
注册一个消息事件响应器,并且当消息的**文本部分**与指定内容完全一致时响应。
|
||||
|
||||
参数:
|
||||
msg: 指定消息全匹配内容
|
||||
rule: 事件响应规则
|
||||
ignorecase: 是否忽略大小写
|
||||
permission: 事件响应权限
|
||||
handlers: 事件处理函数列表
|
||||
temp: 是否为临时事件响应器(仅执行一次)
|
||||
priority: 事件响应器优先级
|
||||
block: 是否阻止事件向更低优先级传递
|
||||
state: 默认 state
|
||||
"""
|
||||
final_kwargs = self.base_kwargs.copy()
|
||||
final_kwargs.update(kwargs)
|
||||
final_kwargs.pop("type", None)
|
||||
matcher = on_fullmatch(msg, **final_kwargs, _depth=1)
|
||||
self.matchers.append(matcher)
|
||||
return matcher
|
||||
|
||||
def on_keyword(self, keywords: Set[str], **kwargs) -> Type[Matcher]:
|
||||
"""
|
||||
注册一个消息事件响应器,并且当消息纯文本部分包含关键词时响应。
|
||||
|
@ -79,6 +79,18 @@ def on_endswith(
|
||||
block: bool = ...,
|
||||
state: Optional[T_State] = ...,
|
||||
) -> Type[Matcher]: ...
|
||||
def on_fullmatch(
|
||||
msg: Union[str, Tuple[str, ...]],
|
||||
rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = ...,
|
||||
ignorecase: bool = ...,
|
||||
*,
|
||||
permission: Optional[Union[Permission, T_PermissionChecker]] = ...,
|
||||
handlers: Optional[List[Union[T_Handler, Dependent]]] = ...,
|
||||
temp: bool = ...,
|
||||
priority: int = ...,
|
||||
block: bool = ...,
|
||||
state: Optional[T_State] = ...,
|
||||
) -> Type[Matcher]: ...
|
||||
def on_keyword(
|
||||
keywords: Set[str],
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||
@ -261,6 +273,19 @@ class MatcherGroup:
|
||||
block: bool = ...,
|
||||
state: Optional[T_State] = ...,
|
||||
) -> Type[Matcher]: ...
|
||||
def on_fullmatch(
|
||||
self,
|
||||
msg: Union[str, Tuple[str, ...]],
|
||||
*,
|
||||
ignorecase: bool = ...,
|
||||
rule: Optional[Union[Rule, T_RuleChecker]] = ...,
|
||||
permission: Optional[Union[Permission, T_PermissionChecker]] = ...,
|
||||
handlers: Optional[List[Union[T_Handler, Dependent]]] = ...,
|
||||
temp: bool = ...,
|
||||
priority: int = ...,
|
||||
block: bool = ...,
|
||||
state: Optional[T_State] = ...,
|
||||
) -> Type[Matcher]: ...
|
||||
def on_keyword(
|
||||
self,
|
||||
keywords: Set[str],
|
||||
|
@ -171,6 +171,47 @@ def endswith(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule
|
||||
return Rule(EndswithRule(msg, ignorecase))
|
||||
|
||||
|
||||
class FullmatchRule:
|
||||
"""检查消息纯文本是否与指定字符串全匹配。
|
||||
|
||||
参数:
|
||||
msg: 指定消息全匹配字符串元组
|
||||
ignorecase: 是否忽略大小写
|
||||
"""
|
||||
|
||||
__slots__ = ("msg", "ignorecase")
|
||||
|
||||
def __init__(self, msg: Tuple[str, ...], ignorecase: bool = False):
|
||||
self.msg = msg
|
||||
self.ignorecase = ignorecase
|
||||
|
||||
async def __call__(
|
||||
self, type: str = EventType(), text: str = EventPlainText()
|
||||
) -> bool:
|
||||
if type != "message":
|
||||
return False
|
||||
return bool(
|
||||
text
|
||||
and any(
|
||||
full.lower() == text.lower() if self.ignorecase else full == text
|
||||
for full in self.msg
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def fullmatch(msg: Union[str, Tuple[str, ...]], ignorecase: bool = False) -> Rule:
|
||||
"""完全匹配消息。
|
||||
|
||||
参数:
|
||||
msg: 指定消息全匹配字符串元组
|
||||
ignorecase: 是否忽略大小写
|
||||
"""
|
||||
if isinstance(msg, str):
|
||||
msg = (msg,)
|
||||
|
||||
return Rule(FullmatchRule(msg, ignorecase))
|
||||
|
||||
|
||||
class KeywordsRule:
|
||||
"""检查消息纯文本是否包含指定关键字。
|
||||
|
||||
|
@ -105,6 +105,43 @@ async def test_endswith(
|
||||
assert await dependent(event=event) == expected
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"msg,ignorecase,type,text,expected",
|
||||
[
|
||||
("fullmatch", False, "message", "fullmatch", True),
|
||||
("fullmatch", False, "message", "Fullmatch", False),
|
||||
("fullmatch", True, "message", "fullmatch", True),
|
||||
("fullmatch", True, "message", "Fullmatch", True),
|
||||
("fullmatch", False, "message", "fullfoo", False),
|
||||
("fullmatch", False, "message", "_fullmatch_", False),
|
||||
(("fullmatch", "foo"), False, "message", "fullmatchfoo", False),
|
||||
("fullmatch", False, "notice", "foo", False),
|
||||
],
|
||||
)
|
||||
async def test_fullmatch(
|
||||
app: App,
|
||||
msg: Union[str, Tuple[str, ...]],
|
||||
ignorecase: bool,
|
||||
type: str,
|
||||
text: str,
|
||||
expected: bool,
|
||||
):
|
||||
from nonebot.rule import FullmatchRule, fullmatch
|
||||
|
||||
test_fullmatch = fullmatch(msg, ignorecase)
|
||||
dependent = list(test_fullmatch.checkers)[0]
|
||||
checker = dependent.call
|
||||
|
||||
assert isinstance(checker, FullmatchRule)
|
||||
assert checker.msg == (msg,) if isinstance(msg, str) else msg
|
||||
assert checker.ignorecase == ignorecase
|
||||
|
||||
message = make_fake_message()(text)
|
||||
event = make_fake_event(_type=type, _message=message)()
|
||||
assert await dependent(event=event) == expected
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"kws,type,text,expected",
|
||||
|
@ -122,14 +122,15 @@ matcher = on_message()
|
||||
5. `on_notice`: 创建通知事件响应器。
|
||||
6. `on_startswith`: 创建消息开头匹配事件响应器。
|
||||
7. `on_endswith`: 创建消息结尾匹配事件响应器。
|
||||
8. `on_keyword`: 创建消息关键词匹配事件响应器。
|
||||
9. `on_command`: 创建命令消息事件响应器。
|
||||
10. `on_shell_command`: 创建 shell 命令消息事件响应器。
|
||||
11. `on_regex`: 创建正则表达式匹配事件响应器。
|
||||
12. `CommandGroup`: 创建具有共同命令名称前缀的命令组。
|
||||
13. `MatcherGroup`: 创建具有共同参数的响应器组。
|
||||
8. `on_fullmatch`: 创建消息完全匹配事件响应器。
|
||||
9. `on_keyword`: 创建消息关键词匹配事件响应器。
|
||||
10. `on_command`: 创建命令消息事件响应器。
|
||||
11. `on_shell_command`: 创建 shell 命令消息事件响应器。
|
||||
12. `on_regex`: 创建正则表达式匹配事件响应器。
|
||||
13. `CommandGroup`: 创建具有共同命令名称前缀的命令组。
|
||||
14. `MatcherGroup`: 创建具有共同参数的响应器组。
|
||||
|
||||
其中,`on_metaevent` `on_message` `on_request` `on_notice` 函数都是在 `on` 的基础上添加了对应的事件类型 `type`;`on_startswith` `on_endswith` `on_keyword` `on_command` `on_shell_command` `on_regex` 函数都是在 `on_message` 的基础上添加了对应的匹配规则 `rule`。
|
||||
其中,`on_metaevent` `on_message` `on_request` `on_notice` 函数都是在 `on` 的基础上添加了对应的事件类型 `type`;`on_startswith` `on_endswith` `on_fullmatch` `on_keyword` `on_command` `on_shell_command` `on_regex` 函数都是在 `on_message` 的基础上添加了对应的匹配规则 `rule`。
|
||||
|
||||
## 自定义规则
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user