A specific role only reaction - discord

I'm trying to get it so a specific role can react to the message to be able to ban the user, I have made the ability to be able to react to the message, and the author of the command can ban the user, but I want to make it so another role can do it as well
Here is what I have currently
def check(rctn, user):
return user.id == ctx.author.id and str(rctn) == '<:tick:837398197931868190>'
reaction, user = await bot.wait_for('reaction_add', check=check)

You can use #commands.has_permissions
For example you could make it:
#bot.command()
#commands.has_permissions(ban_members=True)
# rest of your code
This way only roles with the permissions to ban_members can use the command

Related

How to restrict a certain role from using a command (discord.py)

I've already seen the variant where you allow a certain role to use a command.
However, I'm trying to achieve the exact opposite: How to disallow a certain role from using a command.
I have searched around stack overflow and found no answer, nor did I found an answer on the official discord.py documentation.
Any sort of help is appreciated.
author.roles returns a list of discord.Role so just check if the role you specify is contained in that list, and if so, exit the command early.
Using Role Id (Preferred)
#bot.command()
async def command_without_specific_role(ctx):
if role_id in [role.id for role in ctx.author.roles]:
return
...
Using Role Name
#bot.command()
async def command_without_specific_role(ctx):
if role_name in [role.name for role in ctx.author.roles]:
return
...
Check inside command
#bot.command()
async def hello(ctx: commands.Context):
blacklisted_role = ctx.guild.get_role(ID)
if not any(role == blacklisted_role for role in ctx.author.roles):
await ctx.send("world!")
Own Decorator
A more elegant way is to create your own decorator. It's more like the opposite of has_any_role.
from discord.ext import commands
def has_not_any_role(*roles):
async def extended_check(ctx):
return not any(role.id in roles for role in ctx.author.roles)
return commands.check(extended_check)
#bot.command()
#has_not_any_role(492212595072434186)
async def hello(ctx: commands.Context):
await ctx.send("world!")
If extended_check returns True the message will be sent. If it return False it throws a discord.ext.commands.errors.CheckFailure error which then can be catched in on_command_error.
References:
Member.roles
commands.check

Get the role(s) of a member in discord.py

I am trying to make a command that shows the roles of the mentions user.
This command is a test command which I am going to implement into my mute command. (the command will remove the member's current role and add the mute role)
This is what I have:
#client.command()
async def roles(ctx, member: discord.Member):
roles = member.roles
role_names = [role.name for role in roles]
await ctx.send(role_names)
The command works fine, but the output isn't quite what I expected.
Output:
['#everyone', 'Member']
It correctly displays the 2 roles mentioned by the user, but it isn't formatted in the specific way I want.
I want the output to simply be "Member" or whatever other roles the mentioned member has besides #everyone. basically, I want to remove the square brackets and "#everyone" from the output, leaving only the role name.
Hopefully, somebody can help me with this.
Thanks!
To fix your problem you just have to format the information from role_names in string form and remove the '#everyone'. You can use a list comprehension and the join() method .
Here would be the amended code:
#client.command()
async def roles(ctx, member: discord.Member):
roles = member.roles
role_names = ' '.join([role.name for role in roles if role.name != '#everyone'])
await ctx.send(role_names)

discord.py bot detecting username changes

trying to make a small bot for my server. it kinda works but if the user removes the nickname, the bot returns a None value. how can i fix this?
the goal is to have a bot detect if a user has TBU in the nickname or username, if it does, add him to a role in discord (havnt added this yet, need to look into it) but if the change removes TBU in the name, remove the role.
#client.event
async def on_member_update(before, after):
if before.nick != after.nick: # to only run on status
embed = discord.Embed(title=f"Changed nick")
embed.add_field(name='User', value=before.mention)
embed.add_field(name='Before', value=before.nick)
embed.add_field(name='After', value=after.nick)
# send to admin or channel you choose
channel = client.get_channel(526517582963146762) # notification channel
await channel.send(embed=embed)
admin = client.get_user(174749290902716417) # admin to notify
await admin.send(embed=embed)
if "TBU" in after.nick:
admin = client.get_user(174749290902716417) # admin to notify
await admin.send(embed=embed)
if "TBU" in before.nick and not "TBU" in after.nick:
admin = client.get_user(174749290902716417) # admin to notify
await admin.send(embed=embed)
When a user has no nickname nick returns None.
Change if before.nick != after.nick to if before.nick != after.nick and after.nick is not None, this should work.

How can I allow people to only kick members lower than their role with discord.py?

I'm trying to make a kick command using Python and the discord.py library, though I cannot figure out how to allow people to only be able to kick members lower than their role, so Moderators wouldn't be able to kick Admins.
enter image description here
You can compare the Member object with a function called top_role
An example code would be:
#client.command()
#commands.has_permissions(kick_members=True)
async def kick(ctx, member: discord.Member, *, reason=None):
if member.top_role >= ctx.author.top_role: # Check if the role is below the authors role
await ctx.send("You can only kick users with a lower role!")
return
else:
await member.kick(reason=reason)
await ctx.send(f"{member.id} got kicked with the reason: {reason}.")
Add all the moderators to a list and regular users to another list.
Like this;
string[] moderator = all moderators.
string[] regularUsers = all regular users.

#commands.has_permissions(administrator=True) not working properly?

I am trying to make a simple moderator bot.
the code looks something like this.
import discord
from discord.ext import commands
from random import choice
import os
client = commands.Bot(command_prefix=commands.when_mentioned_or("%"), description='A simple Moderator bot')
def colour():
l = [ 1752220, 3066993, 3447003, 10181046, 15844367, 15105570, 15158332, 3426654, 16580705 ]
return choice(l)
#client.event
async def on_ready():
change_status.start()
print("The Bot is online!")
#client.command()
#commands.has_permissions(administrator=True)
async def kick(ctx, user : discord.Member = None, *,reason = "No reason provided"):
await user.kick(reason = reason)
await ctx.send("Kicked the user.")
#client.command()
#commands.has_permissions(administrator=True)
async def ban(ctx, user : discord.Member = None, *,reason = "No reason provided"):
await user.ban(reason = reason)
await ctx.send("Banned the user")
#client.command()
#commands.has_permissions(administrator=True)
async def warn(ctx, user : discord.Member = None, *,reason = "No reason provided"):
await user.send(f"You have been **Warned** by **{ctx.author.name}** in the **{ctx.guild.name}** for the reason: **{reason}** ")
await ctx.send("Warned the user")
client.run(os.environ['token'])
In this only warn command is working successfully rest all the commands throws an error which looks like this.
Ignoring exception in command kick:
Traceback (most recent call last):
File "main.py", line 140, in kick
await user.kick(reason = reason)
File "/app/.heroku/python/lib/python3.6/site-packages/discord/member.py", line 524, in kick
await self.guild.kick(self, reason=reason)
File "/app/.heroku/python/lib/python3.6/site-packages/discord/guild.py", line 1886, in kick
await self._state.http.kick(user.id, self.id, reason=reason)
File "/app/.heroku/python/lib/python3.6/site-packages/discord/http.py", line 241, in request
raise Forbidden(r, data)
discord.errors.Forbidden: 403 Forbidden (error code: 50013): Missing Permissions
It is throwing a missing permission but I am the owner of the server and even added the bot as an admistrator.
Even tried by making an administrator role and giving all the permissions to it but still no luck.
This error is coming again and again.
Even if anyone know how to check between multiple roles like if anyone has administrator or kick member permission then do this, please let me know how to do that.
Thanks in advance for the help.
If any more information needed, do let me know.
The error you are getting on the last line from your console; discord.errors.Forbidden: 403 Forbidden (error code: 50013): Missing Permissions Is because the bot is missing permissions to perform these actions against the given permissions the bot currently has.
This could occur from the following;
Sufficient Bot permissions haven't been enabled from the invite link
The member has a higher or equal role than the bot
The #everyone role has no permissions
Moreover, these issues aren't at fault with the code you have written and is something that can be fixed in your server with the role hierarchy or bot invite link permissions.
I would also recommend comparing roles to make sure users can't attempt moderation on a higher level member in the first place, this could also help to mitigate the problem
With this, you can use >= and the top_role attribute which would compare the roles that the user compared to the member you are attempting to take moderation on, if your permissions are lower than the member you are attempting to moderate, it will prevent the rest of the code running. Here is a simple way of doing this,
if member.top_role >= ctx.author.top_role:
await ctx.send(f"You can only moderate members below your role")
return

Resources