Send sequence of Embedded-messages in Discord.py - python

I'm trying to make a welcome bot that is going to send the first message, when the person hits a button/emoji below, the next message will come. Going to post the first message, then the next message underneath. Right now I'm using a so to start it you need to write test.
Any suggestion on how I can make it so?
#client.event
async def on_message(message):
if message.content == 'test':
how_channel = client.get_channel(CHANNELNAME)
myEmbed = discord.Embed(title="**Now then, how does everything work? Here's a quick tour!**",
description="You can see your progression as a percentage as you progress, click the button below to continue trough!",
color=0x00ff00)
myEmbed.add_field(name="It should only take a few minutes!", value="Press the button below to continue")
myEmbed.set_footer(text="Christer - How does it work", icon_url="https://cdn.icon-icons.com/icons2/2619/PNG/512/among_us_discord_icon_156922.png")
await how_channel.send(embed=myEmbed)
So that was the first message that is getting sent, so I want the underneath message to be sent after a button or emoji has been pressed.
#client.event
async def on_message(message):
if message.content == 'test':
how_channel = client.get_channel(CHANNELNAME)
myEmbed = discord.Embed(title="News Feed",
description="The #announcements will be your most important channel here! You'll be able to keep up to date with everything which is happening within the server so you'll never miss a beat!",
color=0x00ff00)
myEmbed.add_field(name="It should only take a few minutes!", value="Press the button below to continue")
myEmbed.set_footer(text="Christer - How does it work", icon_url="https://cdn.icon-icons.com/icons2/2619/PNG/512/among_us_discord_icon_156922.png")
await how_channel.send(embed=myEmbed)

If you want to use the buttons, you need to specify the lib you use to implement them so we can help you. For reactions, you need to add one via add_reaction (https://discordpy.readthedocs.io/en/stable/api.html#discord.Message.add_reaction) and then redirect the on_reaction_add event to your control with wait_for (https://discordpy.readthedocs.io/en/stable/api.html#discord.Client.wait_for), as you probably do for messages. Don't forget to set a check in your wait_for.
Example :
message = await ctx.send(first_embed)
await message.add_reaction("🟢")
await client.wait_for('reaction_add', check = lambda reaction, user: user == message.author and str(reaction.emoji) == "🟢")
await ctx.send(second_embed)

Related

Discord.py modify a message based on reaction

I have an issue and I can't understand how to do it.
Basically I have a bot that send a message if he detect a word.
Now I wanted to add the possibility to modify that message based on who add a reaction (the bot add 3 reactions at each message he send).
So far, it's working, my bot respond and add the 3 reactions, but now I don't understant how to make my bot edit his message (I don't even manage to make the bot say who click on a reaction) and also remove the reaction.
I've read the docs, read a lot of forum and I can't find a way to make it work...
Please help =)
blue = '<:blue:1033370324135333898>'
red = '<:red:1033370379663704148>'
yellow = '<:yellow:1033370423078952970>'
#client.event
async def on_message(message):
if 'ping' in message.content.lower():
if message.channel.id == 1032275628470308895:
channel = client.get_channel(1032275628470308895)
react = await channel.send('pong!')
await react.add_reaction(blue)
await react.add_reaction(red)
await react.add_reaction(yellow)
#client.event
async def on_raw_reaction_add(payload):
# and then I don't know what to do, everything I've tried doesn't work
# I wanted to modify the message to (for example "{user} said blue pong!" and remove the reaction the user just use, and just keep the 3 from the bot)
# And then if another user use the yellow reaction, the bot modify his message to for example "{user] said yellow pong!"
# That could be great if the message was edited but keeping what was said before (in that case "pong! \n{user} said blue pong! \n{user} said yellow pong!")
# And last thing, if possible I want the bot to remove all reactions including his after 30mins
https://discordpy.readthedocs.io/en/stable/api.html?highlight=on_raw_reaction_add#discord.RawReactionActionEvent
The event on_raw_reaction_add returns a payload which is a RawReactionActionEvent class.
From this payload you can get the message_id which can be used to fetch the message (using for example discord.utils) and then you can edit the message using the class method edit
https://discordpy.readthedocs.io/en/latest/api.html#discord.Message.remove_reaction
The discord message object has a method called remove reaction that can remove reactions when you pass it an emoji and member object.
https://discordpy.readthedocs.io/en/latest/api.html#discord.Reaction.remove
You can also use the class method remove from the reaction object itself
These resources should be enough for you to code a solution to your problem :)
Alright I think I'm done!
Now my bot answer to a specific word, and add reactions to his message.
If someone react to it, my bot remove the user reaction, and edit his own message, keep his previous text and add something else at the end (in that case):
"Previous message"
"user" said it's blue pong
And then after some times, the bot remove all the reactions
Here the code if someone want to do that someday..
blue = '<:blue:1033370324135333898>'
red = '<:red:1033370379663704148>'
yellow = '<:yellow:1033370423078952970>'
#client.event
async def on_message(message):
if 'ping' in message.content.lower():
if message.channel.id == 1032275628470308895:
channel = client.get_channel(1032275628470308895)
react = await channel.send('pong!')
await react.add_reaction(blue)
await react.add_reaction(red)
await react.add_reaction(yellow)
await asyncio.sleep(10)
await react.clear_reactions()
#client.event
async def on_raw_reaction_add(payload):
channel = client.get_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id)
user = client.get_user(payload.user_id)
if str(payload.emoji) == blue:
if str(payload.user_id) == "bot_ID": # bot_ID here, don't want the bot trigered by his own reaction
return
await message.remove_reaction(payload.emoji, user)
await message.edit(content=f"{message.content} \n{user.name} said it's a blue pong !")
Thanks #hexxx for your help, it's was my first time programming something ^^

How can I work around a possible "wait_for" event error with my discord bot?

I have an event waiting for a reaction under a message. But if the bot should fail once this event is dropped and if you then click on the reaction there is no reaction from the bot, because he can not capture this reaction. Is there an alternative how to get the bot to continue anyway?
I don't like to work with a timeout event or asyncio.sleep, because the input should come from the user. Is this possible?
My code so far without workaround:
try:
reaction, user = await self.bot.wait_for('reaction_add', timeout=43200)
while user == self.bot.user:
reaction, user = await self.bot.wait_for('reaction_add', timeout=43200)
if str(reaction.emoji) == "⏯":
await ctx.send("**Your next question will appear in `6` seconds.**", delete_after=5)
await intense.delete()
except asyncio.TimeoutError:
await ctx.send("**Really?! I though `12 hours` would be enough..."
"Your next question will come in about `6` seconds.**", delete_after=5)
await asyncio.sleep(6)
Below I've added two examples and tweaked up existing code to make it a little more cleaner. It cannot fail unless the message that the bot was adding to was deleted, otherwise it would work. I also added a check function just to help, but you can change that to whatever you want. Included a true loop, so it would keep waiting for an input.
message = await ctx.send("Test reactions")
back = '⬅️'
forward= '⏯'
await message.add_reaction(back)
await message.add_reaction(forward)
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) in [back, forward]
member = ctx.author
while True:
try:
reaction, user = await client.wait_for("reaction_add", check=check)
if str(reaction.emoji) == back:
await ctx.send('Going back')
if str(reaction.emoji) == forward:
await ctx.send('Going forward')
Your other solution is just to use on_reaction_add event, but I wouldn't recommend, unless your bot is small and no one minds if a message always gets send if they sent an emoji you put in your code.
It's by far a much easier approach but may not expect the best results compared to the wait_for But hope this helps too.
#client.event
async def on_reaction_add(reaction, user):
if reaction.emoji == '⬅️':
await ctx.send('Going back')
if reaction.emoji == '⏯':
await ctx.send('Going back')
The problem with this, is it's always active and waiting for those reactions to be added, it can cause intermittent slowdown of the bot, or people could react to the emojis by accident and mess up the command because they shouldn't be able to use these commands unless the original command has been invoked.
I believe raw_reaction_add should work in this context. Eveytime a user will interact with the message reaction it causes an event

