EDIT:
The solution was to add an additional condition in the check function, I have posted the details in a follow-up answer.
Main Question:
I am new python and decided to learn it by creating a discord bot.
On user command, I was able to get the bot to send a message that can change pages on react following this design:
I want to make a multi-page help command using discord.py
I can change the pages of one message just fine, however, if the user calls the command multiple times, reacting to one of the bot sent messages change the pages for all messages that haven't timed out. What can I change in the code so that reacting to one message won't trigger page changes in the other ones?
Additional questions:
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"
reaction, user = await bot.wait_for("reaction_add", timeout=60, check=check)
A better alternative than the method one you provided is the use of ext.menus
(It's in beta, so it doesn't have any docs yet, to install it python -m pip install -U git+https://github.com/Rapptz/discord-ext-menus
)
Example
from discord.ext import menus
class MyMenu(menus.Menu):
async def send_initial_message(self, ctx, channel):
return await channel.send(f'Hello {ctx.author}')
@menus.button('\N{THUMBS UP SIGN}')
async def on_thumbs_up(self, payload):
await self.message.edit(content=f'Thanks {self.ctx.author}!')
@menus.button('\N{THUMBS DOWN SIGN}')
async def on_thumbs_down(self, payload):
await self.message.edit(content=f"That's not nice {self.ctx.author}...")
@menus.button('\N{BLACK SQUARE FOR STOP}\ufe0f')
async def on_stop(self, payload):
self.stop()
# later
@bot.command()
async def menu_example(ctx):
m = MyMenu()
await m.start(ctx)
Unfornatelly I can't answer your first question, I'm not sure why's that happening, sorry.
Answering your additional questions:
wait_for
takes the same arguments as the event, on_message
takes message
so the check will take message
as the single argument (the method will also only return message
). Adding more arguments it's pretty similar to the basics of decorators, we wrap the check in another outer functiondef check(func): # The ADDITIONAL arguments we want to pass, in this example another function
def inner_check(reaction, user): # The actual check
return user == ctx.author and func(reaction, user)
return inner_check
# To use it Note how I'm calling the check an additional argument
reaction, user = await bot.wait_for('reaction_add', check=check(lambda r, u: return True), timeout=60.0)
True
it will return the valuesTrue
the bot will wait till the function returns True
or the timeout is overThanks to Łukasz Kwieciński for answering my additional questions. Because I learned that wait_for waits for any reaction to any message, I've added an additional condition to the check() function. Now each message is independent of one another.
message = await ctx.send(f"Page {cur_page}/{pages}:\n{contents[cur_page-1]}")
# getting the message object for editing and reacting
def check(reaction, user):
if reaction.message != message:
return false
# SOLUTION: Checks if the message reacted on is the same as the one the bot sent
return user == ctx.author and str(reaction.emoji) in ["◀️", "▶️"]
# This makes sure nobody except the command sender can interact with the "menu"
However, this fix creates a possible performance problem. For every bot message that hasn't timed out yet, reacting to any message on the server causes it to run check() that many times.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.