Attribute Error Issue 'User' object has no attribute 'roles' - python-3.x

I'm having a slight issue checking for roles a user has. I'm currently getting a
if member_role in message.author.roles]
AttributeError: 'User' object has no attribute 'roles'
It seems I cannot pass roles with the message argument when checking to see if the message author has them roles.
Help Appreciated.
Here is my code I'm working with:
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
channel = self.bot.get_channel(578731262550736910)
if not channel:
print("Mail channel not found! Reconfigure bot!")
time = datetime.utcnow()
guild = self.bot.get_guild(555844758778544158)
member_role = get(guild.roles, name='Members')
muted_role = get(guild.roles, name='Modmail Muted')
content = message.clean_content
verified = [member for member in guild.members
if member_role in message.author.roles]
muted = [member for member in guild.members
if muted_role in message.author.roles]
if muted:
await message.channel.send("You're not allowed to use modmail.")
return
if verified:
embed = discord.Embed(title="📬 You've got modmail!")
embed.add_field(name="Sent by:", value=f"{message.author.mention} ({message.author.id})" ,inline=False)
embed.add_field(name="Message:", value=content[:1000] or "blank")
embed.set_footer(text="Mods: React with icon below this message to notify us that you're dealing with this modmail. (First reaction already set by the bot)")
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:])
msg = await channel.send(embed=embed)
await msg.add_reaction('📤')
try:
embed = discord.Embed(title=f"Thanks {message.author.display_name}! your message has been sent to the Mods.", timestamp=time)
embed.set_footer(text="Message Sent")
await message.add_reaction('📬')
await message.channel.send(embed=embed)
except discord.ext.commands.errors.CommandInvokeError:
await message.channel.send('📬')
else:
await message.channel.send("Only members can use modmail.")

as suggested by #Patrick Haugh in replies I used get_member.
user_id = message.author.id
author = guild.get_member(user_id)

Related

Star-Board discord.py issues

I'm using a json file in order to store the star-board channels for the guilds. Here's my code:
#client.event
async def on_raw_reaction_add(payload):
star = await get_star_channels()
if str(payload.message.guild.id) not in star:
return
starchannel = client.get_channel(star[str(payload.message.guild.id)])
if payload.emoji == '⭐':
if str(payload.message.guild.id) not in star:
return
if not payload.member.guild_permissions.manage_messages:
await payload.message.channel.send("You don't have the permissions to star a message")
return
if len(payload.message.attachments) > 0:
embed.set_image(url = payload.message.attachments[0].url)
embed = discord.Embed(description = f"{payload.message.author}:\n\n{payload.message.content}\n\n**[Jump to message]({payload.message.jump_url})**", color = random.choice(colors_for_embeds1), timestamp = datetime.now())
embed.set_author(name = f"{payload.member.name} starred a message", icon_url = payload.member.avatar_url)
embed.set_footer(text = f"<#{payload.channel.id}>")
await starchannel.send(embed = embed)
The problem I'm facing is that 'RawReactionActionEvent' object has no attribute 'message' or 'guild' so there's no way I can get the guild ID where the reaction was added (It seems that way). Without accessing the guild ID, I cannot get the star channel ID stored in the JSON file because the key of all the star-channel IDs is the guild ID.
you should use guild_id and message_id
Below are all attributes of RawReactionActionEvent:
message_id
user_id
channel_id
guild_id
emoji
member Only for on_raw_reaction_add
event_type

Error await keeps creating multiple channels instead of checking

Hi I'm having an issue where my code is creating more than one channels. The function of the code should:
Check to see if the channel exists
If Channel does exist don't create a new one.
Otherwise, if the channel does not exist create one.
In a nutshell, how it supposed to work, The on_message event checks for a response from a user in a direct message sent to the bot, their message is then relayed to a guild channel that either already exists otherwise a new one is created before the message is sent.
In this is the issue is that I can't get around it with a check to see if the channel exists, it sure does but it just duplicates everytime a message is sent:
I have tried both these methods to check:
#Check 1
for channel in guild.text_channels:
if channel.name == f"{message.author.name.lower()}{message.author.discriminator}" and channel.name is not None:
await channel.send(embed=embed)
else:
channel_non = await guild.create_text_channel(f'{message.author.name}{message.author.discriminator}', overwrites=overwrites, category=self.bot.get_channel(744944688271720518))
await channel_non.send(embed=embed)
#Check 2
channel_present = False
for channel in guild.text_channels:
if channel.name == f"{message.author.name.lower()}{message.author.discriminator}":
await channel.send(embed=embed)
channel_present = True
if channel_present:
channel_non = await guild.create_text_channel(f'{message.author.name}{message.author.discriminator}', overwrites=overwrites, category=self.bot.get_channel(744944688271720518))
await channel_non.send(embed=embed)
The check 2 code causes the on_message event to do nothing. Help would be much appreciated I don't know where I'm going wrong here.
Honestly, I would just do it by a command.
#client.command()
async def command(ctx):
author = ctx.message.author
guild = ctx.guild
for channel in guild.text_channels:
if channel.name == ctx.message.author:
await channel.send(embed = embed)
return
channel = await guild.create_text_channel(f"{ctx.message.author}", overwrites = overwrites, category = self.bot.get_channel(744944688271720518))
await channel.send(embed = embed)

'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}')

Why is discord.Client.wait_for is asking for event when it has one?

The problem i'm having is the code never getting through "0004" since it gets stuck on wait_for which wants additional positional argument: 'event', which should be the 'message' in bracket right next to it from what I've seen on examples from discord.py site & inside the code itself.
'''
class Test(commands.Cog):
def __init__(self, spreadsheeter):
self.spreadsheeter = spreadsheeter
#commands.command()
async def apply(self, ctx):
a_list = []
submit_channel = spreadsheeter.get_channel(718698172293316608)
channel = await ctx.author.create_dm()
def check(m):
return m.content is not None and m.channel == channel
for question in q_list:
print("0001")
sleep(.5)
print("0002")
await channel.send(question)
print("0003")
msg = await Client.wait_for('message', timeout=60, check=check)
print("0004")
a_list.append(msg.content)
print("0005")
submit_wait = True
print("0006")
while submit_wait:
print("0007")
await channel.send("End of questions 'submit' to finish.")
print("0008")
msg = await Client.wait_for("message", check=check)
print("0009")
if "submit" in msg.content.lower():
print("0010")
submit_wait =False
print("0011")
answers = "\n".join(f"{a}. {b}" for a, b in enumerate(a_list, 1))
print("0012")
submit_msg = f"Apllication from {msg.author} \nThe answers are:\n{answers}"
print("0013")
await submit_channel.send(submit_msg)
print("0014")
Error:
What I've unsuccessfully tried:
discord.client.wait_for('message', check=check)
Error: discord.client has no attribute 'wait_for'
Same for discord.member, ctx.member, ctx.client
Replacing "message" to 'message' (doesn't change anything)
Moving around the position of 'message'
Raises ton of other errors...
Giving the line bracket at start (self, "message", check=check)
Error: 'Test' object has no attribute 'loop'
#client.event instead of cog style commands.command
Error: Unresolved reference 'client'
Intent of the code:
Be called from DM as a command, then start a series of questions from q_list in that DM and then store them into a_list. Then after its done it should submit the a_list into as submit_msg into discord channel.
What it does so far?:
Asks first question from q_list
q_list = [
"Question one",
"Question two ha-ha-haaa",
"Question three ha-ha-haaa"
]
"""
After that it immediately after trying to await answer gives error.
2 Things at the beginning, you should only make a dm channel if there isn't any yet.
ctx.author.chreate_dm() doesn't return a channel, so instead of assigning the dm channel to channel you can check it's in the right channel by using ctx.author.dm_channel
#commands.command()
async def apply(self, ctx):
a_list = []
submit_channel = self.spreadsheeter.get_channel(718698172293316608)
if not ctx.author.dm_channel:
await ctx.author.create_dm()
def check(m):
return m.content is not None and m.channel == ctx.author.dm_channel
Instead of Client use spreadsheeter as that is your client
msg = await self.spreadsheeter.wait_for('message', timeout=60, check=check)
Figured it out, the thing being is it might work in cogs, but I think it won't or something is just missing...
The way i made it to work is to use it in
#Client.event (in my case #spreadsheeter.event)
Instead of ctx. I used payload.
"""
#spreadsheeter.event
async def on_raw_reaction_add(payload):
print("Hell-Yeah")
message_id = payload.message_id
if message_id == 739153263907176480:
guild_id = payload.guild_id
guild = discord.utils.find(lambda g : g.id == guild_id, spreadsheeter.guilds)
print (payload.emoji.name)
if payload.emoji.name == "No1":
role = discord.utils.find(lambda r: r.name == 'RAIDERS', guild.roles)
print(role)
if role in payload.member.roles:
print("You really want to leave?")
else:
print("Kick")
await payload.member.kick()
elif payload.emoji.name == "Yes1":
print("Yo, we got a potential raider over here..")
channel = await payload.member.create_dm()
await channel.send("This is still Work/Bot is under Development (Not actuall application yet).")
await channel.send("Whenever you are ready, please start replaying to the questions.")
print("0000")
submit_channel = spreadsheeter.get_channel(718698172293316608)
if not payload.member.dm_channel:
await payload.member.create_dm()
channel = payload.member.dm_channel
print(channel)
def check(m):
return m.content is not None and m.channel == channel
for question in q_list:
print("0001")
sleep(.5)
print("0002")
await channel.send(question)
print("0003")
msg = await spreadsheeter.wait_for(event='message', timeout=60, check=check)
print("0004")
a_list.append(msg.content)
print("0005")
submit_wait = True
print("0006")
while submit_wait:
print("0007")
await channel.send("End of questions 'submit' to finish.")
print("0008")
msg = await spreadsheeter.wait_for("message", timeout=60, check=check)
print("0009")
if "submit" in msg.content.lower():
print("0010")
submit_wait = False
print("0011")
answers = "\n".join(f"{a}. {b}" for a, b in enumerate(a_list, 1))
print("0012")
submit_msg = f"Apllication from {msg.author} \nThe answers are:\n{answers}"
print("0013")
await submit_channel.send(submit_msg)
print("0014")
elif payload.emoji.name == "Need_more_info":
print("Yo, we got a potential diplomat over here..")
channel = await payload.member.create_dm()
await channel.send("This is still Work/Bot is under Development (Not actuall application yet).")
await channel.send("Whenever you are ready, please start with \n_apply")
"""
It still doesn't have the handling for multiple users, but works fine with one at a time at this moment, which solves the original problem of wait_for not waiting for anything else then timeout to run out.

Resources