Remove user from list when reaction_remove - python-3.x

I have a function to store all the users that have responded in a string and output them.
Now I have the problem that if User1 gives a reaction and deletes it again, then User1 is still in the list.
# hatch <type> <time>
now = datetime.now()
add_time = now + timedelta(minutes=int(args[2]))
cur = add_time.strftime('%H:%M')
embed = discord.Embed(title='Raid meldung von ' + str(message.author.display_name), color=discord.Color.green())
embed.add_field(name='Raid Typ: ', value=args[1], inline=True)
embed.add_field(name="Schluepft um: ", value=cur + "", inline=True)
embed.add_field(name='Interesse: ', value='Keine Zusagen', inline=False)
channel = bot.get_channel(818662081531740163)
mess = await channel.send(embed=embed)
re_time = int(float(args[2])) * 60
emoji_list = ['✅', '❌']
for i in emoji_list:
await mess.add_reaction(i)
while True:
users = ""
try:
reaction, user= await bot.wait_for("reaction_add", timeout=re_time)
if str(reaction) == '✅':
mess = await channel.fetch_message(mess.id)
reaction_list = mess.reactions
react = get(mess.reactions, emoji='✅')
for reactions in reaction_list:
if str(reactions) == "✅":
if react and react.count <= 2:
user_list = [user async for user in reactions.users() if user != bot.user]
for user in user_list:
users = users + user.mention + "\n"
else:
users = users + user.mention + "\n"
embed1 = discord.Embed(title='Raid meldung von ' + str(message.author.display_name), color=discord.Color.green())
embed1.add_field(name='Raid Typ: ', value=args[1], inline=True)
embed1.add_field(name="Schluepft um: ", value=cur + "", inline=True)
embed1.add_field(name='Interesse: ', value=users, inline=False)
await mess.edit(embed = embed1)
except asyncio.TimeoutError:
break
await asyncio.sleep(re_time)
embed_end = discord.Embed(title='Raid meldung von ' + str(message.author.display_name), color=discord.Color.red())
embed_end.add_field(name='Raid Typ: ', value=args[1], inline=True)
embed_end.add_field(name="Schluepft um: ", value="Beendet", inline=True)
await mess.edit(embed = embed_end)
for i in emoji_list:
await mess.clear_reactions()
i hope anyone have a solotion for my proplem

You are only updating the list of users and updating the message whenever there's a ✅ reaction added to the message.
You need to either have logics for both discord.on_reaction_add and discord.on_reaction_remove events,
or you can just update the reaction list at an interval.
If you update the list of users at an interval, you will have an updated user list until your desired timeout.
msg = await channel.send(embed=embed)
while True:
reactions = msg.reactions
for react in reactions:
if str(react) == "✅":
users = [user async for user in reactions.users() if user != bot.user]
break # if you don't care about other reactions
# do stuff with your users
async.sleep(1) # or something else you see fit
Currently, there's a lot of redundancy in your code. Because I don't know the whole context or if some of them are intended or not, so I'll just stick with pointing out the absolute redundancies.
mess = await channel.fetch_message(mess.id)
is not needed since you have the message object above. You can keep referencing it as long as the async method is still active.
if str(reaction) == '✅':
mess = await channel.fetch_message(mess.id)
reaction_list = mess.reactions
react = get(mess.reactions, emoji='✅')
for reactions in reaction_list:
if str(reactions) == "✅":
if react and react.count <= 2:
You already have the ✅ Reaction object. You can play with this to fetch the users; no need to grab the list again, and definitely no need to iterate through the reaction list again.
I'm inclined to suggest you use the discord.on_reaction_add and discord.on_reaction_remove events.
It will be complicated because you have to keep juggling multiple events, but it will do what you want it to do efficiently.

Related

How do I fix my discord bot breaking when waiting for multiple `wait_for(reaction_add)` is called?

I have a verification process on my server that takes the inputs of the new member (First name and section and whatever) then sends it to a channel in a server where admins click on an emote to accept/deny the person. The problem is, once 2+ people open tickets, it basically becomes unusable.
To make me sane while writing it, I'll make a scenario.
x, y join the server.
Both send verification requests.
Admin accepts request for x.
Instead of x being approved, x and y gets approved.
This is really bugging me as I might as well do it without the wait_for() checks.
Code:
#commands.Cog.listener()
async def on_member_join(self, member: discord.Member):
channel = member.guild.get_channel(734637251681583164)
verichannel = member.guild.get_channel(849593765097373696)
embed = discord.Embed(
title=f"Welcome {member.name}, keep in mind that this is NOT an [woah privacy] server!",
description=f"This is not a family-friendly server, so please leave if you're not comfortable with it. \n Make sure to read <#734639183737389060> & <#750190162000216215> before proceeding! \n To get access to our server, please verify at <#848430448223977512>.",
color=0x2ecc71
)
embed.set_thumbnail(url=member.avatar_url)
await channel.send(member.mention, embed=embed)
# DM verification
def check(m):
return m.guild == None and m.author == member
awaitingverification = discord.Embed(title="Verification Request", description=f"User: {member.mention}", color = 0x01c618)
embed= discord.Embed(title="What is this?",
description="This is a verification program for the unofficial [woah privacy] server. We do this to ensure the safety and privacy of our members. We are going to ask information that will be sent to our admins for further processing.",
color=0x00d118)
embed.set_author(name="[woah privacy] Verification program")
embed.add_field(name="1st Question: What is your first name?", value="Example: Jordan",
inline=False)
await member.send(embed=embed)
try:
first_name = await self.bot.wait_for('message',timeout=30, check=check)
except asyncio.TimeoutError:
await member.send("Timed out. Re-Join.")
return
awaitingverification.add_field(name="First Name", value=first_name.content, inline=True)
embed = discord.Embed(title="Verification: 2nd step",
description="This is the second step of the verification process.", color=0x00d118)
embed.set_author(name="[woah privacy] Verification program")
embed.add_field(name="2nd Question: What is your section? If you are not enrolled, reply 'Visitor'.", value="Example: LS208", inline=False)
await member.send(embed=embed)
try:
section = await self.bot.wait_for('message',timeout=30, check=check)
except asyncio.TimeoutError:
await member.send("Timed out. Re-Join.")
return
awaitingverification.add_field(name="Section", value=section.content, inline=True)
embed = discord.Embed(title="Verification: 3rd step",
description="This is the third step of the verification process.", color=0x00d118)
embed.set_author(name="[woah privacy] Verification program")
embed.add_field(name="3rd Question: Do you agree with all our rules? Reply with 'Yes' if you do.",
value="Below is a skimmed version of our full rules, so be sure to read them after.",
inline=False)
embed.add_field(name="This is not an official [woah privacy] server.", value="If there is one, then... we dont care.",
inline=False)
embed.add_field(name="Names in the server are your real first names.", value="If not: kicked out the door.",
inline=False)
embed.add_field(name="This is not a family friendly server.",
value="It's a mess here sometimes but thats what's good about it.", inline=False)
embed.add_field(name="Don't be an asshole.", value="We wont hesitate to swing that ban hammer.", inline=False)
embed.add_field(name="No loopholes.", value="Because.. Well... It's bad.", inline=False)
embed.add_field(name="Read the Discord ToS (Terms of Service)", value="TL:DR: Must be 13+ to use discord.",
inline=False)
await member.send(embed=embed)
try:
rulestatus = await self.bot.wait_for('message',timeout=30, check=check)
except asyncio.TimeoutError:
await member.send("Timed out. Re-Join.")
return
awaitingverification.add_field(name="Rules", value=rulestatus.content, inline=True)
awaitingverification.set_footer(text="🟩: Student, 🟨: Visitor, 🟥: Deny and Kick")
await member.send("Thank you. Your application will be processed in due time.")
verification = await verichannel.send(embed=awaitingverification)
await verification.add_reaction("🟩")
await verification.add_reaction("🟨")
await verification.add_reaction("🟥")
def check(reaction, user):
return str(reaction.emoji) in ["🟩", "🟨", "🟥"] and user != self.bot.user
reaction, user = await self.bot.wait_for("reaction_add", check=check)
if str(reaction.emoji) == "🟩":
embed = discord.Embed(title="Verification Request", description=f"User: {member.mention}")
embed.add_field(name="Status:", value=f"Approved by {user}: Student", inline=False)
await verification.edit(embed=embed)
await verification.clear_reactions()
role1 = discord.utils.get(member.guild.roles, name="Students")
await member.add_roles(role1)
if discord.utils.get(member.guild.roles, name=f"Section: {section.content}"):
sectionrole = discord.utils.get(member.guild.roles, name=f"Section: {section.content}")
await member.add_roles(sectionrole)
else:
await member.guild.create_role(name=f"Section: {section.content}")
sectionrole = discord.utils.get(member.guild.roles, name=f"Section: {section.content}")
await member.add_roles(sectionrole)
await member.send("Your Verification Request has been granted as Student. Have a good time and check the rules!")
return
elif str(reaction.emoji) == "🟨":
embed = discord.Embed(title="Verification Request", description=f"User: {member.mention}")
embed.add_field(name="Status:", value=f"Approved by {user}: Visitor", inline=False)
await verification.edit(embed=embed)
await verification.clear_reactions()
role2 = discord.utils.get(member.guild.roles, name="Visitors")
await member.add_roles(role2)
await member.send("Your Verification Request has been granted as Visitor. Have a good time and check the rules!")
return
elif str(reaction.emoji) == "🟥":
embed = discord.Embed(title="Verification Request", description=f"User: {member.mention}")
embed.add_field(name="Status:", value=f"Denied by {user}", inline=False)
await verification.edit(embed=embed)
await verification.clear_reactions()
await member.send("Your Verification Request has been denied.")
await member.kick()
return
p.s sorry, I didn't skim any of the code, I wanted to share it as is because maybe it's my spaghetti's fault.
To solve this, check if it's the same message in check:
verification = await verichannel.send(embed=awaitingverification)
await verification.add_reaction("🟩")
await verification.add_reaction("🟨")
await verification.add_reaction("🟥")
def check(reaction, user):
return str(reaction.emoji) in ["🟩", "🟨", "🟥"] and user != self.bot.user and reaction.message == verification
reaction, user = await self.bot.wait_for("reaction_add", check=check)
References:
reaction.message

Discord py mute command specifying reason without time

So I made a mute command and converted it to an optional tempmute yesterday, it worked fine except for when I specified a reason and no time. Obviously that would create a problem but sadly I could not figure out how to fix it. i.e: N?mute user time reason works but N?mute user reason doesn't. If I don't specify the reason nor time it mutes them for 2 years. I want to be able to perm mute people with a reason and also temp mute people with a reason (already works). Hereby the code:
#commands.command()
#commands.has_permissions(manage_roles=True)
async def mute(self, ctx, member: discord.Member, time="670d", *, reason="no reason provided"):
role = discord.utils.get(ctx.guild.roles, name="Muted")
admin = discord.utils.get(ctx.guild.roles, name="Admin")
mod = discord.utils.get(ctx.guild.roles, name="Moderator")
guild = ctx.guild
pfp = member.avatar_url
time_convert = {"s":1, "m":60, "h":3600,"d":86400}
tempmute= int(time[0]) * time_convert[time[-1]]
if admin in member.roles or mod in member.roles:
embed = discord.Embed(title="Mute failed", description="Staff cannot be muted.", color=0xff4654)
await ctx.channel.send(embed=embed)
return
if role in member.roles:
embed = discord.Embed(title="Mute failed", description="**{0}** is already muted!".format(member), color=0xff4654)
await ctx.channel.send(embed=embed)
if role not in guild.roles:
perms = discord.Permissions(send_messages=False, speak=False)
await guild.create_role(name="Muted", permissions=perms)
await member.add_roles(role, reason=reason) #User muted
embed = discord.Embed(title="User muted!", description="**{0}** was succesfully muted by **{1}** for **{2}**.".format(member, ctx.message.author, reason), color=0xff4654)
await ctx.channel.send(embed=embed)
channel = discord.utils.get(guild.channels, name="logs")
embed = discord.Embed(title=" ", description="**{0}** was muted by **{1}**. Reason: **{2}**".format(member, ctx.message.author, reason), color=0xff4654)
embed.set_author(name="{0}".format(member), icon_url=pfp)
await channel.send(embed=embed) #Log
await asyncio.sleep(tempmute)
await member.remove_roles(role)
else:
await member.add_roles(role, reason=reason) #User muted
embed = discord.Embed(title="User muted!", description="**{0}** was succesfully muted by **{1}** for **{2}**.".format(member, ctx.message.author, reason), color=0xff4654)
await ctx.channel.send(embed=embed)
channel = discord.utils.get(guild.text_channels, name="logs")
embed = discord.Embed(title=" ", description="<#{0}> was muted by <#{1}>. Reason: **{2}**".format(member.id, ctx.message.author.id, reason), color=0xff4654)
embed.set_author(name="{0}".format(member), icon_url=pfp)
await channel.send(embed=embed) #Log
await asyncio.sleep(tempmute)
await member.remove_roles(role)
I tried doing time: Optional[int] as someone else suggested but alas it broke it entirely.
To answer bluntly, there isn't a nice way of doing this. What I recommend is that you split the command into two separate ones, mute and tempmute.
If you really do want it as one function, you could do something like this:
import re
async def mute(self, ctx, member: discord.Member, *options):
if len(options) == 2: # User specified date and reason
expiry, reason = options
if len(options) == 1: # User specified one of the above so we need to find out which
if re.match(r"\d+\D", options[0]):
# Regex to check for one or more digit then a single non-digit
expiry = options[0]
reason = None
else:
expiry = None
reason = options[0]
On a slightly unrelated note, you should not do await asyncio.sleep(tempmute). Long sleeps are innacurate and if the bot restarts, the person will never be unmuted. You should create a database with datetimes of when the user should be unmuted and have a looping task which queries the database and unmutes users when their time is up.

discord.py react to get role

I have this embed with rules to my discord server and the embed also has a reaction, the reaction is a check mark(✅). If you react to that embed with that check mark you will get a "Verify" role and your reaction will be deleted.
So it always gonna be one reaction and if u react you get "Verify" role and your reaction get deleted.
But that is the problem, I don't get my role when I react and my reaction doesn't get deleted either. I don't have any errors don't know how to fix this.
This is my code
#bot.command(name='rules', pass_ctx=True)
async def rules(ctx):
rules_embed = discord.Embed(title='DISCORD RULES', color=0xf30000)
rules_embed.add_field(name="**1. No spamming** ", value="Don't send a lot of small/big messages right after "
"each other. Do not disrupt chat by spamming.",
inline=False)
rules_embed.add_field(name="**2. No NSFW material** ", value="This is a community server and not meant to "
"share this kind of material.", inline=False)
rules_embed.add_field(name="**3. No bullying or threats** ", value="Threats to other users of DDoS(Distributed "
"Denial of Service), Death, DoX/Doxx, abuse, "
"and other "
"malicious threats are absolutely not okay.",
inline=False)
rules_embed.add_field(name="**4. Don't beg for ranks** ", value="Don't beg for staff or other ranks", inline=False)
rules_embed.add_field(name="**5. No racism** ", value="Racism is absolutely not okay in this discord server",
inline=False)
rules_embed.add_field(name="**6. Have fun** ", value="Just have fun and be nice", inline=False)
send_rules = await ctx.message.channel.send(embed=rules_embed)
reactions = ['✅']
for i in reactions:
await send_rules.add_reaction(i)
#bot.event
async def on_raw_reaction_add(payload):
channel = bot.get_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
guild = bot.get_guild(payload.guild_id)
reaction = discord.utils.get(message.reactions, emoji=payload.emoji.name)
# only work if it is the client
if payload.member.id == bot.user.id:
return
if payload.message_id == 784182764009947177 and reaction.emoji == '✅':
roles = discord.utils.get(guild.roles, name='Verify')
await payload.member.add_roles(roles)
await reaction.remove(payload.member)
Thanks for the help!
You had several mistakes in the code, I have fixed them and commented a few of them. Please check the following CODE.
#bot.event
async def on_raw_reaction_add(payload):
#You forgot to await the bot.get_channel
channel = await bot.get_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
guild = bot.get_guild(payload.guild_id)
#Put the following Line
member = guild.get_member(payload.user_id)
reaction = discord.utils.get(message.reactions, emoji=payload.emoji.name)
# only work if it is the client
if payload.user_id == bot.user.id:
return
if payload.message_id == 784182764009947177 and reaction.emoji == '✅':
roles = discord.utils.get(guild.roles, name='Verify')
await member.add_roles(roles)
await reaction.remove(payload.member)

'TextChannel' object is not iterable | channel check not working

Hi I'm trying to check to see if a channel already exists instead of creating a new one each time a member sends a modmail request. The error I get is
File "C:\Users\User\Desktop\Build 1.0.2\cogs\modmail.py", line 145, in on_message
if get(modmailchannel, name=f"{message.author.name.lower()}{message.author.discriminator}"):
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\utils.py", line 271, in get
for elem in iterable:
TypeError: 'TextChannel' object is not iterable
enter code here
not sure why or the method is incorrect some how. The channel name that is created is the users name and discriminator. What I'm trying to achieve is if that channel already exists don't create another channel/post in the same channel.
Help appreicated
Here is the code I'm working with:
#commands.Cog.listener()
async def on_message(self, message):
if not isinstance(message.channel, discord.DMChannel) or message.author.id == self.bot.user.id:
# not a DM, or it's just the bot itself
return
bucket = self._cd.get_bucket(message)
retry_after = bucket.update_rate_limit()
seconds = bucket.update_rate_limit()
seconds = round(seconds, 2)
hours, remainder = divmod(int(seconds), 3600)
minutes, seconds = divmod(remainder, 60)
if retry_after:
pass
else:
channel = self.bot.get_channel(744311308509249610)
if not channel:
print("Mail channel not found! Reconfigure bot!")
time = datetime.utcnow()
guild = self.bot.get_guild(715288565877309521)
member_role = get(guild.roles, name='Members')
mod_role = get(guild.roles, name='Members')
muted_role = get(guild.roles, name='Modmail Muted')
user_id = message.author.id
author = guild.get_member(user_id)
content = message.clean_content
if muted_role in author.roles:
await message.channel.send("You're not allowed to use modmail.")
return
if len(message.content) < 0:
await message.channel.send("Your message should be atleast 50 characters in length.")
return
if member_role in author.roles:
emoji1 = '📨'
emoji2 = '🗑️'
message_confirm = "You're about to send a message to the mod team, react with :incoming_envelope: to confirm otherwise :wastebasket: to discard your message."
embed = discord.Embed(description=message_confirm, colour=0x5539cc)
embed.title = "Confirm Message"
confirm = await message.channel.send(embed=embed)
await confirm.add_reaction(emoji1)
await confirm.add_reaction(emoji2)
check = reaction_check(message=confirm, author=message.author, emoji=(emoji1, emoji2))
try:
reaction, user = await self.bot.wait_for('reaction_add', timeout=60.0, check=check)
if reaction.emoji == emoji1:
embed = discord.Embed(title=f"Message Sent", description=f"Hello {message.author.display_name}! We have received your message and we will get back to you as soon as possible. You can send another message in **{minutes}m {seconds}s**.", timestamp=time, colour=0x5539cc)
await message.channel.send(embed=embed)
elif reaction.emoji == emoji2:
embed = discord.Embed(title=f"Message Not Sent", description=f"Your message has not been sent. You can send another message in **{minutes}m {seconds}s**.", timestamp=time, colour=0x5539cc)
await message.channel.send(embed=embed)
return
except asyncio.TimeoutError:
await message.channel.send(f'Sorry, you took too long to confirm your message. Try again in **{minutes}m {seconds}s**.')
return
overwrites = {
guild.default_role: discord.PermissionOverwrite(read_messages=False),
guild.me: discord.PermissionOverwrite(read_messages=True),
member_role: discord.PermissionOverwrite(read_messages=False),
mod_role: discord.PermissionOverwrite(read_messages=True)}
embed = discord.Embed(title=f"Modmail message request from — {message.author.name}#{message.author.discriminator}", colour=0xff8100)
embed.add_field(name="Member:", value=f"{message.author.mention}" ,inline=True)
embed.add_field(name="Reply ID:", value=f"{message.author.id}" ,inline=True)
embed.add_field(name="Message:", value=content[:1000] or "blank", inline=False)
embed.set_footer(text=f"C&P Command: !reply {message.author.id}")
if message.attachments:
embed.add_field(name="Attachments", value=", ".join([i.url for i in message.attachments]))
if len(content[1000:]) > 0:
embed.add_field(name="Message (continued):", value=content[1000:])
modmailchannel = await guild.create_text_channel(f'{message.author.name}{message.author.discriminator}', overwrites=overwrites, category=self.bot.get_channel(744944688271720518))
await modmailchannel.send(embed=embed)
if get(modmailchannel, name=f"{message.author.name.lower()}{message.author.discriminator}"):
await modmailchannel.send(embed=embed)
else:
await message.channel.send("Only members can use modmail.")
Instead of if get(modmailchannel, name=f"{message.author.name.lower()}{message.author.discriminator}"): use:
if modmailchannel.name == f"{message.author.name.lower()}{message.author.discriminator}":

Role issuance system

There is a command to request a role. I don't understand how to set some functions for reactions.
#client.command()
async def role(ctx):
role = ctx.guild.get_role(703596629860548643)
zapros_chanell = client.get_channel(729733881129074768 )
zapros2_chanell = client.get_channel(703596629923725339 )
embed = discord.Embed(title="Запрос роли")
embed.add_field(name='Запросивший роль', value=ctx.message.author.mention)
embed.add_field(name='Роль для выдачи', value=role.mention)
embed.set_thumbnail(url=ctx.guild.icon_url)
message = await zapros_chanell.send(embed=embed)
await zapros2_chanell.send(embed = discord.Embed(description = f'{ctx.message.author.mention}, `запрос на выдачу роли был успешно отправлен, ожидайте его рассмотрения модерацией Discord`', color=discord.Color.purple()))
await message.add_reaction('✅')
await message.add_reaction('❎')
The bottom line is that if when you click on ✅, the role was issued and a text was written to the person in zapros2_chanell.
When you click on ❎, the person was not given a role, therefore, but a certain text was also written in zapros2_chanell.
I used an event called on_raw_reaction_add. Made a check to the reaction, keep in mind the will give the role to any message in the guild with ✅.It it just a proof of concept.
You can make it only accept reactions on certain channel, or even only for given message IDs
#client.command()
async def role(ctx):
zapros_chanell = client.get_channel(729733881129074768)
embed = discord.Embed(title="Запрос роли")
embed.add_field(name='Запросивший роль', value=ctx.message.author.mention)
embed.add_field(name='Роль для выдачи', value=role.mention)
embed.set_thumbnail(url=ctx.guild.icon_url)
message = await zapros_chanell.send(embed=embed)
await zapros2_chanell.send(embed = discord.Embed(description = f'{ctx.message.author.mention}, `запрос на выдачу роли был успешно отправлен, ожидайте его рассмотрения модерацией Discord`', color=discord.Color.purple()))
await message.add_reaction('✅')
await message.add_reaction('❎')
#client.event
async def on_raw_reaction_add(payload):
channel = await client.fetch_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
user = await client.fetch_user(payload.user_id)
emoji = payload.emoji.name
guild = client.get_guild(ID_HERE)
role = guild.get_role(703596629860548643)
zapros2_chanell = client.get_channel(703596629923725339 )
if user.id != bot.user.id: # bot to count itself
if emoji == '✅':
await client.add_roles(user, role)
await zapros2_chanell.send(f'{user.name} has been given {role.name}')
elif emoji == '❎':
await zapros2_chanell.send(f'{user.name} did not want {role.name}')

Resources