I've made a command that prompts the server with 2 numbers, and the users add the numbers in the shortest time possible. I'm trying to figure out a way to ping the user who got the correct answer, but I can't seem to get it to work. One thing I've tried applying is adding m.author == message.author to the return line, as seen here, but I get an error saying message is undefined. I'm still pretty new to this, so it'd be nice to know how to implement it into my code, or if there's a different way, as all I want is to be able to have the bot ping the user who sent the right answer first.
#client.command()
async def math(ctx):
num_one = random.randint(1, 98)
num_two = randint(1, 98)
answer = num_one + num_two
answer_string = str(answer)
await ctx.send(f'{num_one} + {num_two}?')
def check(m):
return m.content == answer_string
msg = await client.wait_for('message', check=check)
await ctx.send(f"got the correct answer!")```
You should be able to do await ctx.send(f"{msg.author.mention} got the correct answer!").
client.wait_for returns a Message object, which means it has an author attribute. You can then use the .mention attribute of a Member object(the author), which will ping them.
Related
When someone mentions the bot, I want it to respond saying, "Hey! I've been mentioned. My prefix is >. You can type >help to see my commands." I have the code written out but the bot does not respond at all when it gets mentioned. I do not get any errors when I run it or mention the bot. How do I get this to work? My code is below. Thanks in advance.
#bot.event
async def on_message(message):
if message.author.id == bot.user.id:
return
msg_content = message.content.lower()
mention = ['#830599839623675925', '#Ultimate Bot']
if any(word in msg_content for word in mention):
await message.channel.send("Hey! I've been mentioned. My prefix is `>`. You can type `>help` to see my commands.")
else:
await bot.process_commands(message)
You can use the mentioned_in() method.
So in this case it would be;
#bot.event
async def on_message(message):
if message.author.id == bot.user.id: return
if bot.user.mentioned_in(message):
await message.channel.send("Hey! I've been mentioned.")
await bot.process_commands(message)
Some things to note:
Hard-coding in values such as in your mention variable is bad practice. It makes it much harder to maintain your code, and when your project gets bigger you may question what the purpose of these seemingly random strings are referring to. For example, if you were to change the name of the bot, or to transfer your code to a new bot, going through your entire codebase and picking out and changing these values would be far from ideal.
I don't know about the rest of your application, but you probably don't want process_commands() to be in the else statement. For example, if the user mentions the bot in their message, but are using a command, the command will be blocked because you are not triggering process_commands().
Hope this answers your question
You could instead just check if bot.user is in the message mentions.
You can also check if the bot display name was used.
if bot.user in message.mentions or (message.guild and message.guild.me in message.mentions):
print("bot was mentioned")
elif message.guild and message.guild.me.display_name in message.content:
print("bot name was used")
I am writing a discord quiz bot using discordpy.
The bot sends a message that contains the questions and the 4 possible answers.
The bot also adds reactions to his message with the emojis 1️⃣, 2️⃣, 3️⃣ and 4️⃣.
The idea is, that the bot waits 30 seconds for people to click on one fo the reactions. If the clicked reaction is the correct/wrong answer, the bot replies with either correct or wrong. The bot should also stop waiting for new reaction once one person answered. Aka: Once a person clicks on one of the 4 reaction emojis, the bot should reply, and not process any future reactions to this message.
Currently, I got the bot to send the message (an embed) and add the reaction emojis to it. However, obtaining the results from the people is where I have problems with.
For one, the bot still seems to get triggered by his own reactions for some reason, even I excluded that in the check function. (Or so I thought).
In general, I'd like to have a very well structured approach for this. I am familiar with all the api calls/events, such as on_message() and on_reaction_add(), but I have trouble putting everything together correctly.
This is what I have so far:
#commands.command(name="quiz")
async def on_command_quiz(ctx):
#if ctx.message.author.bot:
# return
print("Quiz command!")
quiz = QuizGame()
# Send quiz
reply = await ctx.message.channel.send(embed=quiz.format())
# Add reply emojis
for x in range(0, len(quiz.quiz_answers)):
await reply.add_reaction(Utils.get_number_emoji_by_number(x + 1))
print("Correct Answer:", quiz.quiz_correct_answer)
# Evaluate replies
async def check_answer(reaction, user):
emojis = ["1️⃣","2️⃣","3️⃣","4️⃣"]
return user != ctx.message.author and str(reaction.emoji) in emojis
# Wait for replies
try:
reaction, user = await bot.wait_for('reaction_add', timeout=30.0, check=check_answer)
except asyncio.TimeoutError:
print("Timeout")
else:
if user != ctx.message.author:
if str(reaction.emoji) == "1️⃣":
print("1")
elif str(reaction.emoji) == "2️⃣":
print("2")
elif str(reaction.emoji) == "3️⃣":
print("3")
elif str(reaction.emoji) == "4️⃣":
print("4")
else:
print("Unknown reaction")
How can I get this right?
There are several errors and some inaccuracies in your code; first I'll list them and then I'll show you what I think is the best way to set up this type of commands.
Please note that some of the following are not actual fixes but more efficient ways to organize your code.
-You should use decorators to define bot commands, instead of using functions like on_command:
#bot.command()
async def quiz(ctx)
-The ctx class provides the channel attribute already, so ctx.message.channel is kind of redundant, use ctx.channel instead.
Same applies for ctx.message.author.
-If the number of answers is always the same, then you can add the numeric emojis with a very simple for loop (also, there is no need to call Utils to get the relevant emojis):
for emoji in ["1️⃣","2️⃣","3️⃣","4️⃣"]:
reply.add_reaction(emoji)
-The check_answer function is redundant as well, and logically wrong too.
It is redundant because there is no need to verify that the reaction emoji is one of the 4 available, since it will be determined later in the try block anyway.
It is logically wrong because it should return True if the user who added the reaction matches the author of the command, and not the opposite (you will notice that this will also prevent the bot from being triggered by its own reactions).
Then, there is no need for the function to be asynchronous.
def check_answer(reaction, user):
return user == ctx.author
-Finally, the whole try-except-else block is not really functional here. In order for the bot to remain responsive until the first reaction of the specific user or until the 30 seconds timeout expires, you should integrate the try-except block into an infinite while loop:
while True:
try:
reaction, user = await bot.wait_for("reaction_add", timeout=30, check=check_answer)
# The following line is optional: it removes the reaction added by the user
# to let them react again with the same emoji; not really necessary in your case,
# but very helpful if your bot would still be responsive after the first reaction.
await reply.remove_reaction(reaction, user)
# Here goes the if-else block of reactions.
except asyncio.TimeoutError:
print("Timeout")
Remember that somewhere in the try block you will have to stop the loop with a break statement when the operation is finished, otherwise it will continue indefinitely.
I am developing a Discord bot too and am still a beginner, so I hope I've been able to explain well.
Anyway, to sum it up, here is an example of how I would personally implement that command:
#bot.command()
async def quiz(ctx):
print("Quiz command!")
quiz = QuizGame()
reply = await ctx.send(embed=quiz.format())
emojis = ["1️⃣","2️⃣","3️⃣","4️⃣"]
for emoji in emojis:
await reply.add_reaction(emoji)
def check_answer(reaction, user):
return user == ctx.author
while True:
try:
reaction, user = await bot.wait_for("reaction_add", timeout=30, check=check_answer)
await reply.remove_reaction(reaction, user)
# Shorter representation of that if-else block.
if reaction.emoji in emojis:
print(emojis.index(reaction.emoji) + 1)
break
else:
print("Unknown reaction")
except asyncio.TimeoutError:
print("Timeout")
Then of course you should define how to recognize the correct answer and how to notify the user.
If you need some clarification on what I wrote, feel free to comment on this answer and I will be happy to answer you.
You didn't really ignore the bots reaction, at least from what I can see in the code.
You can try the following method:
try:
reaction, user = await self.bot.wait_for('reaction_add', timeout=15)
while user == self.bot.user:
reaction, user = await self.bot.wait_for('reaction_add', timeout=15)
if str(reaction.emoji) == "YourEmoji":
A check function could be:
reactions = "YourReactions"
def check1(reaction, user):
return user == ctx.author and str(reaction.emoji) in [reactions]
Here we check if the reaction comes from the author of the command and also check if the emoji is in the reactions "list".
#MassDM Command
#bot.command()
async def massdm(ctx, msg):
await ctx.message.delete()
show_cursor()
wipeinput = input(Fore.RED+"Are you sure you want to mass dm? (WARNING: This happens really fast so it will probably flag your account)(y or n): ")
hide_cursor()
if wipeinput == "y" or wipeinput == "Y":
for user in ctx.guild.members:
if user != bot.user:
try:
channel = await user.create_dm()
await channel.send(msg)
print(Fore.GREEN + f'Sent to user "{user.name}"')
except:
print(Fore.YELLOW + f'Failed to DM user "{user.name}"')
pass
print(Fore.GREEN+"Finished")
When I run this it just says "Finished" and doesnt do anything. when i remove the try/except it gives not error? I have all the proper intents set up i think
You're making a mistake in this line:
wipeinput = input(Fore.RED+"Are you sure you want to mass dm? (WARNING: This happens really fast so it will probably flag your account)(y or n): ")
It seems that you're waiting for a response. The way to do that is with the wait_for function. You should change that to this:
await ctx.send("Are you sure you want to mass dm? (WARNING: This happens really fast so it will probably flag your account)(y or n)?")
wipeinput = bot.wait_for('message') # You can add another parameter to check if the input is valid and what you want.
In addition, I am unsure of what the functions show_cursor() and hide_cursor() are. They may also be causing something to not work.
EDIT: (Thanks to IPSDSILVA for pointing this out) Even though the code that I gave doesn't revolve around the issue in the post, any issue in the code may cause your code to not work
Here is the full code with everything working.
#client.command(pass_context=True)
async def massdm(ctx):
await ctx.message.delete()
for member in list(client.get_all_members()):
try:
embed = discord.Embed(title="Test",
description="Test!",
color=discord.Colour.blurple())
embed.set_thumbnail(
url="test")
embed.set_footer(
text=
"test"
)
await asyncio.sleep(30)
await member.send(embed=embed)
except:
pass
#await ctx.send(f"Messaged: {member.name}")
print(f"Messaged: {member.name}")
I have bunch of fun commands like "hug, marry etc."
They are working with
.hug user
but ı want my python bot tell "You need to mention someone" if you dont mention a user. However, whenever I test the bot, nothing happens and users cant know that they need to mention a user
My code:
async def hug(ctx,user:discord.Member):
if not discord.Member:
await ctx.send("you need to mention someone")
return
embed = discord.Embed(title=f"{ctx.author} hugs {user.name}", description="nice...")
embed.set_image(url="url")
await ctx.send(embed=embed)``
The solution is very simple, the message object has a mentions attribute, you can check the length of the message's mentions and if they are not equal to 1 then there should be an error.
async def hug(ctx, user:discord.Member):
if len(ctx.message.mentions) != 1:
await ctx.send("you must mention a user!")
return
embed = discord.Embed(title=f"{ctx.author} hugs {user.name}", description="nice...")
embed.set_image(url="url")
await ctx.send(embed=embed)``
The Discord API documentation about the message.mentions attribute: https://discordpy.readthedocs.io/en/latest/api.html?highlight=message%20mentions#discord.Message.mentions
The way you are checking, is wrong, you defined an attribute named user and gave it a type discord.Member, so you need to check whether user is passed.
async def hug(ctx, user:discord.Member):
if not user:
await ctx.send("you must mention a user!")
return
embed = discord.Embed(title=f"{ctx.author} hugs {user.name}", description="nice...")
embed.set_image(url="url")
await ctx.send(embed=embed)
The problem, if you are checking len(ctx.message.mentions) != 1, is that when you use:
.hug xyz #billy it is also gonna pass the if condition, but not gonna work, instead it will throw an error. because a BadArgument - xyz is passed second.
I recently had the idea of DMing my bot commands. For example, a command which would unban me from every server that the bot is on.
Unfortunately I don't have any starting point for the command because I'm not even sure DMing commands is possible.
Keywords like discord.py, command or DM are so common in google that finding any good info on the topic is very hard.
I'm looking for a way for the bot to receive DMs as commands and only accept them from me (my ID is stored in the variable ownerID if anyone wants to share any code).
While I'm mostly looking for the above, some code for the DM unban command would also be very helpful.
EDIT: I was asked to show some example code from my bot. Here is the code for the command number which generates a random number and sends it in a message. I hope this gives you an idea of how my bot is made:
#BSL.command(pass_context = True)
async def number(ctx, minInt, maxInt):
if ctx.message.author.server_permissions.send_messages or ctx.message.author.id == ownerID:
maxInt = int(maxInt)
minInt = int(minInt)
randomInt = random.randint(minInt, maxInt)
randomInt = str(randomInt)
await BSL.send_message(ctx.message.channel, 'Your random number is: ' + randomInt)
else:
await BSL.send_message(ctx.message.channel, 'Sorry, you do not have the permissions to do that #{}!'.format(ctx.message.author))
You can send commands in private messages. Something like
#BSL.command(pass_context=True)
async def unban(ctx):
if ctx.message.channel.is_private and ctx.message.author.id == ownerID:
owner = await BSL.get_user_info(ownerID)
await BSL.say('Ok {}, unbanning you.'.format(owner.name))
for server in BSL.servers:
try:
await BSL.unban(server, owner) # I think having the same name should be fine. If you see weird errors this may be why.
await BSL.say('Unbanned from {}'.format(server.name))
except discord.HTTPException as e:
await BSL.say('Unbanning failed in {} because\n{}'.format(server.name, e.text))
except discord.Forbidden:
await BSL.say('Forbidden to unban in {}'.format(server.name))
else:
if ctx.message.author.id != ownerID:
await BSL.say('You are not my owner')
if not ctx.message.channel.is_private:
await BSL.say('This is a public channel')
should work. I'm not sure what happens if you try to unban a user who is not banned, that may be what raises HTTPException