Repository: nonebot/nonebot2

Issue:
自定义规则时,state保留的信息出现了问题。
**描述问题：**

我仿照`nonebot.rule.regex`，写了一个规则`reg`，这个规则满足条件时会在`state`中添加`re.search()`匹配后的`match`。
但是在测试阶段，发现`state`的`_matched`和`match`都不见了，logger记录为`None`,这让我很疑惑。

**如何复现？**
```python
from nonebot import on_message,on_regex
from nonebot.adapters import Bot, Event
from nonebot.rule import Rule
from nonebot.typing import T_State
from nonebot.log import logger
from typing import Union
import re
def reg(regex: str, flags: Union[int, re.RegexFlag] = 0) -> Rule:

    pattern = re.compile(regex, flags)

    async def _regex(bot: Bot, event: Event, state: T_State) -> bool:
        if event.get_type() != "message":
            return False
        text = event.get_plaintext()
        matched = pattern.search(text)
        if matched:
            state['match'] = matched
            state["_matched"] = matched.group()
            return True
        else:
            state['match'] = None
            state["_matched"] = None
            return False

    return Rule(_regex)


testreg = on_message(reg(r'^谁是(.{1,20})$'))
testreg1 = on_message(reg(r'^nb'))

@testregex.handle()
async def _(bot: Bot, event: Event, state: T_State):
    logger.debug(str(state['match']))
    logger.debug(str(state['_matched']))
````
以上我测试的代码。
一个令人困惑的地方是，如果用这个规则注册了两个响应器,即`testreg`和`testreg1`,那么在我测试触发`testreg`，发送
`谁是我`,日志会记录两个`None`,但如果将第二个响应器注释掉，只保留一个`testreg`时，发送`谁是我`，日志将会正常记录。
在之后，我将这两个响应器换成用普通的`on_regex()`函数注册，同样地会出现相似的问题：注册两个时候第一个记录的`state['_matched']`是`None`，注册一个时候`state['_matched']`会正常记录

**期望的结果**

我觉得不管这个规则注册了几个响应器，在处理事件时`state`都应该保留`match`和`_matched`。

**环境信息：**

 - OS: ubuntu_20.04 and Windows 10 LTSC
 - Python Version: 3.9.1
 - Nonebot Version: 2.0.0a8.post2



Discussion:
事实上, 我们在[文档中已经注明了](https://v2.nonebot.dev/guide/creating-a-matcher.html#%E8%87%AA%E5%AE%9A%E4%B9%89-rule):
> 请勿将事件处理的逻辑写入 rule 中，这会使得事件处理返回奇怪的响应。

我们通常不建议在rule中写入任何对命令处理造成影响的逻辑
> 事实上, 我们在[文档中已经注明了](https://v2.nonebot.dev/guide/creating-a-matcher.html#%E8%87%AA%E5%AE%9A%E4%B9%89-rule):
> 
> > 请勿将事件处理的逻辑写入 rule 中，这会使得事件处理返回奇怪的响应。
> 
> 我们通常不建议在rule中写入任何对命令处理造成影响的逻辑

但即便是nonebot自身的代码，即用on_regex注册的响应器也会出现这个问题，state之间会冲突
> > 事实上, 我们在[文档中已经注明了](https://v2.nonebot.dev/guide/creating-a-matcher.html#%E8%87%AA%E5%AE%9A%E4%B9%89-rule):
> > > 请勿将事件处理的逻辑写入 rule 中，这会使得事件处理返回奇怪的响应。
> > 
> > 
> > 我们通常不建议在rule中写入任何对命令处理造成影响的逻辑
> 
> 但即便是nonebot自身的代码，即用on_regex注册的响应器也会出现这个问题，state之间会冲突

这样的结果是，[regex规则](https://github.com/nonebot/nonebot2/blob/0428b1dd81263e474d7e18e36745d5bb9d572d14/nonebot/rule.py#L285)中的

>       可以通过 ``state["_matched"]`` 获取正则表达式匹配成功的文本。
将很难真正实现到。
目前在测试中，发现了一个粗鲁的但有效的办法解决这个问题，就是在不满足rule_checker的时候，直接返回`False`即可，不处理`state`。
```
...
else:
    return False
...
```

Resolving patch:
diff --git a/nonebot/message.py b/nonebot/message.py
--- a/nonebot/message.py
+++ b/nonebot/message.py
@@ -99,47 +99,30 @@ def run_postprocessor(func: T_RunPostProcessor) -> T_RunPostProcessor:
     return func
 
 
-async def _check_matcher(priority: int, bot: "Bot", event: "Event",
-                         state: T_State) -> Iterable[Type[Matcher]]:
-    current_matchers = matchers[priority].copy()
-
-    async def _check(Matcher: Type[Matcher], bot: "Bot", event: "Event",
-                     state: T_State) -> Optional[Type[Matcher]]:
+async def _check_matcher(priority: int, Matcher: Type[Matcher], bot: "Bot",
+                         event: "Event", state: T_State) -> None:
+    if Matcher.expire_time and datetime.now() > Matcher.expire_time:
         try:
-            if (not Matcher.expire_time or datetime.now() <= Matcher.expire_time
-               ) and await Matcher.check_perm(
-                   bot, event) and await Matcher.check_rule(bot, event, state):
-                return Matcher
-        except Exception as e:
-            logger.opt(colors=True, exception=e).error(
-                f"<r><bg #f8bbd0>Rule check failed for {Matcher}.</bg #f8bbd0></r>"
-            )
-        return None
-
-    async def _check_expire(Matcher: Type[Matcher]) -> Optional[Type[Matcher]]:
-        if Matcher.expire_time and datetime.now() > Matcher.expire_time:
-            return Matcher
-        return None
-
-    checking_tasks = [
-        _check(Matcher, bot, event, state) for Matcher in current_matchers
-    ]
-    checking_expire_tasks = [
-        _check_expire(Matcher) for Matcher in current_matchers
-    ]
-    results = await asyncio.gather(*checking_tasks)
-    expired = await asyncio.gather(*checking_expire_tasks)
-    for expired_matcher in filter(lambda x: x, expired):
-        try:
-            matchers[priority].remove(expired_matcher)  # type: ignore
+            matchers[priority].remove(Matcher)
         except Exception:
             pass
-    for temp_matcher in filter(lambda x: x and x.temp, results):
+        return
+
+    try:
+        if not await Matcher.check_perm(
+                bot, event) or not await Matcher.check_rule(bot, event, state):
+            return
+    except Exception as e:
+        logger.opt(colors=True, exception=e).error(
+            f"<r><bg #f8bbd0>Rule check failed for {Matcher}.</bg #f8bbd0></r>")
+
+    if Matcher.temp:
         try:
-            matchers[priority].remove(temp_matcher)  # type: ignore
+            matchers[priority].remove(Matcher)
         except Exception:
             pass
-    return filter(lambda x: x, results)  # type: ignore
+
+    await _run_matcher(Matcher, bot, event, state)
 
 
 async def _run_matcher(Matcher: Type[Matcher], bot: "Bot", event: "Event",
@@ -244,11 +227,9 @@ async def handle_event(bot: "Bot", event: "Event"):
         if show_log:
             logger.debug(f"Checking for matchers in priority {priority}...")
 
-        run_matchers = await _check_matcher(priority, bot, event, state)
-
         pending_tasks = [
-            _run_matcher(matcher, bot, event, state.copy())
-            for matcher in run_matchers
+            _check_matcher(priority, matcher, bot, event, state.copy())
+            for matcher in matchers[priority]
         ]
 
         results = await asyncio.gather(*pending_tasks, return_exceptions=True)
diff --git a/nonebot/rule.py b/nonebot/rule.py
--- a/nonebot/rule.py
+++ b/nonebot/rule.py
@@ -282,7 +282,8 @@ def regex(regex: str, flags: Union[int, re.RegexFlag] = 0) -> Rule:
 
       根据正则表达式进行匹配。
 
-      可以通过 ``state["_matched"]`` 获取正则表达式匹配成功的文本。
+      可以通过 ``state["_matched"]`` ``state["_matched_groups"]`` ``state["_matched_dict"]``
+      获取正则表达式匹配成功的文本。
 
     :参数:
 
@@ -302,9 +303,13 @@ async def _regex(bot: "Bot", event: "Event", state: T_State) -> bool:
         matched = pattern.search(str(event.get_message()))
         if matched:
             state["_matched"] = matched.group()
+            state["_matched_groups"] = matched.groups()
+            state["_matched_dict"] = matched.groupdict()
             return True
         else:
             state["_matched"] = None
+            state["_matched_groups"] = None
+            state["_matched_dict"] = None
             return False
 
     return Rule(_regex)


Test patch: