Check if user has Admin -- Discord.py - python

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")

Related

How can I exclude certain users from a command?

I have a command where there is an asyncio event. Unfortunately this is lost when the bot restarts. Therefore I want to be able to exclude certain users from a command, respectively the bot should not react if they are entered in the list.
My attempt so far has been the following:
#commands.command
async def test(self, ctx):
#Shortened
user = discord.Member
userlist = [HereWePutTheUserIDs]
if user in userlist:
return await ctx.send("You are on the blocklist.")
Unfortunately, this did nothing. Is there anyway to prevent certain users, by ID/name, from executing the command until I remove them from the list?
You're assigning the user to a class, you don't even instantiate it, that's not how it works, the user variable should be ctx.author.id
user = ctx.author.id

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.

Confused on this Discord.py Rewrite + Reaction Light Code - Need Explanation

So while we're all I'm surely aware of the entire copy and paste someone elses code, and magically it works, it looses some context and understanding when figuring out how this code actually works and functions.
I'm working with the Discord.py Rewrite, and a chunk of code called Reaction Light in order to create a bot that allows for self-assigning roles in my Discord Server. The bot is 100% functional and works as intended. Now, I have changed some things around from their code, so my methods are in different locations and are called from different areas.
Here's where I'm confused:
I have a method called isadmin() this is called whenever there needs to be a check completed to figure out if the user issuing the command is an admin. Admin roles are defined in the .env file which I'm retrieving using the dotenv module. Pretty straight forward stuff. (I'll rewrite that portion of it later and hopefully put all the admin role id's into a file and grab them from there.) However I'm confused on what exactly the third argument of this method does. msg=False
Whenever I wrote my cogs for this I'm calling this method as such without passing that 'msg' argument to it as illustrated below:
admin.py
# admin.py
# Simple ping pong command
#commands.command()
async def ping(self, ctx):
if helpers.isadmin(ctx):
print("Running Command from admin.py")
await ctx.send('Pong!')
Now in my on_message() listener method, it is passing the msg argument, then executing some code unrelated to the ping command but related to the functionality of the self-assigning role portion of the bot.
message.py
# message.py
#commands.Cog.listener()
async def on_message(self, message):
if helpers.isadmin(message, msg=True):
# Execute some code here for the self-assigning roles
The way this workflow works is this as far as I know, and I'll be using the ping command as an example which is called by the r.ping command.
on_message() listens for all messages being sent in the server
Ping Command Issued In Channel By User
on_message() hears the message & checks if the user is an admin, but also passes the msg argument
The ping command is called from admin.py and then checks (again?) if the user is an admin, and if he/she is, follows through with the command.
So, I'm trying to figure out when or when not to use this msg argument, and if it's already checking to see if the user is an admin in the listener do I have to check again in the actual command itself? Isn't that a bit redundant?
Here's the isadmin() method in the helpers.py file
# helpers.py
def isadmin(self, ctx, msg=False):
# Checks if command author has one of .env admin role IDs
try:
check = (
[role.id for role in ctx.author.roles]
if msg
else [role.id for role in ctx.message.author.roles]
)
if self.admin_a in check or self.admin_b in check or self.admin_c in check:
return True
return False
except AttributeError:
# Error raised from 'fake' users, such as webhooks
return False
To be entirely honest I'm not sure why that's there. If you have access to ctx.author you have access to ctx.message as ctx.author is simply the author of that message, seems redundent. However I strongly suggest you use checks for this. For example I have:
def is_owner():
def predicate(ctx):
return ctx.author.id in ctx.bot.config()["owners"]
return commands.check(predicate)
and I use this as a decorator
# utils/checks/checks.py
from utils.checks import checks
#checks.is_owner()
#commands.group(hidden=True, case_insensitive=True, description="Load a module")
async def load(self, ctx):
if not ctx.invoked_subcommand:
return await ctx.send_help(ctx.command)
For example you could have
def is_admin():
def predicate(ctx):
role_id = 123123123123123 # replace this with os.getenv("wherever your admin role is")
return role_id in [x.id for x in ctx.author.roles]
return commands.check(predicate)
It's just much cleaner and much easier to use/understand in my opinion.

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

Permission System for Discord.py Bot

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.

Categories