Repository: Cog-Creators/Red-DiscordBot

Issue:
[V3 Permissions] UX

I'll be going through and improving permissions UX a bit later. There are a couple of things which were pointed out by @retke which can be improved. Namely:

- [x] Consistent Argument ordering
- [x] Consistent way to remove settings


Discussion:
Also, I don't know how this should be done, but maybe we could add something so we can print a message when the rules doesn't allow the execution of a command.

For example, if we aren't in an authorised voice channel and we try to use the Audio cog, the bot should output something like "I cannot join that voice channel".
And also, if I can add some suggestions, I'd like these features in the permissions cog:

- Be able to select everything (instead of a cog or a command), so you can do things like lock all commands everywhere except in the #bot channel.

- Be able to select multiple objects and combine them (for example x role can use y command in z channel only, while the others can't)
I feel like both of those are out of scope for what permissions is aimed at doing. That said, if you want either of those behaviors, here's a brief demo of the extensibility built into permissions (not thoroughly documented yet) for such special cases.

```py
class PermissionExtender:
    """Demonstrate the pre/post hooks in core Permissions"""

    async def __red_permissions_before(self, ctx, level) -> bool:
        """
        This gets evaluated prior to normal permissions logic.
        Should return 
        `True` if the user can run it, (use this one with extreme caution)
        `False` if they can't or 
        `None` to defer to remaining logic
        """
        if ctx.guild is None:
            return None
        if ctx.guild.id == 133049272517001216:
            if ctx.channel.name == "bot-chamber":
                return None  # We don't want to allow, we want to not disallow
            else:
                return False  # block commands in that guild not in the bot-chamber
   

    async def __red_permissions_after(self, ctx, level) -> bool:
        """
        This gets evaluated after normal permissions logic.
        Should return 
        `True` if the user can run it, (use this one with extreme caution)
        `False` if they can't or 
        `None` to defer to remaining logic 
        """
        if ctx.channel.name == "red-team" and "Red Team Leader" in [r.name for r in ctx.author.roles]:
            if ctx.command.name == 'mute':
                return True
        return None
```

Resolving patch:
diff --git a/redbot/cogs/permissions/converters.py b/redbot/cogs/permissions/converters.py
--- a/redbot/cogs/permissions/converters.py
+++ b/redbot/cogs/permissions/converters.py
@@ -27,3 +27,18 @@ async def convert(self, ctx: commands.Context, arg: str) -> str:
         raise commands.BadArgument(
             '"{arg}" is not a valid rule. Valid rules are "allow" or "deny"'.format(arg=arg)
         )
+
+
+class ClearableRuleType(commands.Converter):
+    async def convert(self, ctx: commands.Context, arg: str) -> str:
+        if arg.lower() in ("allow", "whitelist", "allowed"):
+            return "allow"
+        if arg.lower() in ("deny", "blacklist", "denied"):
+            return "deny"
+        if arg.lower() in ("clear", "reset"):
+            return "clear"
+
+        raise commands.BadArgument(
+            '"{arg}" is not a valid rule. Valid rules are "allow" or "deny", or "clear" to remove the rule'
+            "".format(arg=arg)
+        )
diff --git a/redbot/cogs/permissions/permissions.py b/redbot/cogs/permissions/permissions.py
--- a/redbot/cogs/permissions/permissions.py
+++ b/redbot/cogs/permissions/permissions.py
@@ -11,7 +11,7 @@
 
 from .resolvers import val_if_check_is_valid, resolve_models, entries_from_ctx
 from .yaml_handler import yamlset_acl, yamlget_acl
-from .converters import CogOrCommand, RuleType
+from .converters import CogOrCommand, RuleType, ClearableRuleType
 from .mass_resolution import mass_resolve
 
 _models = ["owner", "guildowner", "admin", "mod", "all"]
@@ -521,18 +521,12 @@ async def rem_from_guild_rule(
     @checks.guildowner_or_permissions(administrator=True)
     @permissions.command(name="setdefaultguildrule")
     async def set_default_guild_rule(
-        self, ctx: commands.Context, cog_or_command: CogOrCommand, allow_or_deny: RuleType = None
+        self, ctx: commands.Context, allow_or_deny: ClearableRuleType, cog_or_command: CogOrCommand
     ):
         """
         Sets the default behavior for a cog or command if no rule is set
-
-        Use with a cog or command and no setting to clear the default and defer to
-        normal check logic
         """
-        if allow_or_deny:
-            val_to_set = {"allow": True, "deny": False}.get(allow_or_deny)
-        else:
-            val_to_set = None
+        val_to_set = {"allow": True, "deny": False, "clear": None}.get(allow_or_deny)
 
         model_type, type_name = cog_or_command
         async with self.config.guild(ctx.guild).owner_models() as models:
@@ -551,19 +545,12 @@ async def set_default_guild_rule(
     @checks.is_owner()
     @permissions.command(name="setdefaultglobalrule")
     async def set_default_global_rule(
-        self, ctx: commands.Context, cog_or_command: CogOrCommand, allow_or_deny: RuleType = None
+        self, ctx: commands.Context, allow_or_deny: ClearableRuleType, cog_or_command: CogOrCommand
     ):
         """
         Sets the default behavior for a cog or command if no rule is set
-
-        Use with a cog or command and no setting to clear the default and defer to
-        normal check logic
         """
-
-        if allow_or_deny:
-            val_to_set = {"allow": True, "deny": False}.get(allow_or_deny)
-        else:
-            val_to_set = None
+        val_to_set = {"allow": True, "deny": False, "clear": None}.get(allow_or_deny)
 
         model_type, type_name = cog_or_command
         async with self.config.owner_models() as models:


Test patch: