Reputation: 299
I have a Discord.py Command, I want to make a custom permission handler.
My Command:
@commands.command()
@custom_permission("administrator")
async def example(self, ctx, args):
...
In this case, @custom_permission()
is the Permission handler, now how do I make it work for a decorator that in async def?
The Decorator Function:
async def custom_permission(permission):
async def predicate(ctx, permission):
if ctx.author.id in config.owners:
return True
elif permission == "administrator":
if ctx.author.guild_permissions.administrator:
return True
else:
embed = discord.Embed(timestamp=ctx.message.created_at, description=f"You do not meet the required guild permissions the command \"`{ctx.command.name}`\" requires to be executed.\n\nYou need `{permission.upper()}` Permission in this Guild to be able to execute/run/use this command.", color=242424)
embed.set_author(name="Insufficient Permissions", icon_url=config.forbidden_img)
embed.set_footer(text="Numix", icon_url=config.logo)
await ctx.send(embed=embed)
elif permission == "manage_messages":
if ctx.author.guild_permissions.manage_messages:
return True
else:
embed = discord.Embed(timestamp=ctx.message.created_at, description=f"You do not meet the required guild permissions the command \"`{ctx.command.name}`\" requires to be executed.\n\nYou need `{permission.upper()}` Permission in this Guild to be able to execute/run/use this command.", color=242424)
embed.set_author(name="Insufficient Permissions", icon_url=config.forbidden_img)
embed.set_footer(text="Numix", icon_url=config.logo)
await ctx.send(embed=embed)
elif permission == "kick":
if ctx.author.guild_permissions.kick:
return True
else:
embed = discord.Embed(timestamp=ctx.message.created_at, description=f"You do not meet the required guild permissions the command \"`{ctx.command.name}`\" requires to be executed.\n\nYou need `{permission.upper()}` Permission in this Guild to be able to execute/run/use this command.", color=242424)
embed.set_author(name="Insufficient Permissions", icon_url=config.forbidden_img)
embed.set_footer(text="Numix", icon_url=config.logo)
await ctx.send(embed=embed)
elif permission == "ban":
if ctx.author.guild_permissions.ban:
return True
else:
embed = discord.Embed(timestamp=ctx.message.created_at, description=f"You do not meet the required guild permissions the command \"`{ctx.command.name}`\" requires to be executed.\n\nYou need `{permission.upper()}` Permission in this Guild to be able to execute/run/use this command.", color=242424)
embed.set_author(name="Insufficient Permissions", icon_url=config.forbidden_img)
embed.set_footer(text="Numix", icon_url=config.logo)
await ctx.send(embed=embed)
elif permission == "manage_guild":
if ctx.author.guild_permissions.manage_guild:
return True
else:
embed = discord.Embed(timestamp=ctx.message.created_at, description=f"You do not meet the required guild permissions the command \"`{ctx.command.name}`\" requires to be executed.\n\nYou need `{permission.upper()}` Permission in this Guild to be able to execute/run/use this command.", color=242424)
embed.set_author(name="Insufficient Permissions", icon_url=config.forbidden_img)
embed.set_footer(text="Numix", icon_url=config.logo)
await ctx.send(embed=embed)
return commands.check(predicate(ctx, permission))
Now, how do I make this work? I can't change it to a normal function, because If I did that, then I can't send the embed message when the permission requirement is met.
Upvotes: 0
Views: 501
Reputation: 15728
You don’t need to make the outer-most function asynchronous, however the predicate
function can be a coroutine
def custom_permission(permission):
async def predicate(ctx):
...
return commands.check(predicate)
Keep in mind that the predicate
coroutine can only take one argument, ctx
(the arguments are passed internally in the commands.check
method, you do not pass them yourself) If you want to access the other arguments use ctx.args
.
Upvotes: 1
Reputation: 3083
@decorator
def function():
...
is just syntax sugar for this:
def function():
...
function = decorator(function)
In [1]: async def foo():
...: return 42
...:
In [2]: await foo()
Out[2]: 42
In [3]: foo()
Out[3]: <coroutine object foo at 0x7f7ca626da40>
So when you do
@an_async_function("argument")
async def foo():
...
an_async_function("argument")
will be a coroutine object, which you are trying to call.
commands.check
expects an async function, while you're passing in the call to that function.What you can do is:
a) use functools.partial
to partially apply the predicate
function to the permissions
argument:
async def _check_permission(ctx, permission):
...
def custom_permission(permission):
return commands.check(partial(_check_permission, permission=permission))
b) Just use the permission
that you pass in the decorator inside predicate
.
def custom_permission(permission):
async def predicate(ctx):
... # use `permission` here
return commands.check(predicate)
Upvotes: 2