So modern documentation on the bot.wait_for() coroutine is not super detailed, and I'm having trouble getting it to work with reactions. Would appreciate feedback.
Python 3 with Discord.py
## Test Role Add
#kelutralBot.command(name='testreaction')
async def testReaction(ctx):
member = ctx.message.author
message = await ctx.send("This is a test message.")
emojis = ['\u2642','\u2640','\u2716']
for emoji in emojis:
await message.add_reaction(emoji)
def check(reaction, user):
return user == message.author and str(reaction.emoji) == '\u2642'
try:
reaction, user = await kelutral.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
await ctx.send("Window has passed to self-assign pronouns. Please DM a mod if you would still like to do so.")
else:
print(reaction)
male = get(member.guild.roles, name="He/Him")
await member.add_roles(male)
print("Assigned " + member.name + " He/Him pronouns.")
Two things were wrong.
First, don't use Client and Bot in the same command. Bot is sufficient for both.
Second, Unicode Emoji for Discord are treated as '\U000#####', which was the biggest problem.
Once we solved that, everything worked as intended.
The problem here is that you are checking if the person who sent the reaction is the author of the message that contains the message, which is only satisfied by the bot that reacted. Also consider using a role ID instead (server settings > roles > left click role > right click role > copy ID). You also want to be consistent with kelutral or kelutral Bot throughout the command.
#kelutralBot.command(name='testreaction')
async def testReaction(ctx):
member = ctx.message.author
message = await ctx.send("This is a test message.")
emojis = ['\u2642','\u2640','\u2716']
for emoji in emojis:
await message.add_reaction(emoji)
def check(reaction, user):
return user == member and str(reaction.emoji) == '\u2642' # check against the member who sent the command, not the author of the message
try:
reaction, user = await kelutralBot.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
await ctx.send("Window has passed to self-assign pronouns. Please DM a mod if you would still like to do so.")
else:
print(reaction)
male = ctx.message.guild.get_role(ROLE_ID_GOES_HERE) # put your role ID here #
await member.add_roles(male)
print(f"Assigned {member.name} He/Him pronouns.")
Keep in mind your code only works for the "male" role, you have to implement a different check function to use it for everything.
Related
async def on_message(message):
if message.content.startswith("-test"):
await message.channel.send("Checking for the Status :pencil:..")
time.sleep(1)
async def on_member_update(before, after):
if str("discord.gg / discord") in member.activities[0].name():
print("Correct Status giving role to User")
guild = client.get_guild(8516587165187561)
role = discord.utils.get(guild.roles, id=9157981598717517571)
await message.channel.send("roled")
i try to get an user status and if he have that status specified he get the role, i don't know why it doesn't work, i use discord.py
There's no need to put on_member_update in the code. It wouldn't work anyway even if you do due to the fact it's an event function, not something you can call on.
async def on_message(message):
if message.content.startswith("-test"):
await message.channel.send("Checking for the Status :pencil:..")
time.sleep(1)
if str("discord.gg / discord") in message.author.activities[0].name():
print("Correct Status giving role to User")
guild = client.get_guild(8516587165187561)
role = discord.utils.get(guild.roles, id=9157981598717517571)
await message.author.add_roles(role)
await message.channel.send("roled")
I've also added the await message.author.add_roles(role) part because there wasn't any command that assign roles in your original code.
It regards user imput, so like I was commissioned to do a prompt but I've never done this so like this is what i found online
playerChoice = await bot.wait_for('message', check=check(ctx.author), timeout=30)
I get some of it, but I don't get the 'message' part and the 'check = check'.
Here's my full code
#client.command()
async def event(ctx):
await ctx.send("Prompt will continue in DMs.")
embed = discord.Embed(title="Event Prompt", description="Please specify the type of event.")
embed.set_footer("Prompt will expire in ## seconds")
await ctx.author.send(embed=embed)
eventType = await bot.wait_for('message', check=check(ctx.author), timeout=30) # I want it to send the event type.
await ctx.send(eventType)
I'd like an explaination and a possible way to improve that and make it work. Thanks in advance
https://discordpy.readthedocs.io/en/stable/api.html?highlight=wait_for#discord.Client.wait_for
#client.event
async def on_message(message):
if message.content.startswith('$greet'):
channel = message.channel
await channel.send('Say hello!')
def check(m):
return m.content == 'hello' and m.channel == channel
msg = await client.wait_for('message', check=check)
await channel.send('Hello {.author}!'.format(msg))
await client.wait_for('message', check=check)
message refers to the event. You could have also do it for the event reaction_add, voice_state_update, etc.
So this means you are waiting for a message. The check defines what you are waiting for exactly. It is a method and returns a True or False.
In my case I am waiting for a message event which is posted in the same channel and has the message content "hello".
If you didn't have such check it would pick up a message that might have been send by a different author, in a different channel, in a different guild.
I've been troubleshooting this thing for days and this is the last straw.
Reaction_add only works if I do !on_message in the server 'general' channel, if I directly message the bot, it doesn't do anything. Though, funny thing is, if I message bot directly first and the enter !on_message in server general channel, bot reacts in general channel and in direct messages.
After TimeoutError I get thumbs down in direct messages.
This is the code, straight from discord.py documentation and I'm using Bot as Client:
#bot.command(pass_context=True)
async def on_message(ctx):
channel = ctx.channel
await ctx.send('Send me that 👍 reaction, mate')
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) == '👍'
try:
reaction, user = await bot.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
await channel.send('👎')
else:
await channel.send('👍')
It's funny that wait_for(message, ...) message works just fine everywhere.
Tried adding this and still no luck.
intents = discord.Intents.default()
intents.dm_reactions = True
bot = commands.Bot(command_prefix=PREFIX, description=DESCRIPTION, intents=intents)
It's because on_message is a bot event, which means it does not need to be invoked as a command, you can call the command with (), but in your case, you are trying to use the command as a event.
This is an event
#bot.event
async def on_message(message):
channel = message.channel
await message.channel.send('Send me that 👍 reaction, mate')
def check(reaction, user):
return user == message.author and str(reaction.emoji) == '👍'
try:
reaction, user = await bot.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
await message.channel.send('👎')
else:
await message.channel.send('👍')
This is a command, a command needs to be invoked with a function, in this case, it would be your prefix + thumbs !thumbs hope both of these help.
#bot.command()
async def thumbs(ctx):
channel = ctx.channel
await ctx.send('Send me that 👍 reaction, mate')
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) == '👍'
try:
reaction, user = await bot.wait_for('reaction_add', timeout=60.0, check=check)
except asyncio.TimeoutError:
await ctx.send('👎')
else:
await ctx.send('👍')
I added intents = discord.Intents.all() and that seems to do the trick, though it's not efficient because I might not need all privileged intents.
Input on issue would be helpful.
I've got this code for my discord bot, which can ban people and DM them the reason for the ban first.
cooldown = []
#bot.command(pass_context = True)
#commands.has_role('Senior Moderator')
async def ban(ctx, member: discord.Member = None, *, reason = None):
author = str(ctx.author)
if author in cooldown:
await ctx.send('Calm down! You\'ve already banned someone less than two hours ago.')
return
try:
if reason == None:
reason = 'breaking the rules.'
await member.send(f'You have been banned from **{ctx.guild.name}** for **{reason}**')
await member.ban(reason = f"Banned by {ctx.message.author} for "+reason)
await ctx.send(f'{member.mention} has been banned.')
cooldown.append(author)
await asyncio.sleep(2 * 60 * 60) #The argument is in seconds. 2hr = 7200s
cooldown.remove(author)
except:
await ctx.send('Error. Please check you are typing the command correctly. `!ban #username (reason)`.')
However, if the user I am trying to ban has DM's disabled, the bot can't send the ban reason message, so therefore doesn't proceed to the next step, which is banning them, and returns the error message, which is Error. Please check you are typing the command correctly. !ban #username (reason)
Please can you rewrite the code to allow it to try to DM someone the reasoning before banning them, but if they have DM's disabled, it shall still ban them anyway. Thank you!
By simply moving the ban to be executed first (as it is a priority), then it will attempt to dm the user.
I also re-adjusted some parts of the code. It will now try to send a dm, if not, it will still ban but a message will be sent to the channel alerting a message was not sent to the banned user.
#bot.command(pass_context = True)
#commands.has_role('Senior Moderator')
async def ban(ctx, member: discord.Member = None, *, reason = None):
author = ctx.author
if author in cooldown:
await ctx.send('Calm down! You\'ve already banned someone less than two hours ago.')
return
if member == None:
await ctx.send('Please mention a member to ban!')
return
if reason == None:
reason = 'breaking the rules.'
await member.ban(reason = f"Banned by {ctx.message.author} for " + reason)
try:
await member.send(f'You have been banned from **{ctx.guild.name}** for **{reason}**')
except:
await ctx.send(f'A reason could not be sent to {ctx.message.author} as they had their dms off.')
await ctx.send(f'{member.mention} has been banned.')
cooldown.append(author)
await asyncio.sleep(2 * 60 * 60)
cooldown.remove(author)
I too noticed the same thing when I first made my bot ban command, I did the following to fix my ban command. First I tried a scenario where the bot could dm the user, if it can, then It will dm the user and then ban the user (Note: Do not ban the user before DMing the user as the bot can only DM the user when the user and the bot share a common server). However, I then made an Exception that if the bot couldn't dm the user, it will send a message in the channel the command was executed "Couldn't dm the user" then ban the user
try:
await member.send(embed=embo)
await ctx.guild.ban(member, reason=reason)
except Exception:
await ctx.send("Couldn't send dm to the user")
await ctx.guild.ban(member, reason=reason)
EDIT:
you can also opt for the following:
try:
await member.send(embed=embo)
await ctx.guild.ban(member, reason=reason)
except discord.HTTPException:
await ctx.send("Couldn't send dm to the user")
await ctx.guild.ban(member, reason=reason)
before I specified this, I encountered the following error.
I fixed it, afterward, adding the exception case.
I am currently creating a simple login system for my discord server and im trying to find a way to store the user input into a variable and using it for a database. I currently want the input of [https://steamcommunity.com/id/] in the variable.
async def on_message(message):
if message.content.startswith('!login'):
channel = message.channel
embed = discord.Embed(color=discord.Color.red())
embed.set_author(name='{0.user}'.format(client), icon_url='https://scontent.fsyd1-1.fna.fbcdn.net/v/t1.15752-9/119154591_373692013645493_2520568812144261390_n.png?_nc_cat=100&ccb=2&_nc_sid=ae9488&_nc_ohc=qee8e9Q3a0sAX9_z2Nj&_nc_ht=scontent.fsyd1-1.fna&oh=ffeafaaa09f2bb802afce08030c41f08&oe=60394D0B')
embed.add_field(name='WELCOME TO LINKED.GG', value='Looking forward to seeing you in queue!', inline=False)
embed.add_field(name='Link your STEAM account to the bot:',
value='Reply to this bots message: \n `!login <steam profile url>`', inline=False)
embed.add_field(name='Please read over the rules:',
value='<#804911670698442815>', inline=False)
embed.add_field(name='Join our discord server', value='[Click to join discord](https://discord.gg/2dKCUAzceN)', inline=True)
await channel.send(embed=embed)
def check(m):
return m.content.startswith('https://steamcommunity.com/id/') and m.channel == channel
msg = await client.wait_for('message', check=check)
print(msg)
await channel.send('Connected {.author}!'.format(msg))
await client.process_commands(message)```
It's better by using a command instead of an on_message then you can pass an argument with the logging command. Ill leave it up to you to get your database sorted out, and searching if an account was already created.
Below is a simple example of passing a "username" argument through the command, the username would then be stored within the username variable.
#client.command()
async def login(ctx, *, username=None):
if username == None:
await ctx.send(f'{ctx.author.mention}, please enter your username to log in!')
return
await ctx.send(f'Logged in as {username}! Welcome back.')