Waiting for command author's reaction to edit to the help category page

message=await ctx.send(f"{ctx.author.mention}, after reacting to an emoji below the message will be edited to your choosen category)
await message.add_reaction('📜')
await message.add_reaction('🔧')
await message.add_reaction('🔨')
await message.add_reaction('🔥')
message = await client.wait_for('on_reaction_add', timeout= 10, check=lambda message: message.author == ctx.author)
if message.reaction_add == '📜':
await ctx.send("You reacted!")
Im making a command to use the help page and it edits the pre-existing help message to the category the user chooses that corresponds to the emoji they reacted for.
I have used a way that has no checks which annoys other people if they happen to react with those emojis to another message, is there are way to make a check similar to what I have already done or close to, where the message of help can be edited to the page the user wants and only works if they used the command specifically for the author that can use the emojis?
To condense this a little bit, I will use only two of the provided reactions.
#client.command()
async def test(ctx):
message = await ctx.send("Message that's getting reactions")
paper = '📜' # for the sake of "I'd rather not copy and paste these emojis a lot"
hammer = '🔨'
await message.add_reaction(paper)
await message.add_reaction(hammer)
def check(reaction, user):#checking for reactions and user
return user == ctx.author and str(reaction.emoji) in [paper, hammer]
# in this case, the message author and the reaction emojis paper and hammer
try:
reaction, user = await client.wait_for("reaction_add", timeout = 3, check=check)
# waiting for a reaction to be added
if str(reaction.emoji) == paper:
await ctx.send("You reacted!")
if str(reaction.emoji) == hammer:
await message.edit(content="Another reaction!")# this is for editing the message sent
except: # Timeout error-handling
await message.edit(content="Run command to use again!")
If you'd like, you could also include while True so the user doesn't have to constantly execute the command.
Working test as seen in the image below:
References:
discord.Client.wait_for
discord.Message.edit
How to loop with a reaction embed menu (SO)
reaction, user = await client.wait_for('reaction_add', timeout=10.0, check...)
If you read the docs about on_reaction_add, you'll see that it takes reaction and user arguments.
To edit the message:
message = reaction.message
await message.edit(content='new content!')

Discord.py Commands within Commands?

I've been thinking of making a Blackjack game in my discord bot, but I've hit a roadblock.
I obviously have the game which is summoned with the command .blackjack, and it works fine in generating the random values and sending the messages. However, I don't know how to make it so the player is able to say hit or stand after the message with the cards dealt is sent, for example.
#client.command()
async def blackjack(ctx):
# (insert all random number gens, etc. here)
await ctx.send(f"{dface1}{dsuit1} ({dvalue1}), {dface2}{dsuit2} ({dvalue2})")
await ctx.send(f"(Dealer Total: {dtotal})")
await ctx.send(f"{pface1}{psuit1} ({pvalue1}), {pface2}{psuit2} ({pvalue2})")
await ctx.send(f"(Total: {ptotal})")
Now what? What do I do to run my next part of the code, which is whether or not the player hit or stands, the dealer hitting and standing, etc.
I don't really know how to play Blackjack, so I'm afraid I won't be able to give you a full answer to your question. However I will say how you can achieve what you want. There are two ways you can go about doing this in my opinion.
Method 1
Waiting for the user to react to your bot's message
For this, you have to use:
reaction, user = await client.wait_for('reaction_add', timeout=60.0, check=check)
For example, say you are waiting for 🇦 or 🅱️ from the user (This can mean hit and stand respectively). The code would look something like this:
#client.command()
async def start(ctx):
def check(reaction, user):
return (user == ctx.author) and (str(reaction.emoji) == '🇦' or str(reaction.emoji) == '🅱️')
async def sendMessage(msg):
message = await ctx.send(msg)
await message.add_reaction('🇦')
await message.add_reaction('🅱️')
try:
reaction, user = await client.wait_for('reaction_add', timeout = 60.0, check = check)
except:
await message.clear_reactions()
await ctx.send('No reaction received.')
else:
await message.clear_reactions()
return reaction
return 0
reaction = str(await sendMessage('This is my message'))
This is a simple code to check if the user reacts with 🇦 or 🅱️. You'll have to add more conditions and loops to get what you desire.
Method 2
Waiting for the user to send a message
For this, you have to use:
msg = await client.wait_for('message', check = check, timeout = 60.0)
You'll have to then check if msg equals hit or stand or some short form like h or s. Also be sure to write a check(author) function that is called inside the client.wait_for() function (check = check) to check if the author is that same as the one that ran the command.
I hope you'll be able to come up with the code you are looking for after reading this answer.
discord.py has built-in subcommand support, here's an example:
#commands.group(invoke_without_subcommand=True)
async def your_command_name(ctx):
# Do something if there's not a subcommand invoked
#your_command_name.command()
async def subcommand_name(ctx, *args):
# Do something
# To invoke
# {prefix}your_command_name subcommand_name some arguments here
Or you can simply wait for a message
#client.command()
async def blackjack(ctx):
# ...
def check(message):
"""Checks if the message author is the same as the one that invoked the
command, and if the user chose a valid option"""
return message.author == ctx.author and message.content.lower() in ['stand', 'hit']
await ctx.send('Would you like to hit or stand?')
message = await client.wait_for('message', check=check)
await ctx.send(f"You chose to `{message.content}`")
# To invoke
# {prefix}blackjack
# Would you like to hit or stand?
# stand
# You chose to `stand`

Discord.py Extract user from reaction

I am writing a feature in my bot which allows 2 users to duel each other, and the way it works is that the bot sends a message telling everyone to get ready and then after a random number of seconds pass the bot reactions with a certain emoji on the message and the first person in the duel reacts to the message wins. It a simple I idea but I can't figure out how to see if a certain user has reacted to the message. My question is - is this possible or do I have to figure out another approach to this?
This is definitely possible with wait_for which is documented here.
The wait_for function waits for any specified event listed in the event reference.
It takes in three parameters Event, Check (optional) and timeout(optional)
there are examples in the documentation but for you it would be something like:
bot = commands.Bot(command_prefix='.', case_insensitive=True)
#bot.command()
async def game(ctx):
# Whatever you want here
msg = await ctx.send("React first to win")
await msg.add_reaction('👍')
def check(reaction, user):
return str(reaction.emoji) == '👍' and user != bot.user
try:
# Timeout parameter is optional but sometimes can be useful
reaction, user = await bot.wait_for('reaction_add', timeout=30, check=check)
# Will wait until a user reacts with the specified checks then continue on with the code
await ctx.send(f"Congratulations {user.name} you won!")
except asyncio.TimeoutError:
# when wait_for reaches specified timeout duration (in this example it is 30 seconds)
await ctx.send("You ran out of time!")
The Timeout=30 parameter is 100% optional you can remove it so it would wait forever (or until you turn your bot off). The check parameter is also optional.
P.S. I am assuming you are using python 3 or more hence the F-strings
Yes, that's possible for sure, there are multiple ways for doing this, I never tried it before since I didn't create any bots with reaction things but that should work.
using on_reaction_add
#client.event
async def on_reaction_add(reaction, user):
channel = client.get_channel('123')
if reaction.message.channel.id != channel.id:
return
else:
if reaction.emoji == "✅":
#Do something
print(user.name)
print(user.discriminator)
await channel.send(f"{user} has won the game")
Make sure to take a look at the on_reaction_add documentation and the reaction one

Categories

Resources