Skip to content

Instantly share code, notes, and snippets.

@Soheab
Last active July 11, 2023 16:56
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Soheab/e73ab6f66881ee4102be37815da3a24e to your computer and use it in GitHub Desktop.
Save Soheab/e73ab6f66881ee4102be37815da3a24e to your computer and use it in GitHub Desktop.
Examples for a wait_for in ext.commands.

This gist shows how to make the bot wait for a message or reaction after doing a command. This should not be copypasted.

Docs

Check the discord.py docs for a detailed explanation of how it all works internally and which kwargs it takes.

Commands

See here two commands, one waiting for any message in a channel and the other waiting for a reaction with a specific emoji.

Check

Both examples have a check that checks if the author and channel matches with the context.
Need to wait for X in dms? Replace the channel check with a check for the guild like so, <message>.guild is None.

Message:

import asyncio
import discord


@bot.command(name="messagecheck")
async def message_check(ctx): # waiting for message here
    await ctx.send(f"**{ctx.author}**, send anything in 60 seconds!")

    def check(m: discord.Message):  # m = discord.Message.
        return m.author.id == ctx.author.id and m.channel.id == ctx.channel.id 
        # checking author and channel, you could add a line to check the content.
        # and m.content == "xxx"
        # the check won't become True until it detects (in the example case): xxx
        # but that's not what we want here.
        # Waiting for a number?
        # str has a method called "isdigit" that returns True -
        # - if the string is a number
        # so.. we can add something like following in the check:
        # m.content.isdigit()
        # and now the won't become True
        # until the content is all numbers, no letters.

    try:
        #              event = on_message without on_
        msg = await bot.wait_for('message', check = check, timeout = 60.0)
        # msg = discord.Message
    except asyncio.TimeoutError: 
        # at this point, the check didn't become True, let's handle it.
        await ctx.send(f"**{ctx.author}**, you didn't send any message that meets the check in this channel for 60 seconds..")
        return
    else:
        # at this point, the check has become True and the wait_for has done its work, now we can do ours.
        # we could also do things based on the message content here, like so
        # if msg.content == "this is cool":
        #    return await ctx.send("wait_for is indeed a cool method")
        
        await ctx.send(f"**{ctx.author}**, you responded with {msg.content}!")
        return

# invoke: [p]messagecheck

Reaction:

import asyncio
import discord
from typing import Union


@bot.command(name="reactiontest")
async def reaction_test(ctx): # waiting for reactions (✅, ❌) here
    await ctx.send(f"**{ctx.author}**, please react with :white_check_mark: or :x: on this message in 60 seconds")
    
    def check(r: discord.Reaction, u: Union[discord.Member, discord.User]):  # r = discord.Reaction, u = discord.Member or discord.User.
        return u.id == ctx.author.id and r.message.channel.id == ctx.channel.id and \
               str(r.emoji) in ["\U00002705", "\U0000274c"]
        # checking author, channel and only having the check become True when detecting a ✅ or ❌
        # else, it will timeout.

    try:
        #                         event = on_reaction_add without on_
        reaction, user = await bot.wait_for('reaction_add', check = check, timeout = 60.0)
        # reaction = discord.Reaction, user = discord.Member or discord.User.
    except asyncio.TimeoutError:
        # at this point, the check didn't become True.
        await ctx.send(f"**{ctx.author}**, you didnt react with a ✅ or ❌ in 60 seconds.")
        return
    else:
        # at this point, the check has become True and the wait_for has done its work, now we can do ours.
        # here we are sending some text based on the reaction we detected.
        
        #                         unicode for ✅ :
        #                         https://emojipedia.org/emoji/✅/#:~:text=Codepoints
        if str(reaction.emoji) == "\U00002705":
            return await ctx.send(f"{ctx.author} reacted with a ✅")
            # or we could also add a role here, like so
            # role = await ctx.guild.get_role(ROLE_ID)
            # await ctx.author.add_roles(role)
            
        #                         unicode for ❌ :
        #                         https://emojipedia.org/emoji/❌/#:~:text=Codepoints
        if str(reaction.emoji) == "\U0000274c":
            return await ctx.send(f"{ctx.author} reacted with a ❌")
@wasi-master
Copy link

Thanks

@Firminou
Copy link

Thank you for this example it really helped me !
But for the future people : don't use codepoints, use regular emojis it work better I had problems with the 4️⃣ and other number

@xu3s
Copy link

xu3s commented Aug 29, 2021

Hi, i would like to know if it possible to combine bot code on single command?
For example: i would like to make a bot where it return a list of link but i made it in multiple pages. Then the user can choose the number on the list by sending message.
It basically just both the code above combined but i don't know how to do that, searching on google didn't help.
Thanks

@v1s1t0r999
Copy link

v1s1t0r999 commented Sep 6, 2021

Search discord.py help command Paginator on google @xu3s

@xu3s
Copy link

xu3s commented Sep 6, 2021

Search discord.py help command Paginator on google @xu3s

i have found the way ,but i cant suppress this exception somehow:

Task exception was never retrieved
future: <Task finished name='Task-25' coro=<wait_for() done, defined at /data/data/com.termux/files/usr/lib/python3.9/asyncio/tasks.py:421> exception=TimeoutError()>
Traceback (most recent call last):
  File "/data/data/com.termux/files/usr/lib/python3.9/asyncio/tasks.py", line 492, in wait_for
    fut.result()
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/data/data/com.termux/files/usr/lib/python3.9/asyncio/tasks.py", line 494, in wait_for
    raise exceptions.TimeoutError() from exc
asyncio.exceptions.TimeoutError

here is my code if you would like to help:

@bot.command('epl3')
async def epl3(ctx):

    def _page(cur_p, pages, content):
        nl = '\n'
        return Embed(title='List of Chapter:', description=
                f'Page {cur_p}/{pages}:\n{nl.join(content[cur_p-1])}')

    stored = []

    detail = alpol().get_detail(3)
    ep_tier = ['rent_f','rent_p','episode']
    episodes = [ep for c in ep_tier for ep in detail[c] ]
    renp = [
            f'{n}.) {x["title"]} - {x["price"]}' if x['up'] not in stored else
            f'{n}.) {x["title"]} - {x["price"]} {x["up"]}'
            for n,x in enumerate(episodes,1)
            ]
    contents = [renp[x:x+10] for x in range(0,len(renp),10)]
    pages = len(contents)
    cur_p = 1
    message = await ctx.reply(embed=_page(cur_p, pages, contents))
    await message.add_reaction('⏮️')
    await message.add_reaction('⬅️')
    await message.add_reaction('➡️')
    await message.add_reaction('⏭️')

    while True:
        try:

            def check(reaction,user):
                return user == ctx.author and str(reaction.emoji) in ['⬅️', '➡️','⏮️','⏭️']
            def check_msg(msg):
                return msg.author == ctx.author
                
            done, pending = await asyncio.wait([
                    asyncio.create_task(bot.wait_for('reaction_add', check=check, timeout=15)),
                    asyncio.create_task(bot.wait_for('message', check=check_msg, timeout=15))
                    ], return_when=asyncio.FIRST_COMPLETED)

            for task in pending:
                task.cancel()

            event = done.pop().result()


            if isinstance(event, Message):
                await ctx.reply(event.content)
                break

            #reaction
            reaction, user = event

            if str(reaction.emoji) == '➡️' and cur_p != pages:
                cur_p +=1
                await message.edit(embed=_page(cur_p, pages, contents))

                await message.remove_reaction(reaction,user)

            elif str(reaction.emoji) == '⬅️' and cur_p > 1:
                cur_p -=1
                await message.edit(embed=_page(cur_p, pages, contents))

                await message.remove_reaction(reaction,user)

            else:
                await message.remove_reaction(reaction, user)

        except asyncio.TimeoutError:
            await ctx.reply('Time is Up!!!')
            break
        except asyncio.CancelledError:
            print('cancelled')
            break

i'll just ignore it if there is no way to cath it haha
Thank you @v1s1t0r999

@puang59
Copy link

puang59 commented Oct 9, 2021

Thank you so much <3

@v1s1t0r999
Copy link

@xu3s
Sorry if twas rude to u 😉 Didn't mean it......
You can use discordSuperUtils or discord-pretty-help also.
discordSuperUtils has way more functionalities too.

@enzo405
Copy link

enzo405 commented Mar 13, 2022

is this possible to make the 2nd part in DM ??? because it's been 2d i don't get it !! please help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment