Skip to content

Instantly share code, notes, and snippets.

@Soheab
Last active October 20, 2022 09:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Soheab/ec3f40f9f54add0dba787783719331f8 to your computer and use it in GitHub Desktop.
Save Soheab/ec3f40f9f54add0dba787783719331f8 to your computer and use it in GitHub Desktop.
See here a custom check for all types of commands that can be used to check if user has any permissions you pass

See here the "complete" check

"complete" as in it supports app and text commands and has some comments explaining the code. As you may have noticed, there are multiple files in this gist:

Text Cmmands Only Here

Complete

See here the complete check with support for app and text commands:

from typing import Any, Callable, Union, Literal, TYPE_CHECKING

from discord import Permissions, Interaction
from discord.app_commands import CheckFailure as AppCheckFailure, check as app_check, Command as AppCommand
from discord.ext.commands import Context, CheckFailure as ExtCheckFailure, check as ext_check, Command as ExtCommand

if TYPE_CHECKING:
    from discord.ext.commands._types import CoroFunc

    ExtCheckFunc = Union[ExtCommand[Any, ..., Any], CoroFunc]
    from discord.app_commands.commands import CheckInputParameter as AppCheckFunc
else:
    ExtCheckFunc = Callable
    AppCheckFunc = Callable

# error to raise
class MissingAllPermissions(ExtCheckFailure, AppCheckFailure):
    pass


def has_any_permissions(
    command_type: Literal["app", "text", "hybrid", "auto"] = "auto", **perms: bool
) -> Union[ExtCheckFunc, AppCheckFunc]:
    # check if all permissions are valid
    invalid = perms.keys() - Permissions.VALID_FLAGS.keys()
    if invalid:
        raise TypeError(f"Invalid permission(s): {', '.join(invalid)}")

    # the actual check
    # since this supports a text and slash command, we need to accept either Context or Interaction
    def predicate(ctx_interaction: Union[Context, Interaction]):

        # attribute exists on both so its safe to use.
        permissions = ctx_interaction.permissions

        # check the perms
        if any(value == getattr(permissions, perm) for perm, value in perms.items()):
            return True

        raise MissingAllPermissions(f"You need at least one of the following permissions: {', '.join(perms.keys())}")

    command_type = command_type if command_type != "hybrid" else "text"
    from_command_type = {"text": ext_check, "app": app_check}

    # this is where we handle the input when command_type is set to "auto" (default)
    def fake_check(check_input: Union[ExtCheckFunc, AppCheckFunc]):
        # return the right check based on the command type
        if isinstance(check_input, ExtCommand):
            return from_command_type["text"](predicate)(check_input)
        elif isinstance(check_input, AppCommand):
            return from_command_type["app"](predicate)(check_input)
        else:
            # else guess from the missing first param
            try:
                check_input()  # type: ignore # call the input to see if it raises an error
            except TypeError as e:
                if "'ctx'" in str(e):
                    return from_command_type["text"](predicate)(check_input)
                elif "'interaction'" in str(e):
                    return from_command_type["app"](predicate)(check_input)
                else:
                    raise TypeError("Could not guess command type, please specify it manually.")

    # check the command_type and return accordingly
    if command_type != "auto":
        return from_command_type[command_type](predicate)
    else:
        return fake_check  # type: ignore # this is a callable
from typing import Any, Callable, Union, Literal, TYPE_CHECKING
from discord import Permissions
from discord.ext.commands import Context, CheckFailure, check, Command
if TYPE_CHECKING:
from discord.ext.commands._types import CoroFunc
CheckFunc = Union[Command[Any, ..., Any], CoroFunc]
else:
CheckFunc = Callable
# error to raise
class MissingAllPermissions(CheckFailure):
pass
def has_any_permissions(**perms: bool) -> CheckFunc:
invalid = perms.keys() - Permissions.VALID_FLAGS.keys()
if invalid:
raise TypeError(f"Invalid permission(s): {', '.join(invalid)}")
def predicate(ctx: Context):
permissions = ctx.permissions
if any(value == getattr(permissions, perm) for perm, value in perms.items()):
return True
raise MissingAllPermissions(f"You need at least one of the following permissions: {', '.join(perms.keys())}")
return check(predicate)

Here is how to use this check

# text command
@bot.command(...)  # @commands.command(...)
@has_any_permissions(kick_members=True)
async def foo(ctx: commands.Context):
    ...

# hybrid command
@hybrid.command()  # @commands.hybrid_command(...)
@has_any_permissions(ban_members=True)
async def bar(ctx: commands.Context):
    ...

# slash command
@bot.tree.command()  # @app_commands.command(...) or @tree.command(...)
@has_any_permissions(moderate_members=True)
async def aaa(interaction: discord.Interaction):
    ...

# context user menu
@bot.tree.context_menu()  # @app_commands.context_menu(...) or @tree.context_menu(...)
@has_any_permissions(manage_messages=True)
async def bbb(interaction: discord.Interaction, member: discord.Member):
    ...

# context message menu
@bot.tree.context_menu()  # @app_commands.context_menu(...) or @tree.context_menu(...)
@has_any_permissions(send_message=True)
async def ccc(interaction: discord.Interaction, member: discord.Message):
    ...

the moment you notice these are all a joke because it's not hard at all to use it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment