Repository: nonebot/nonebot2

Issue:
Bug: MatcherGroup的Rule覆盖问题
**描述问题：**

阅读源码和实践中发现，`nonebot.plugin.MatcherGroup`有一个比较严重的[问题](https://github.com/nonebot/nonebot2/blob/3460d445b83d1f8c7653f4316a374fb5935a3f25/nonebot/plugin.py#L688)，基于`on_message`的`on_*`都会传递一个`rule=*`的关键字参数，这个参数会覆盖掉group设置的rule参数，导致group设置的参数会直接失效。可以让两个rule pop出来再&起来，就不会有问题了。(pr就不水了)




Discussion:

Resolving patch:
diff --git a/nonebot/plugin.py b/nonebot/plugin.py
--- a/nonebot/plugin.py
+++ b/nonebot/plugin.py
@@ -745,11 +745,7 @@ def on_request(self, **kwargs) -> Type[Matcher]:
         self.matchers.append(matcher)
         return matcher
 
-    def on_startswith(self,
-                      msg: str,
-                      rule: Optional[Optional[Union[Rule,
-                                                    T_RuleChecker]]] = None,
-                      **kwargs) -> Type[Matcher]:
+    def on_startswith(self, msg: str, **kwargs) -> Type[Matcher]:
         """
         :说明:
 
@@ -771,12 +767,14 @@ def on_startswith(self,
 
           - ``Type[Matcher]``
         """
-        return self.on_message(rule=startswith(msg) & rule, **kwargs)
+        final_kwargs = self.base_kwargs.copy()
+        final_kwargs.update(kwargs)
+        final_kwargs.pop("type", None)
+        matcher = on_startswith(msg, **final_kwargs)
+        self.matchers.append(matcher)
+        return matcher
 
-    def on_endswith(self,
-                    msg: str,
-                    rule: Optional[Optional[Union[Rule, T_RuleChecker]]] = None,
-                    **kwargs) -> Type[Matcher]:
+    def on_endswith(self, msg: str, **kwargs) -> Type[Matcher]:
         """
         :说明:
 
@@ -798,12 +796,14 @@ def on_endswith(self,
 
           - ``Type[Matcher]``
         """
-        return self.on_message(rule=endswith(msg) & rule, **kwargs)
+        final_kwargs = self.base_kwargs.copy()
+        final_kwargs.update(kwargs)
+        final_kwargs.pop("type", None)
+        matcher = on_endswith(msg, **final_kwargs)
+        self.matchers.append(matcher)
+        return matcher
 
-    def on_keyword(self,
-                   keywords: Set[str],
-                   rule: Optional[Union[Rule, T_RuleChecker]] = None,
-                   **kwargs) -> Type[Matcher]:
+    def on_keyword(self, keywords: Set[str], **kwargs) -> Type[Matcher]:
         """
         :说明:
 
@@ -825,11 +825,15 @@ def on_keyword(self,
 
           - ``Type[Matcher]``
         """
-        return self.on_message(rule=keyword(*keywords) & rule, **kwargs)
+        final_kwargs = self.base_kwargs.copy()
+        final_kwargs.update(kwargs)
+        final_kwargs.pop("type", None)
+        matcher = on_keyword(keywords, **final_kwargs)
+        self.matchers.append(matcher)
+        return matcher
 
     def on_command(self,
                    cmd: Union[str, Tuple[str, ...]],
-                   rule: Optional[Union[Rule, T_RuleChecker]] = None,
                    aliases: Optional[Set[Union[str, Tuple[str, ...]]]] = None,
                    **kwargs) -> Type[Matcher]:
         """
@@ -842,8 +846,8 @@ def on_command(self,
         :参数:
 
           * ``cmd: Union[str, Tuple[str, ...]]``: 指定命令内容
-          * ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
           * ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
+          * ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
           * ``permission: Optional[Permission]``: 事件响应权限
           * ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
           * ``temp: bool``: 是否为临时事件响应器（仅执行一次）
@@ -856,27 +860,15 @@ def on_command(self,
 
           - ``Type[Matcher]``
         """
-
-        async def _strip_cmd(bot: "Bot", event: "Event", state: T_State):
-            message = event.get_message()
-            segment = message.pop(0)
-            new_message = message.__class__(
-                str(segment)
-                [len(state["_prefix"]["raw_command"]):].strip())  # type: ignore
-            for new_segment in reversed(new_message):
-                message.insert(0, new_segment)
-
-        handlers = kwargs.pop("handlers", [])
-        handlers.insert(0, _strip_cmd)
-
-        commands = set([cmd]) | (aliases or set())
-        return self.on_message(rule=command(*commands) & rule,
-                               handlers=handlers,
-                               **kwargs)
+        final_kwargs = self.base_kwargs.copy()
+        final_kwargs.update(kwargs)
+        final_kwargs.pop("type", None)
+        matcher = on_command(cmd, aliases=aliases, **final_kwargs)
+        self.matchers.append(matcher)
+        return matcher
 
     def on_shell_command(self,
                          cmd: Union[str, Tuple[str, ...]],
-                         rule: Optional[Union[Rule, T_RuleChecker]] = None,
                          aliases: Optional[Set[Union[str, Tuple[str,
                                                                 ...]]]] = None,
                          parser: Optional[ArgumentParser] = None,
@@ -884,53 +876,43 @@ def on_shell_command(self,
         """
         :说明:
 
-        注册一个支持 ``shell_like`` 解析参数的命令消息事件响应器。
+          注册一个支持 ``shell_like`` 解析参数的命令消息事件响应器。
 
-        与普通的 ``on_command`` 不同的是，在添加 ``parser`` 参数时, 响应器会自动处理消息。
+          与普通的 ``on_command`` 不同的是，在添加 ``parser`` 参数时, 响应器会自动处理消息。
 
-        并将用户输入的原始参数列表保存在 ``state["argv"]``, ``parser`` 处理的参数保存在 ``state["args"]`` 中
+          并将用户输入的原始参数列表保存在 ``state["argv"]``, ``parser`` 处理的参数保存在 ``state["args"]`` 中
 
         :参数:
 
-        * ``cmd: Union[str, Tuple[str, ...]]``: 指定命令内容
-        * ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
-        * ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
-        * ``parser: Optional[ArgumentParser]``: ``nonebot.rule.ArgumentParser`` 对象
-        * ``permission: Optional[Permission]``: 事件响应权限
-        * ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
-        * ``temp: bool``: 是否为临时事件响应器（仅执行一次）
-        * ``priority: int``: 事件响应器优先级
-        * ``block: bool``: 是否阻止事件向更低优先级传递
-        * ``state: Optional[T_State]``: 默认 state
-        * ``state_factory: Optional[T_StateFactory]``: 默认 state 的工厂函数
+          * ``cmd: Union[str, Tuple[str, ...]]``: 指定命令内容
+          * ``aliases: Optional[Set[Union[str, Tuple[str, ...]]]]``: 命令别名
+          * ``parser: Optional[ArgumentParser]``: ``nonebot.rule.ArgumentParser`` 对象
+          * ``rule: Optional[Union[Rule, T_RuleChecker]]``: 事件响应规则
+          * ``permission: Optional[Permission]``: 事件响应权限
+          * ``handlers: Optional[List[T_Handler]]``: 事件处理函数列表
+          * ``temp: bool``: 是否为临时事件响应器（仅执行一次）
+          * ``priority: int``: 事件响应器优先级
+          * ``block: bool``: 是否阻止事件向更低优先级传递
+          * ``state: Optional[T_State]``: 默认 state
+          * ``state_factory: Optional[T_StateFactory]``: 默认 state 的工厂函数
 
         :返回:
 
-        - ``Type[Matcher]``
+          - ``Type[Matcher]``
         """
-
-        async def _strip_cmd(bot: "Bot", event: "Event", state: T_State):
-            message = event.get_message()
-            segment = message.pop(0)
-            new_message = message.__class__(
-                str(segment)
-                [len(state["_prefix"]["raw_command"]):].strip())  # type: ignore
-            for new_segment in reversed(new_message):
-                message.insert(0, new_segment)
-
-        handlers = kwargs.pop("handlers", [])
-        handlers.insert(0, _strip_cmd)
-
-        commands = set([cmd]) | (aliases or set())
-        return self.on_message(rule=shell_command(*commands, parser=parser) &
-                               rule,
-                               handlers=handlers,
-                               **kwargs)
+        final_kwargs = self.base_kwargs.copy()
+        final_kwargs.update(kwargs)
+        final_kwargs.pop("type", None)
+        matcher = on_shell_command(cmd,
+                                   aliases=aliases,
+                                   parser=parser,
+                                   **final_kwargs)
+        self.matchers.append(matcher)
+        return matcher
 
     def on_regex(self,
                  pattern: str,
                  flags: Union[int, re.RegexFlag] = 0,
-                 rule: Optional[Rule] = None,
                  **kwargs) -> Type[Matcher]:
         """
         :说明:
@@ -956,7 +938,12 @@ def on_regex(self,
 
           - ``Type[Matcher]``
         """
-        return self.on_message(rule=regex(pattern, flags) & rule, **kwargs)
+        final_kwargs = self.base_kwargs.copy()
+        final_kwargs.update(kwargs)
+        final_kwargs.pop("type", None)
+        matcher = on_regex(pattern, flags=flags, **final_kwargs)
+        self.matchers.append(matcher)
+        return matcher
 
 
 def load_plugin(module_path: str) -> Optional[Plugin]:


Test patch: