Permission System for Discord.py Bot - python

I am in the process of making a discord bot using discord.py and asyncio. The bot has commands like kick and ban which obviously should not be available to normal users.
I want to make a simple system which will detect what permissions the user's role has using ctx.message.author to get the user who sent the command.
I do not want the bot to detect a specific role name as these vary across servers. I also prefer not to have multiple files for the bot to keep it simple.
I have seen the discord.py documentation and various other sources but none contain examples of how to implement the various methods they talk about.
As an example, here is a single command from my bot:
async def kick(ctx, userName: discord.User):
if True: #ctx.message.author.Permissions.administrator
await BSL.kick(userName)
else:
permission_error = str('Sorry ' + ctx.message.author + ' you do not have permissions to do that!')
await BSL.send_message(ctx.message.channel, permission_error)
Where the if else statement is my attempt of doing this on my own. The #ctx.message.author.Permissions.administrator is commented out as it does not work and replaced with True for testing purposes.
Thank you for any help and suggestions in advance.

Permissions is the name of the class. To get the message authors permissions, you should access the guild_permissions property of the author.
if ctx.message.author.guild_permissions.administrator:
# you could also use guild_permissions.kick_members
Update:
A better way to validate the permissions of the person invoking the commands is by using the check feature of the commands extension, specifically the has_permissions check. For example, if you wanted to open your command only to people who had either the manage_roles permission or the ban_members permission, you could write your command like this:
from discord import Member
from discord.ext.commands import has_permissions, MissingPermissions
#bot.command(name="kick", pass_context=True)
#has_permissions(manage_roles=True, ban_members=True)
async def _kick(ctx, member: Member):
await bot.kick(member)
#_kick.error
async def kick_error(ctx, error):
if isinstance(error, MissingPermissions):
text = "Sorry {}, you do not have permissions to do that!".format(ctx.message.author)
await bot.send_message(ctx.message.channel, text)

You could also use decorators.
#bot.command(name = "Kick")
#bot.has_permissions(kick_user = True)
#bot.bot_has_permissions(kick_user = True)
async def _kick(ctx, member: Member):
#Do stuff...
The advantage of checking user and bot permissions means it is easier to handle errors from either providing useful "Insufficient Permission" error messages.

The tips found the accepted answer may not work:
There may be compatibility issues with the rewrite version of the discord.py library and the pre-rewrite versions, which remain non-obsolete, non-deprecated, and still in use.
The bot should also check it's own permissions, to rule out one reason for the error.
If there is an error, or permissions for the bot itself are invalid, the bot should say something, correct?
Something needs to be implemented to prevent the bot from attempting to work this command in a DM or group context. It will almost always error.
I propose the following solution for pre-rewrite (assuming you use the command extension):
import discord
from discord.ext import commands
import time
#bot.command(pass_context=True,description="Kicks the given member. Please ensure both the bot and the command invoker have the permission 'Kick Members' before running this command.")
async def kick(ctx, target:discord.Member):
"""(GUILD ONLY) Boot someone outta the server. See 's!kick' for more."""
if not str(ctx.message.channel).startswith("Direct Message with "):
msg=await bot.say("Checking perms...")
time.sleep(0.5)
if ctx.message.server.me.server_permissions.kick_members:
if ctx.message.author.server_permissions.kick_members:
await bot.edit_message(msg,new_content="All permissions valid, checking issues with target...")
time.sleep(0.5)
if target==ctx.message.server.owner:
await bot.edit_message(msg, new_content="All permissions are correct, but you're attempting to kick the server owner, whom you can't kick no matter how hard you try. Whoops!")
else:
if target==ctx.message.server.me:
await bot.edit_message(msg, new_content="Whoops! All permissions are corrent, but you just tried to make me kick myself, which is not possible. Perhaps you meant someone else, not poor me?")
else:
await bot.edit_message(msg, new_content="All permissions correct, and no issues with target being self or server owner, attempting to kick.")
time.sleep(2)
try:
await bot.kick(target)
await bot.edit_message(msg, ":boom: BAM! ***kicc'd***")
except Exception:
await bot.edit_message(msg, new_content="I was unable to kick the passed member. The member may have a higher role than me, I may have crashed into a rate-limit, or an unknown error may have occured. In that case, try again.")
else:
await bot.edit_message(msg, new_content="I've the correct permissions, {}, but you do not. Perhaps ask for them?").format(ctx.message.author.mention)
else:
await bot.edit_message(msg, new_content="I'm just a poor bot with no permissions. Could you kindly grant me the permission `Kick Members`? Thanks! :slight_smile:")
else:
await bot.say("'Tis a DM! This command is for servers only... try this again in a server maybe? :slight_smile:")

Based on the current version of discord.py, using discord.ext.commands, you can do this:
Replace all <> with what it says.
import discord
from discord.ext import commands
bot = commands.Bot(command_prefix = "<this can be whatever you want>", intents = discord.intents.all())
#make sure to enable all intents in the discord dev portal. I use all intents to make things simple.
#bot.command()
#commands.has_permissions(<permission> = True)
async def ban():
#the ban commands go here
The permission has to be exactly like how it says in the api documentation here.
These permission ID's are for discord, but they are the same for discord.ext.
The ID for banning is ban_members. In the decorator #commands.has_permissions, remember to NOT put the ID in quotes. Also remember to add = True to the end of the id.

Related

How to program a discord.py bot to dm me when a command is used by another person?

I want to create a command that people can use to send suggestions to me. They'd use the command "!suggest", followed by their input, and the bot privately dms that whole message to me. However I am struggling with making the bot dm me instead of the author of the command.
if message.content.startswith('!suggest'):
await message.author.send(message.content)
The problem here lies with the author part in the code- instead of dming me every time, it will dm the person who typed the command, which is useless to me. How can I replace 'author' with my discord ID, to make it dm me and only me with the message? I've asked this before and all answers have not worked for me. These are the solution's I've gotten that did not work:
message.author.dm_channel.send("message"), but that is the same problem, it does not get rid of the author issue.
me = await client.get_user_info('MY_SNOWFLAKE_ID')
await client.send_message(me, "Hello!")
This code also did not work for me. The user that it is dming is the same each time, so I have no need to run the get_user_info in the first place. And whenever the second line runs, I get an error code because the MyClient cannot use the send_message attribute.
Your code is from the old discord version. It has been changed in the discord.py rewrite. I also recommend you to use discord.ext.commands instead of using on_message to read commands manually.
As for the dm, simply use
your_id = 123456789012345678
me = client.get_user(your_id)
await me.send("Hello")
Note that it requires members intents enabled. Use await fetch_user(your_id) if you don't have it enabled. Also, note that there might be also rate-limiting consequences if multiple users use commands simultaneously.
Docs:
get_user
fetch_user
User.send
#client.command()
async def suggest(ctx, message = None):
if message == None:
await ctx.send("Provide a suggestion")
else:
await ctx.{your_id}.send("message")
Please don't mind the indent
You can dm a user by doing this:
user = client.get_user(your_id_here)
await user.send('work!')
FAQ : https://discordpy.readthedocs.io/en/stable/faq.html#how-do-i-send-a-dm
if you want the full code here it is:
#client.command()
async def suggest(ctx, *, suggest_text):
user_suggestor = ctx.author
user = client.get_user(user_id) # replace the user_id to your user id
if suggest_text == None:
await ctx.send("Enter your suggestion! example : !suggest {your suggest here}")
else:
await user.send(f"{user_suggestor.name} sent an suggestion! \nSuggestion : {suggest_text}") # the \n thing is to space
thank me later :D

Check if user has Admin -- Discord.py

I would like to make a command that requires the user to have Administrator permission to execute the command.
An example is when a user first invited bot on the server, members must not be able to the use the so called "permissions" command. However members with the moderator role should have access to it and execute the rest of the command.
Would anyone be able to help with this in my command?
It's still not clear what you want to reserve who you want to command to be avaliable to however, the has_permissions decorator allows you to set what permissions a user can use to access a command. This can be set within the parameters
For example, if you just only want a member with Administrator permissions to have access to your command, you can add #commands.has_permissions(administrator = True) after the command decorator. Heres an example,
#bot.command()
#commands.has_permissions(administrator = True)
async def permission(ctx):
await ctx.send('You have administrator access...')
More information can be found in Discord's documentation:
https://discordpy.readthedocs.io/en/latest/ext/commands/api.html
EDIT:
However, using an if statement within a command can be done with:
if ctx.author.guild_permissions.administrator:
...
While this question is aimed at discord.py - this question came up when I was searching for how to do this with it's sort-of successor library Discord Interactions as discord.py is quite limited - so I'll let people know how to do this with interactions too.
So for those wondering, this is how you check if the calling user is a Server Administrator on interactions:
import interactions
bot = interactions.Client(token=TOKEN)
#bot.command(scope=SERVER_IDS)
async def my_command(ctx):
perms = (await ctx.author.get_guild_permissions(ctx.guild_id))
if interactions.Permissions.ADMINISTRATOR in perms:
return await ctx.send("You are an admin")
await ctx.send("You are NOT an admin")
Additionally, here's a function snippet which can be used within your commands to quickly check if the caller is a server admin:
async def is_server_admin(ctx: Union[CommandContext, ComponentContext]) -> bool:
"""Returns :bool:`True` if the calling user is a Discord Server Administrator"""
perms = (await ctx.author.get_guild_permissions(ctx.guild_id))
return interactions.Permissions.ADMINISTRATOR in perms
Example usage of that function:
#bot.command(scope=SERVER_IDS)
async def my_command(ctx):
if await is_server_admin(ctx):
return await ctx.send("You are an admin")
await ctx.send("You are NOT an admin")

I want to use a Discord Role as requirement for a Command

Im coding a Discord bot in Python, and i want to make a Command which requires a Role. Is there a way to use a role as requirement in a if statement?
You just need to do
#client.command()
#commands.has_role(role='Role name')
async def command(ctx):
and yea
or if it doesn't work you need to put the #commands.has_role(role='Role name') above #client.command()
if you don't get it you can read the documentation
or you can do #commands.has_permission(kind of permission=True/False) for it as well role means the NEED the role and permission means they also need the permission but you need to have a discord role permission or else it would send it doesn't know the kind of permission
I've done exactly that in a bot I've created recently, I set up my commands as JSON configs with data including syntax, description, and allowed roles. The bot uses DiscordJS, but I'm sure you can use the same principles for a Python bot:
if (cmd.allowedRoles.includes('everyone') || message.member.roles.cache.some(role => cmd.allowedRoles.includes(role.name.toLowerCase())))
You can use
#client.command()
#commands.has_permissions(**permission needed**=True)
Which will only allow people with certain permissions to be allowed to do the command (Error messages option). Or if you want only people with a role you can use
if message.author.role.id == **role id**:
or
if ctx.message.author.role.id == **role id**:. Here is an example code:
#client.event
async def on_message(message):
link = ["https://"]
for word in link:
if message.content.count(word) > 0:
if message.author.role.id == 706694479847096381:
return
else:
print(f'{message.author}({message.author.id}) Sent an link')
await message.delete()
This code allows the bot to ignore people with that role when they send a link.
You can use argparse and create a required argument called role. Take a look at the documentation.

How can I add a role to a user with discord.py/my discord bot?

I've made a little discord bot in python to run it on my server, but i don't want to have randoms in my server so i created this command.
When executed the "code" (can also seen as password), the user get the user role (from the visitor role)
My code:
#client.command()
async def test(ctx):
user = ctx.message.author
await user.add_roles(discord.utils.get(user.guild.roles, id=...))
The error I get:
discord.ext.commands.errors.CommandInvokeError: Command raised an exception:
Forbidden: 403 Forbidden (error code: 50013): Missing Permissions
What I tried so far:
Give the bot all permissions
Elevate the bot at highest hierarchy.
So, could anyone please help me?
You can see the error code at The discord developer portal, Error Code 50013 Is You lack permissions to perform that action or the bot can't do the action you wanted it to do. More Info here.
Note: There's probably something wrong with your code.
I found a similar question to this one, that had some answers, so I tweaked the code to figure something out, try this, it worked for me:
#client.command()
async def test(ctx):
user= ctx.message.author
role = (discord.utils.get(user.guild.roles, id=insert id here))
await ctx.author.add_roles(role)
await ctx.send("Successfully assigned {}!".format(role.mention))
client.run('token for obvious reasons here')

Permission Check Discord.py Bot

I am working on a discord bot for basic moderation which does kick, ban and mute for now at least. But the problem is other members can use it too. I want only a few specified role who can use it.
Don't want to work on it depending on the #role either because the name of the roles across different servers aren't the same. Also wanting to keep the bot as simple as possible.
Now, I started out as this:
#client.command(name='ban')
async def mod_ban(member: discord.User):
try:
await client.ban(member, delete_message_days=0)
await client.say('**{0}** has been banned.'.format(str(member)))
except Exception as error:
await client.say(error)
But any member can use the commands then. So, tried follow this one = Permission System for Discord.py Bot and ended up with this:
#client.command(name='ban')
async def mod_ban(context, member: discord.User):
if context.message.author.server_premission.administrator:
try:
await client.ban(member, delete_message_days=0)
await client.say('**{0}** has been banned.'.format(str(member)))
except Exception as error:
await client.say(error)
else:
await client.say('Looks like you don\'t have the perm.')
Which lands me with this error: ;-;
raise MissingRequiredArgument('{0.name} is a required argument that is missing.'.format(param))
discord.ext.commands.errors.MissingRequiredArgument: member is a required argument that is missing.
Also, besides context.message.author.server_premission.administrator I don't only want the roles with Admin perm to use this command. I also want a few other roles with have few perms like manage message, manage roles etc. to use to command too.
Thanks in advance for the help! Also, sorry if I've missed anything stupid or silly ;-;
You aren't passing the context into the coroutine in your second example (and as #Andrei suggests, you can only ban members):
#client.command(name='ban', pass_context=True)
async def mod_ban(context, member: discord.Member):
...
Also, I should probably update my answer to that question. In the context of commands, you can use the very powerful checks built into discord.ext.commands to do a lot of this for you. has_permissions does exactly what you're looking for, validating that the user has any of the necessary permissions.
from discord.ext.commands import has_permissions, CheckFailure
#client.command(name='ban', pass_context=true)
#has_permissions(administrator=True, manage_messages=True, manage_roles=True)
async def mod_ban(ctx, member: discord.Member):
await client.ban(member, delete_message_days=0)
await client.say('**{0}** has been banned.'.format(str(member)))
#mod_ban.error
async def mod_ban_error(error, ctx):
if isinstance(error, CheckFailure):
await client.send_message(ctx.message.channel, "Looks like you don't have the perm.")
As far as I can see in the discord.py documentation discord.User is not the same as discord.Member.
Try to change
async def mod_ban(context, member: discord.User):
to
async def mod_ban(context, member: discord.Member):
If you are using discord.py rewrite, you can use checks (Discord.py rewrite checks)
Which (obviusly) checks for certain things suchs as roles or permissions on the command invoker
You can use both of this decorators, below your first decorator
#commands.has_role("rolename"/roleid)
#commands.has_any_role("rolename"/roleid,"rolename"/roleid,"rolename"/roleid ...)
Where rolename is a string containing the EXACT name of the role, letter by letter and space by space, and roleid is the id of the role, which, in case it's mentionable, you can get it by typing #rolename on any of your server chatrooms
Note how you must use the second decorator if you want to pass more than one role to check

Categories