add full match Matcher

This commit is contained in:
Akirami 2022-02-15 08:20:29 +08:00
parent 6ceaf51af7
commit 9f12404338
No known key found for this signature in database
GPG Key ID: 1C5732E68EB72CB3
7 changed files with 162 additions and 7 deletions

View File

@ -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

View File

@ -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

View File

@ -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]:
"""
注册一个消息事件响应器并且当消息纯文本部分包含关键词时响应

View File

@ -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],

View File

@ -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:
"""检查消息纯文本是否包含指定关键字。

View File

@ -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",

View File

@ -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`
## 自定义规则