I am trying to create a command that can ban people on the Server and those who are not on the server. But there is a problem: If I use async def ban(ctx, member: discord.Member, *, reason): to use the "roles" attribute, then it will not be able to ban those who are not on the server. If i use async def ban(ctx, member: discord.User, *, reason): i will not be able to use the "roles" attribute because it is not an attribute.
Why am I doing this? I am trying to make it so that Server Moderators cannot ban those who have Moderator roles
Code:
async def ban(ctx, member: discord.User = None, *, reason=None):
if get(ctx.author.roles, id=866038959980544050) or get(ctx.author.roles, id=866436325503008768) or get(
ctx.author.roles, id=866441730631008316) or ctx.author.guild_permissions.administrator: # Check if the author has the Role "Moderator", "Admin", etc.
if get(member.roles, id=867371024466968596) or get(member.roles, id=867132420489871411) and not ctx.author.guild_permissions.administrator: # Check if the member has the Role "Moderator", "Admin", etc.
await ctx.send("You cannot ban this user.")
Assuming that you use discord.ext.commands, you could use a typing.Union converter to ensure that the argument is either a user or a member.
import typing
[...]
async def ban(ctx, user: typing.Union[discord.Member, discord.User]):
Note that order matters: By having discord.Member first, it tries to apply this converter first before trying discord.User, thus prioritising conversion to a member over conversion to a user (which should be what you want).
To distinguish between the two cases, you can now check whether user is of type discord.Member and perform your checks on it.
if type(user) is discord.Member:
if YOUR_CHECKS:
await ctx.send('You cannot ban this user')
return
# Perform ban
await ctx.send('Ban successful')
When testing, I discovered that the command library will give you a numerical user id instead of a real user and shows
Failed fetching discord object! Passing ID instead. as a warning. If you need more than just the user id, you could convert it to a User object using
if type(user) is int:
user = await bot.fetch_user(user)
if user is None:
await ctx.send('User not found')
return
Note that fetch_user is an API call and you might want to avoid it. See the docs for more info.
One way of doing this would be to check if the given member is in the current guild. If this is True, you can create a new member object with their id or with another prefered method, otherwise you can assume that they do not have the Moderator role. For simplicity, I only included checking for a single role, but you can tweak this in the way you want to do it. Other than that, everything else is explained in further detail in the code below.
if member in ctx.guild.members: # checks if the provided member is in the current server
member = ctx.guild.get_member(member.id) # Get the member object of the user
if get(member.roles, id=866038959980544050): # Check if this role is in the member's roles
await ctx.send("This member has <#&866038959980544050>")
else:
await ctx.send("This member does not have <#&866038959980544050>")
return # returns, since member was in the server
# This happens if the provided member is not in the current server
await ctx.send("This member, who is not in this server, does not have <#&866038959980544050>")
You must use this
#bot.command(...)
#commands.has_any_role(role id)
...
Related
I have a ban command which uses discord.User cog as to ban members who are not in the guild, but I want to add role hierarchy but when I checked the docs there was no attribute for it, is there a way to do both? Have role hierarchy and be able to ban users who are not in the guild
You can iterate through the roles of the user and check if he has a role in a list. I prefer to use the role IDs since it can't be changed.
Use typing.Union to either get a member, or the id if the user is not in the server
from typing import Union
#client.command()
#commands.bot_has_permissions(ban_members=True)
#commands.has_permissions(ban_members=True)
async def ban(ctx, user: Union[discord.Member, int]):
if isinstance(user, int):
# user is not in guild -> ban him by id
pass
else:
whitelisted_roles = [123456, 456789, 789012] # List of Mod roles
for role in user.roles:
if role.id in whitelisted_roles:
return await ctx.send("You can't ban this user! He is a moderator!")
else:
pass
# ban the member
Welcome to StackOwerflow!
Yes, it's possible to check role hierarchy as well as ban users with their user_ids.
I recommend taking a looking into the answer by Guddi.
First and formost:
You need a check role heirarchy.
To do that, here's the code:
if not (user.guild_permissions.administrator and user.top_role.position > ctx.me.top_role.position and user.top_role.position > ctx.author.top_role.position):
user.ban()
To ban the user who is not in the guild,
if isinstance(user, int):
user = await bot.fetch_user(user)
await ctx.guild.ban(user)
I hope this is helpful.
I am trying to make an id converter that converts any id to name.
#bot.command()
async def idconv(ctx, *, arg):
id=int(arg)
conv=discord.Object(id)
await ctx.send(conv.name)
It appears as though you are trying to get a users name from their ID.
discord.py offers a class called Member that does it for you
import discord
#bot.command()
async def idconv(ctx, member: discord.Member):
await ctx.send(member.name)
While creating a discord.Object with an id is useful for banning\unbanning a user who doesn't share a guild with your bot, it isn't useful for your case.
Here is the documentation for discord.Member.
Here the command idconv accepts arguments like user-id, name, nickname and tries to convert it into a Member object.
There might be instances where the ID of the member you provided may not share a server with the bot, and hence member ends up being None , resulting in any operation with member raising an error.
You can account for that by checking it beforehand
if member: #or if member is not None
await ctx.send(member.name)
else:
await ctx.send("Could not find a user with that ID")
If you use bot.fetch_user, you
can fetch any discord user by their ID, if found it returns an User object,
which is similar to Member but has overall lesser information.
I'm trying to make a command that kicks all the members with a certain role. It's not going very well - this is my current code:
import discord
from discord.ext import commands
from discord.ext.commands import has_permissions
client = commands.Bot(command_prefix = "k!")
#client.command(pass_context=True)
#has_permissions(administrator=True)
async def kickall(ctx):
role_id = 754061046704242799
for role_id in roles: #roles undefined lol
try:
await member.kick()
except:
continue
You can iterate through all members, and check if they have the specified role. If they do, you can kick them.
#client.command(pass_context=True)
#has_permissions(administrator=True)
async def kick(ctx, role: discord.Role, reason: str=None):
for member in ctx.guild.members:
if role in member.roles: # does member have the specified role?
await ctx.guild.kick(member, reason=reason)
The usage would be k! kick <role> <reason(optional)>
I'd recommend looking at the documentation for more details.
The parameters (ctx, role: discord.Role, reason: str=None) are as follows:
ctx - is passed in by default. This contains all the context information such as message author, server the message was sent in etc
role: discord.Role - the user must specify a role to kick when calling the command. The colon tells python and discord.py the type to try and convert the parameter to, meaning that it will convert a role name (i.e. a string) into a discord.Role object, so you can do operations on it such as comparing roles.
reason: str=None - an optional parameter (default is None). If this is provided, the users kicked will see this string as the reason message for being kicked.
Using the following lines of code:
#bot.command(pass_context=True)
async def ban_role(ctx, *,role_name):
role = discord.utils.get(ctx.message.server.roles, name=role_name)
if role:
try:
await ctx.delete_role(ctx.message.server, role)
await ctx.send("The role {} has been deleted!".format(role.name))
except discord.Forbidden:
await ctx.send("Missing Permissions to delete this role!")
else:
await ctx.send("The role doesn't exist!")
I get the error:
You're using outdated syntax from v0.16 of discord.py that isn't supported anymore.
See the migration guide for v1, specifically how Server is now Guild.
You can also just get the guild directly from the Context object's guild attribute, instead of going through its message attribute.
Aditionally, unless you've overridden your Context, it's not going to have a delete_role method.
You'll want to use the Role.delete method instead.
For help with markdown, see https://stackoverflow.com/editing-help.
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