Repository: Cog-Creators/Red-DiscordBot

Issue:
Reserve [p]cancel
# Feature request

#### Select the type of feature you are requesting:

- [ ] Cog
- [ ] Command
- [X] API functionality

#### Describe your requested feature

Reserve a no-op `[p]cancel` command, which is currently used by the built-in [cancelled predicate](https://red-discordbot.readthedocs.io/en/stable/framework_utils.html#redbot.core.utils.predicates.MessagePredicate.cancelled), in the core cog, with a valid docstring, in order to prevent surprising behavior from other third-party cogs registering that command.



Discussion:
Rather than reserving a command here, it might be better to forbid the use of a list of strings as command names or alises and raise an error explaining this when someone attempts to create a command named as such.

This prevents us from dispatching on a no-op while still preventing the surprising behaviors.

Resolving patch:
diff --git a/redbot/cogs/alias/alias.py b/redbot/cogs/alias/alias.py
--- a/redbot/cogs/alias/alias.py
+++ b/redbot/cogs/alias/alias.py
@@ -89,8 +89,12 @@ async def is_alias(
         return False, None
 
     def is_command(self, alias_name: str) -> bool:
+        """
+        The logic here is that if this returns true, the name shouldnt be used for an alias
+        The function name can be changed when alias is reworked
+        """
         command = self.bot.get_command(alias_name)
-        return command is not None
+        return command is not None or alias_name in commands.RESERVED_COMMAND_NAMES
 
     @staticmethod
     def is_valid_alias_name(alias_name: str) -> bool:
diff --git a/redbot/cogs/customcom/customcom.py b/redbot/cogs/customcom/customcom.py
--- a/redbot/cogs/customcom/customcom.py
+++ b/redbot/cogs/customcom/customcom.py
@@ -221,6 +221,9 @@ async def cc_create_random(self, ctx: commands.Context, command: str.lower):
 
         Note: This command is interactive.
         """
+        if command in (*self.bot.all_commands, *commands.RESERVED_COMMAND_NAMES):
+            await ctx.send(_("There already exists a bot command with the same name."))
+            return
         responses = await self.commandobj.get_responses(ctx=ctx)
         try:
             await self.commandobj.create(ctx=ctx, command=command, response=responses)
@@ -240,7 +243,7 @@ async def cc_create_simple(self, ctx, command: str.lower, *, text: str):
         Example:
         - `[p]customcom create simple yourcommand Text you want`
         """
-        if command in self.bot.all_commands:
+        if command in (*self.bot.all_commands, *commands.RESERVED_COMMAND_NAMES):
             await ctx.send(_("There already exists a bot command with the same name."))
             return
         try:
diff --git a/redbot/core/commands/commands.py b/redbot/core/commands/commands.py
--- a/redbot/core/commands/commands.py
+++ b/redbot/core/commands/commands.py
@@ -28,8 +28,14 @@
     "GroupMixin",
     "command",
     "group",
+    "RESERVED_COMMAND_NAMES",
 ]
 
+#: The following names are reserved for various reasons
+RESERVED_COMMAND_NAMES = (
+    "cancel",  # reserved due to use in ``redbot.core.utils.MessagePredicate``
+)
+
 _ = Translator("commands.commands", __file__)
 
 
@@ -155,6 +161,12 @@ def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self._help_override = kwargs.pop("help_override", None)
         self.translator = kwargs.pop("i18n", None)
+        if self.parent is None:
+            for name in (self.name, *self.aliases):
+                if name in RESERVED_COMMAND_NAMES:
+                    raise RuntimeError(
+                        f"The name `{name}` cannot be set as a command name. It is reserved for internal use."
+                    )
 
     def _ensure_assignment_on_copy(self, other):
         super()._ensure_assignment_on_copy(other)


Test patch: