Discord.py Give role if member customstatus contains hi - discord

Im working on a Loop that checks every 5 seconds if user Status contain "hi" if so it should give the user a role.
I dont really know how to do it, But maybe someone can help.
My code:
#Here is a little base for the Command i asked...
#I hope it can help you
#tasks.loop(seconds=15)
async def status_role():
if "hi" in ???.lower()
#here comes the code
#(Idk what the code is so i asked you guys :D)
member = ctx.message.author
role = get(member.server.roles, name="Friendly dude")
await bot.add_roles(member, role)

Looking at your new error, you're getting an IndexError because one of your members doesn't have an activity set, so activities[0] doesn't exist. Use an if-statement to check this. To see if a tuple is empty, you can just do if tuple (as empty lists/tuples are falsy), so the code below should solve that:
#tasks.loop(seconds=15)
async def status_role():
guild = client.get_guild(your guilds id)
role = get(guild.roles, name='Friendly dude')
for member in guild.members:
if member.activities and 'hi' in member.activities[0].name.lower():
await member.add_roles(role)
In addition to the fragment in the answer above.
EDIT: Apparently you didn't get my above ^ sentence saying it was meant to be combined with the other answer, so I've edited my answer & combined it myself. This should be the correct answer to your question.

Still same error
Unhandled exception in internal background task 'status_role'.
Traceback (most recent call last):
File "C:\Users\lequi\AppData\Local\Programs\Python\Python38-32\lib\site-packages\discord\ext\tasks\__init__.py", line 101, in _loop
await self.coro(*args, **kwargs)
File "C:\Users\lequi\Desktop\Programs\Clipox\ClipoxMain\main.py", line 136, in status_role
[await member.add_roles(role) for member in guild.members if 'hi' in member.activities[0].name.lower()]
File "C:\Users\lequi\Desktop\Programs\Clipox\ClipoxMain\main.py", line 136, in <listcomp>
[await member.add_roles(role) for member in guild.members if 'hi' in member.activities[0].name.lower()]
IndexError: tuple index out of range

You can use member.activities. This will return you a list of member's activities. As far as I know, you can get custom status with member.avtivities[0].name. This'll return you a string of member's custom activity.
Also, you can't use ctx.message.author. You need to iterate through members in the guild to check every one of their's activity.
So you can simply do:
#tasks.loop(seconds=15)
async def status_role():
guild = client.get_guild(your guilds id)
role = get(guild.roles, name='Friendly dude')
[await member.add_roles(role) for member in guild.members if 'hi' in member.activities[0].name.lower()]
EDIT
There're some updates about Intents in the discord.py 1.5.x. You need to define it before defining client = discord.Bot(prefix='') to get guilds, channels etc.
import discord
intents = discord.Intents().all()
client = discord.Bot(prefix='', intents=intents)
EDIT 2
If member has no status, it'll return an empty tuple, that's why you get IndexError. To prevent this, you can add a simple if block to your code.
async def status_role():
guild = client.get_guild(your guilds id)
role = get(guild.roles, name='Friendly dude')
[await member.add_roles(role) for member in guild.members if member.activities[0] and 'hi' in member.activities[0].name.lower()]

Related

AttributeError: 'NoneType' object has no attribute 'roles' Discord Self-Bot

Am using discord.py library to create a Discord self-bot that reads through a .txt file I already have filled with Discord IDs and checks their role in a guild server, if the role matches the role am looking for then their ID is saved in a new notepad called 'roletrue.txt'. I have been stuck for quite some time now, any help is appreciated! I know it is against Terms of Services however I still do it.
Code:
import discord
client = discord.Client()
#client.event
async def on_ready():
# Read the file containing the Discord IDs
with open("ids.txt", "r") as f:
ids = f.read().split("\n")
# Get the guild and the role
guild = client.get_guild('GUILD_ID')
role = discord.utils.get(guild.roles, name="ROLE_NAME")
# Iterate through the IDs
for id in ids:
# Get the member from the guild
member = await guild.fetch_member(int(id))
if role in member.roles:
with open("roletrue.txt", "a") as f:
f.write(f"{id}\n")
print(f"Discord ID {id} has the role {role.name}.")
else:
print(f"Discord ID {id} does not have the role {role.name}.")
client.run("TOKEN", bot = False)
Error:
File "C:\Users\spath\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "c:\Users\spath\OneDrive\Υπολογιστής\NFT Vict Scrape\checkroles.py", line 13, in on_ready
role = discord.utils.get(guild.roles, name="Customer")
^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'roles'
I tried rewriting the code from scratch and check discord library and searched online and couldn't find a answer for my problem.
Change
guild = client.get_guild("GUILD_ID")
to
guild = await client.fetch_guild("GUILD_ID")
Get only returns the guild object if it's cached - if it's returning None then it's not so use fetch instead.
You will have to use await in order to fetch guild id and the roles from it,
try using this:
guild = await client.fetch_guild("GUILD_ID")
or if it doesn't work, try using discord utils.

If I want to remove an ID-specified role within a function, how can I do that?

If I want to remove an ID-specified role within a function, how can I do that?
#bot.command()
async def aa(ctx,member:discord.Member):
role_muted = discord.utils.get(ctx.guild.roles, name='test01')
await member.remove_roles(role_muted)
What I want is to specify an ID in a function like this.
#bot.command()
async def aa(ctx):
member = 414087251992117211 #ID User
role_muted = discord.utils.get(ctx.guild.roles, name='test01')
await member.remove_roles(role_muted)
how can i do it The reason I have to do this is because I will continue to develop such as When you receive this Role within 3 days if not online it will remove your Role.
Use Guild.get_member(id) to get a member by their ID. You can get the Guild either from the ctx if you want it to run in the guild where you call the command, or from a Guild ID that you store somewhere using Bot.get_guild(id).
If you want to pass the ID in as an argument to the function and have it convert to a member automatically, use a Converter.
Can you give an example? I tried to do as you said. but an error occurred
#bot.command()
async def aa(ctx):
member = discord.Guild.get_member(414087251992117211)
role_muted = discord.utils.get(ctx.guild.roles, name='test01')
await member.remove_roles(role_muted)
Error
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'NoneType' object has no attribute 'remove_roles'
#UpFeR Pattamin, please comment on a reply instead of posting a new one. You're getting this error because you're not specifying which guild you are talking about. If you want to get the one that the command was executed in, I'm pretty sure you need to do ctx.guild.get_member().

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)

#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

Discord Py - How can I add_role to user on_message?

I want to use regex to look at and accept literally ANY incoming message from a user and assign them a role. But ONLY if that user does not have a role already assigned to them. I don't get any errors when running the code, but it does not work.
Here is what I have:
#client.event
async def on_message(message):
match = re.search(r'(.*?)', message.content)
member = message.author
role = discord.utils.get(member.guild.roles, name="Creators")
if message.author == client.user:
return
if role not in member.roles:
if match and message.channel.id == target_channel:
# add member to role
# send message to to users
await message.channel.send(
f'Hi {message.author}, welcome to the server! Don\'t forget to choose your #roles'
)
await discord.Member.add_roles(member, role)
You wrote:
await discord.Member.add_roles(member, role)
https://discordpy.readthedocs.io/en/latest/api.html?highlight=add_role#discord.Member.add_roles
As you can see from the documentation the passed arguments is *roles, reason=None, atomic=True. roles is something you have to specify, reason and atomic are optional.
You tried passing member which is not a valid argument.
discord.Member is a class. You need to get an instance of that class. ctx.author is an instance of discord.Member.
So the final call should be:
await ctx.author.add_roles(role)
To get the Member object from on_message, you can use member = message.author.
Then simply do member.add_roles(role).
Thanks for the help guys, it turns out it was something very minor (and dumb on my part) that prevented it from working.
when comparing message.channel.id.id to target_channel, I forgot that the channel id object does not come in as a string, but target_channel was set up as a string, so it failed to see them as equal. Simply converting message.channel.id to string did the trick.

Resources