Skip to content

Instantly share code, notes, and snippets.

@jpanahon
Last active July 25, 2021 05:57
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jpanahon/41ba697ae4284dc5b4ea15c09fb1e730 to your computer and use it in GitHub Desktop.
Save jpanahon/41ba697ae4284dc5b4ea15c09fb1e730 to your computer and use it in GitHub Desktop.
Help example for the rewrite branch of discord.py
# NOTICE: Before you copy this example, be sure you understand what all this does. Remember: This is a gist, not a github file meaning
# you can't pip install this, you would need to put this into a new file and add it to your cog list.
import discord
import asyncio
from discord.ext import commands
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
def helper(ctx):
"""Displays all commands"""
cmds_ = []
cogs = ctx.bot.cogs
for i in cogs:
cmd_ = ctx.bot.get_cog(i).get_commands()
cmd_ = [x for x in cmd_ if not x.hidden]
for x in list(chunks(list(cmd_), 6)):
embed = discord.Embed(color=discord.Color.blurple()) # can be any color
embed.set_author(name=f"{i} Commands ({len(cmd_)})")
embed.description = ctx.bot.cogs[i].__doc__
for y in x:
embed.add_field(name=y.signature, value=y.help, inline=False)
cmds_.append(embed)
for n, a in enumerate(cmds_):
a.set_footer(
text=f'Page {n+1} of {len(cmds_)} | Type "{ctx.prefix}help <command>" for more information'
)
return cmds_
def cog_helper(cog):
"""Displays commands from a cog"""
name = cog.__class__.__name__
cmds_ = []
cmd = [x for x in cog.get_commands() if not x.hidden]
if not cmd:
return (discord.Embed(color=discord.Color.blurple(),
description=f"{name} commands are hidden.")
.set_author(name="ERROR \N{NO ENTRY SIGN}"))
for i in list(chunks(list(cmd), 6)):
embed = discord.Embed(color=discord.Color.blurple()) # can be any color
embed.set_author(name=name)
embed.description = cog.__doc__
for x in i:
embed.add_field(name=x.signature, value=x.help, inline=False)
cmds_.append(embed)
for n, a in enumerate(cmds_):
a.set_footer(text=f"Page {n+1} of {len(cmds_)}")
return cmds_
def command_helper(command):
"""Displays a command and it's sub commands"""
try:
cmd = [x for x in command.commands if not x.hidden] # retrieves commands that are not hidden
cmds_ = []
for i in list(chunks(list(cmd), 6)):
embed = discord.Embed(color=discord.Color.blurple())
embed.set_author(name=command.signature)
embed.description = command.help
for x in i:
embed.add_field(name=x.signature, value=x.help, inline=False)
cmds_.append(embed)
for n, x in enumerate(cmds_):
x.set_footer(text=f"Page {n+1} of {len(cmds_)}")
return cmds_
except AttributeError:
embed = discord.Embed(color=discord.Color.blurple())
embed.set_author(name=command.signature)
embed.description = command.help
return [embed]
# ?tag lazy paginator is a more refined/easier to read version of this paginator.
async def paginate(ctx, input_):
"""Paginator"""
try:
pages = await ctx.send(embed=input_[0])
except (AttributeError, TypeError):
return await ctx.send(embed=input_)
if len(input_) == 1:
return
current = 0
r = ['\U000023ee', '\U000025c0', '\U000025b6',
'\U000023ed', '\U0001f522', '\U000023f9']
for x in r:
await pages.add_reaction(x)
paging = True
while paging:
def check(r_, u_):
return u_ == ctx.author and r_.message.id == pages.id and str(r_.emoji) in r
done, pending = await asyncio.wait([ctx.bot.wait_for('reaction_add', check=check, timeout=120),
ctx.bot.wait_for('reaction_remove', check=check, timeout=120)],
return_when=asyncio.FIRST_COMPLETED)
try:
reaction, user = done.pop().result()
except asyncio.TimeoutError:
try:
await pages.clear_reactions()
except discord.Forbidden:
await pages.delete()
paging = False
for future in pending:
future.cancel()
else:
if str(reaction.emoji) == r[2]:
current += 1
if current == len(input_):
current = 0
try:
await pages.remove_reaction(r[2], ctx.author)
except discord.Forbidden:
pass
await pages.edit(embed=input_[current])
await pages.edit(embed=input_[current])
elif str(reaction.emoji) == r[1]:
current -= 1
if current == 0:
try:
await pages.remove_reaction(r[1], ctx.author)
except discord.Forbidden:
pass
await pages.edit(embed=input_[len(input_) - 1])
await pages.edit(embed=input_[current])
elif str(reaction.emoji) == r[0]:
current = 0
try:
await pages.remove_reaction(r[0], ctx.author)
except discord.Forbidden:
pass
await pages.edit(embed=input_[current])
elif str(reaction.emoji) == r[3]:
current = len(input_) - 1
try:
await pages.remove_reaction(r[3], ctx.author)
except discord.Forbidden:
pass
await pages.edit(embed=input_[current])
elif str(reaction.emoji) == r[4]:
m = await ctx.send(f"What page you do want to go? 1-{len(input_)}")
def pager(m_):
return m_.author == ctx.author and m_.channel == ctx.channel and int(m_.content) > 1 <= len(input_)
try:
msg = int((await ctx.bot.wait_for('message', check=pager, timeout=60)).content)
except asyncio.TimeoutError:
return await m.delete()
current = msg - 1
try:
await pages.remove_reaction(r[4], ctx.author)
except discord.Forbidden:
pass
await pages.edit(embed=input_[current])
else:
try:
await pages.clear_reactions()
except discord.Forbidden:
await pages.delete()
paging = False
class Help(commands.Cog):
"""Help command"""
def __init__(self, bot):
self.bot = bot
@commands.command(hidden=True)
async def help(self, ctx, *, command=None):
if not command:
await paginate(ctx, helper(ctx))
if command:
thing = ctx.bot.get_cog(command) or ctx.bot.get_command(command)
if not thing:
return await ctx.send(f'Looks like "{command}" is not a command or category.')
if isinstance(thing, commands.Command):
await paginate(ctx, command_helper(thing))
else:
await paginate(ctx, cog_helper(thing))
def setup(bot):
bot.remove_command("help")
bot.add_cog(Help(bot))
@jpanahon
Copy link
Author

UPDATE 24/2/19: Changed the example to comply with new cogs and also edited a bunch of things.

@fleesu
Copy link

fleesu commented Feb 27, 2019

def helper(ctx):
    """Displays all commands"""

    cmds_ = []
    cogs = ctx.bot.cogs
    for i in cogs:
        cmd_ = ctx.bot.get_cog(i).get_commands()
        cmd_ = [x for x in cmd_ if not x.hidden]
        for x in list(chunks(list(cmd_), 6)):
            embed = discord.Embed(color=discord.Color.blurple())  # can be any color
            embed.set_author(name=f"{i} Commands ({len(cmd_)})")
            embed.description = ctx.bot.cogs[i].__doc__
            for y in x:
                embed.add_field(name=y.signature, value=y.help, inline=False)
            cmds_.append(embed)

        for n, a in cmds_:
            a.set_footer(
                text=f'Page {n + 1} of {len(cmds_)} | Type "{ctx.prefix}help <command>" for more information'
            )
    return cmds_
``` returns this when i invoke help
```py
    for n, a in cmds_:
TypeError: 'Embed' object is not iterable```

@TheFutureKnight
Copy link

Guess this needs another update to work with the new HelpFormater

@jpanahon
Copy link
Author

Guess this needs another update to work with the new HelpFormater
No, it doesn't (i use a modified version of this for my bot).

def helper(ctx):
    """Displays all commands"""

    cmds_ = []
    cogs = ctx.bot.cogs
    for i in cogs:
        cmd_ = ctx.bot.get_cog(i).get_commands()
        cmd_ = [x for x in cmd_ if not x.hidden]
        for x in list(chunks(list(cmd_), 6)):
            embed = discord.Embed(color=discord.Color.blurple())  # can be any color
            embed.set_author(name=f"{i} Commands ({len(cmd_)})")
            embed.description = ctx.bot.cogs[i].__doc__
            for y in x:
                embed.add_field(name=y.signature, value=y.help, inline=False)
            cmds_.append(embed)

        for n, a in cmds_:
            a.set_footer(
                text=f'Page {n + 1} of {len(cmds_)} | Type "{ctx.prefix}help <command>" for more information'
            )
    return cmds_
``` returns this when i invoke help
```py
    for n, a in cmds_:
TypeError: 'Embed' object is not iterable```

I fixed that in the code

@TheFutureKnight
Copy link

TheFutureKnight commented Apr 19, 2019

@OneEyedKnight it doesn't show the commands name, it just shows the arguments.

maybe you're on a different version but I believe I have the latest version currently
it sometimes doesn't iterate between pages upon using reactions and says:
In embed.fields.1.name: This field is required In embed.fields.2.name: This field is required In embed.fields.3.name: This field is required In embed.fields.5.name: This field is required

@thecaralice
Copy link

It just doesn't work for me

Traceback (most recent call last):
  File "C:\Users\iVan\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\ext\commands\core.py", line 63, in wrapped
    ret = await coro(*args, **kwargs)
  File "C:\Users\iVan\Documents\GitHub\Black-Cat\cogs\help.py", line 202, in help
    await paginate(ctx, helper(ctx))
  File "C:\Users\iVan\Documents\GitHub\Black-Cat\cogs\help.py", line 92, in paginate
    pages = await ctx.send(embed=input_[0])
  File "C:\Users\iVan\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\abc.py", line 776, in send
    data = await state.http.send_message(channel.id, content, tts=tts, embed=embed, nonce=nonce)
  File "C:\Users\iVan\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\http.py", line 222, in request
    raise HTTPException(r, data)
discord.errors.HTTPException: BAD REQUEST (status code: 400): Invalid Form Body
In embed.fields.0.name: This field is required

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

Traceback (most recent call last):
  File "C:\Users\iVan\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\ext\commands\bot.py", line 860, in invoke
    await ctx.command.invoke(ctx)
  File "C:\Users\iVan\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\ext\commands\core.py", line 698, in invoke
    await injected(*ctx.args, **ctx.kwargs)
  File "C:\Users\iVan\AppData\Local\Programs\Python\Python37\lib\site-packages\discord\ext\commands\core.py", line 72, in wrapped
    raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: HTTPException: BAD REQUEST (status code: 400): Invalid Form Body
In embed.fields.0.name: This field is required

@Andrew-9
Copy link

Not getting all the cogs. it's only getting one cog

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