I'm making a "dynamic" help menu command for my Discord bot. Now my commands work with "normal" messages. But I want to use Embed. So, let me explain.
Basically, I do re!help and the bot sends an Embed. Then I add two reactions ("◀" and "▶"), and if I click on a reaction (eg "▶") the Embed is updated with the commands (I want both the title and the description of the Embed to be updated). I hope I was clear and if you did not understand what I want tell me I will try to explain it again. Here is my code anyway:
#client.command()
async def help(ctx):
contents = [
"""
text 1
""",
"""
text 2
""",
"""
text 3
""",
"""
text 4
"""]
pages = 4
cur_page = 1
message = await ctx.send(f"{contents[cur_page-1]}")
# getting the message object for editing and reacting
await message.add_reaction("◀️")
await message.add_reaction("▶️")
def check(reaction, user):
return user == ctx.author and str(reaction.emoji) in ["◀️", "▶️"]
# This makes sure nobody except the command sender can interact with the "menu"
while True:
try:
reaction, user = await client.wait_for("reaction_add", timeout=60, check=check)
# waiting for a reaction to be added - times out after x seconds, 60 in this
# example
if str(reaction.emoji) == "▶️" and cur_page != pages:
cur_page += 1
await message.edit(content=f"Page {cur_page}/{pages}:\n{contents[cur_page-1]}")
await message.remove_reaction(reaction, user)
elif str(reaction.emoji) == "◀️" and cur_page > 1:
cur_page -= 1
await message.edit(content=f"Page {cur_page}/{pages}:\n{contents[cur_page-1]}")
await message.remove_reaction(reaction, user)
else:
await message.remove_reaction(reaction, user)
# removes reactions if the user tries to go forward on the last page or
# backwards on the first page
except asyncio.TimeoutError:
await message.delete()
break
Wish someone could help!
Instead of contents being a list of strings, make it a list of embeds, then edit the message appropiately
contents = [
discord.Embed(title="page 1"),
discord.Embed(title="page 2"),
discord.Embed(title="page 3"),
discord.Embed(title="page 4")
]
...
await message.edit(
content=f"Page {cur_page}/{pages}",
embed=contents[cur_page - 1]
)
Related
I have mostly rebuilt the code and made changes.
When I do !embed everything works as it should, the bot creates the embed, and reacts with the emojis set, it just doesn't give user the roles once they react with the emojis the bot reacts to the post with..
I decided to add in some random print statements to hit the console per step and found I can see up to while True as commented in my code but after that the print statements don't respond.
My error handler/trace back has been pretty good so far and picked up everything, so its like after while True nothing exists, I believe the problem lies within this line:
reaction = await bot.wait_for("reaction_add", check=lambda reaction, user: user == reaction.message.id == msg.id)
Any help would be greatly appreciated.
desc1 = ("React with the corresponding emoji to game specific channels you want\n"
"\n👍 - Counter Strike\n"
"😀 - Warframe\n"
"🤓 - Lost Ark\n"
"😆 - Chiller\n"
"💪 - Warhammer")
#bot.command()
async def embed(ctx: commands.Context):
embed=discord.Embed(title="Create Your Profile", description=(desc1))
embed.set_footer(text="Jammys Bot")
msg = await ctx.send(embed=embed)
await msg.add_reaction('👍')
await msg.add_reaction('😀')
await msg.add_reaction('🤓')
await msg.add_reaction('😆')
await msg.add_reaction('💪')
print('it works here')
while True:
print('it works here 1') ## makes it up to here. Error handle/trace back isn't picking anything below up so its like its non-existent.
reaction = await bot.wait_for("reaction_add", check=lambda reaction, user: user == reaction.message.id == msg.id) # stops working here
print('it works here 2')
if reaction.emoji == '👍':
print('it works here 3')
CSGO = ctx.guild.get_role(780182144747569193)
print('it works here 4')
await bot.add_roles(CSGO)
print('it works here 5')
if reaction.emoji == '😀':
WF = ctx.guild.get_role(780244861130768395)
await ctx.add_roles(WF)
if reaction.emoji == '🤓':
LA = ctx.guild.get_role(947231964753170473)
await bot.add_roles(LA)
if reaction.emoji == '😆':
CHILL = ctx.guild.get_role(896853904384458772)
await bot.add_roles(CHILL)
if reaction.emoji == '💪':
WH = ctx.guild.get_role(896882687816630293)
await bot.add_roles(WH)
To send it to a specific channel you can try this code
channel =client.get_channel(your channelid)
msg = await channel.send(embed = embed)
I am currently making a car economy bot and I wanted to be able to interact with the bot just like how a game would. I wanted to use reactions for buttons but the problem was that when I used the code,
confirm_right = await client.wait_for("reaction_add", check=react_check_right)
confirm_left = await client.wait_for("reaction_add", check=react_check_left)
It didn't work as expected, first it waits for the confirm_right then the confirm_left.
I'm hoping to find a way to be able to make them both work at the same time.
This is my check functions,
def check(message):
return ctx.author.id == message.author.id
def react_check_right(reaction, user):
return user == ctx.author and str(reaction.emoji) in ["➡️"]
Found the answer
reaction, user = await client.wait_for('reaction_add', check = lambda reaction, user: reaction.emoji in ["➡️", "⬅️"])
if user == ctx.author:
if reaction.emoji == '➡️':
if car_shown < len(users[str(user.id)]['car']) - 1:
car_shown += 1
else:
car_shown = 0
```
this was basically it
I'm trying to make my discord bot have a higher or lower game where the player clicks an up or down arrow to guess, but I'm struggling to work out how check if the reaction is the first one or the second one.
Here is my code so far (its in a cog):
#commands.command()
async def highlow(self, ctx, limit:int = 100, *args):
num = random.randint(0, limit)
msg = await ctx.reply("The number is {num} \nWHould you like to go (h)igher or (l)ower")
await self.client.add_reaction(msg, emoji="<:arrow_up:>")
await self.client.add_reaction(msg, emoji="<:arrow_down:>")
def check(reaction, user):
return reaction.message == msg and str(reaction.emoji) == '<:arrow_down:>'
await self.client.wait_for('reaction_add', check=check)
(Also, I am aware that self.client.add_reaction will not work, its some old code I just quickly copied over to test out)
How do I make it so that I can check either reaction instead of just one?
To add a reaction we use msg.add_reaction("Your reaction") in your case.
If you want to check the reaction of a user we wait_for a reaction_add.
You can have a look at the following code:
try:
reaction, user = await client.wait_for('reaction_add')
while user == client.user:
reaction, user = await client.wait_for('reaction_add')
if str(reaction.emoji) == "YourEmoji": # First reaction
# Do what you want to do
if str(reaction.emoji) == "YourEmoji": # Second reaction
# Do what you want to do
except Exception: # Timeout can be added for example
return
Here we check that the bot reaction is not counting as a valid reaction but instead a user one. You can then add more if-statements in your code.
I have been using the following two methods to get a user input from a discord embed. The first I use to get a text based message input and the second I use to get a reaction input (used to paginate the embed). What I would like to know is if I can merge these two to create a method to wait for either a reaction or a message input?
#message input
try:
answer = await self.bot.wait_for(
"message",
timeout=60,
check=lambda message: message.author.id == ctx.author.id
and isinstance(message.channel, discord.channel.DMChannel)
)
#reaction input
try:
reaction, user = await self.bot.wait_for(
"reaction_add",
timeout=60,
check=lambda reaction, user: user.id == ctx.author.id
and reaction.emoji in buttons
#and isinstance(reaction.channel, discord.channel.DMChannel),
)
UPDATE:
So I have tried to implement the method duckboycool linked to (Many thanks man!).
The issue I am having now is that when I react to the paginated embed it works perfectly, the reaction is noted and the embed is updated accordingly. But if I input a message instead it get the following error:
"reaction, emoji = await task
TypeError: cannot unpack non-iterable Message object"
here is my code:
finished =0
while finished == 0:
done_tasks = None
check1=lambda reaction, user: user.id == ctx.author.id and reaction.emoji in buttons
check2=lambda message: message.author.id == ctx.author.id and isinstance(message.channel, discord.channel.DMChannel)
pending_tasks = [self.bot.wait_for('reaction_add',check=check1),self.bot.wait_for('message',check=check2)]
done_tasks, pending_tasks = await asyncio.wait(pending_tasks, return_when=asyncio.FIRST_COMPLETED)
#print(done_tasks)
for task in pending_tasks:
task.cancel()
for task in done_tasks:
reaction, emoji = await task
message = await task
if reaction:
print(reaction)
previous_page = current
if reaction.emoji == u"\u23EA":
current = 0
elif reaction.emoji == u"\u25C0":
if current > 0:
current -= 1
elif reaction.emoji == u"\u25B6":
if current < len(pages)-1:
current += 1
elif reaction.emoji == u"\u23E9":
current = len(pages)-1
if current != previous_page:
await msg.edit(embed = pages[current])
else:
print(message.content)
In the updated code, you need to check what kind of awaitable the event is so that you can unpack with the correct number of values and complete the intended behavior. With your examples of message and reaction_add, this would probably look something like this.
for task in done_tasks:
taskobj = await task
if isinstance(taskobj, discord.Message):
message = taskobj
#Message logic
else:
reaction, user = taskobj
#Reaction logic
You might have to do different things depending on which events you're using. (Here, it relies on a tuple being recognized as not an instance of discord.Message.)
def isAdmin(reaction, user):
for i in admins:
if user.id == i: return (str(reaction.emoji) == '👍' or str(reaction.emoji) == '👎')
try: reaction, user = await self.bot.wait_for('reaction_add', timeout=43200, check=isAdmin)
except asyncio.TimeoutError: await msg.edit(embed=embedEnd)
else:
resign_channel = discord.utils.get(ctx.guild.channels, name="resignings")
if str(reaction.emoji) == '👍':
print('{}: {} resigned {} {}.'.format(extCalls.timeNow('datetime'), ctx.channel.category.name, firstName, lastName))
await msg.edit(embed=embedFin)
await resign_channel.send(embed=embedConf)
else: await msg.edit(embed=embedEnd)
This is my code to have the bot wait for a reaction on msg. But, for some reason, when I have two msg (messages) waiting for a reaction at the same time, when I react to one message, both trigger success, even though I didn't react to the second message. This is hugely problematic for an admin-based approval system... does anyone know why this is happening or how to fix it?
This code is really messy, but if you're searching for a way to wait only for a reaction on one message you should add stuff to your check command, for example:
#bot.command(name="some_command")
async def sc(ctx):
msg = await ctx.send("Some interesting message!")
def check(reaction, user):
return reaction.message.id == msg.id
try:
reaction, user = await bot.wait_for("reaction_add", check=check,timeout=30.0)
except asyncio.TimeoutError:
await ctx.send("timeout")
return
await ctx.send(f"You reacted {str(reaction)}")