コード例 #1
0
    async def send_command_help(self, command):
        '''Help interface for the commands themselves (Overridden)'''

        ## Initial setup
        self.paginator = Paginator()
        clip_groups = self.context.bot.get_cog("Clips").clip_groups

        ## Is the help command a category? If so only dump the relv
        command_str = command.__str__()
        if (command.name in clip_groups):
            await self.send_clip_category_help(command)
            return

        # <signature> section
        signature = self.get_command_signature(command)
        self.paginator.add_line(signature, empty=True)

        # <long doc> section
        help_section = command.help
        if help_section:
            if (len(help_section) > self.paginator.max_size):
                for line in help_section.splitlines():
                    self.paginator.add_line(line)
            else:
                self.paginator.add_line(help_section, empty=True)

        self.paginator.close_page()
        await self.send_pages()
コード例 #2
0
ファイル: util.py プロジェクト: AkiraSama/fresnel
 def __init__(self, ctx: Context, base_title: str, color=None):
     self.ctx = ctx
     self.title = base_title
     self.attrs = {}
     if color:
         self.attrs['color'] = color
     self.paginator = Paginator(prefix='', suffix='', max_size=2048)
コード例 #3
0
 async def show_off(self, ctx: CommandContext, name: str):
     paginator = Paginator(prefix="```py")
     for line in inspect.getsource(
             self.bot.get_command(name).callback).split('\n'):
         paginator.add_line(line.replace("`", "`\u200B"))
     for page in paginator.pages:
         await ctx.send(page)
コード例 #4
0
    async def source(self, ctx: Context, *, command_name: str) -> None:
        """Get the source code of a command."""
        command = self.bot.get_command(command_name)

        if not command:
            await ctx.send(f"No command named `{command_name}` found.")
            return

        try:
            source_lines, _ = inspect.getsourcelines(command.callback)
        except (TypeError, OSError):
            await ctx.send(
                f"I was unable to retrieve the source for `{command_name}` for some reason."
            )
            return

        source_lines = textwrap.dedent("".join(source_lines).replace(
            "```", f"`{ZWS}`{ZWS}`")).split("\n")
        paginator = Paginator(
            prefix="```py",
            suffix="```",
            max_size=2048,
        )

        for line in source_lines:
            paginator.add_line(line)

        pages = menus.MenuPages(
            source=SauceSource(
                paginator.pages,
                per_page=1,
            ),
            clear_reactions_after=True,
        )
        await pages.start(ctx)
コード例 #5
0
    def __init__(self, ctx, text, *, prefix='```', suffix='```', max_size=2000):
        paginator = CommandPaginator(
            prefix=prefix, suffix=suffix, max_size=max_size - 200)
        for line in text.split('\n'):
            paginator.add_line(line)

        super().__init__(ctx, entries=paginator.pages, per_page=1, show_entry_count=False)
コード例 #6
0
    async def reload(self, ctx: Context):
        """Reloads all cogs."""

        # make a copy of the extension list, because we will be iterating over it and modifying it due to the
        # load and unload calls
        extensions = self.bot.extensions.copy()

        log = Paginator()

        # unload and load everything
        for extension_name, _module in extensions.items():
            try:
                self.log.info('reloading "%s"', extension_name)
                self.bot.unload_extension(extension_name)
                self.bot.load_extension(extension_name)
            except Exception:
                self.log.exception('failed extension load (%s):',
                                   extension_name)
                log.add_line(
                    f'\N{CROSS MARK} Failed to reload extension `{extension_name}`.'
                )

        if not log.pages:
            await ctx.send('\N{OK HAND SIGN}')
        else:
            for page in log.pages:
                await ctx.send(page)
コード例 #7
0
    async def send_bot_help(self, mapping):
        '''The main bot help command (overridden)'''

        ## Initial setup
        self.paginator = Paginator()
        phrase_cog = self.context.bot.get_cog("Phrases")
        phrase_groups = None
        if (phrase_cog != None):
            phrase_groups = phrase_cog.phrase_groups

        self.dump_header_boilerplate()

        ## Dump the non-phrase commands
        self.dump_commands()

        ## Dump the base phrase commands
        if (phrase_groups != None):
            phrases_group = phrase_groups["phrases"]
            if (phrases_group):
                self.dump_phrase_group(phrases_group)

            ## Dump the names of the additional phrases. Don't print their commands because that's too much info.
            ## This is a help interface, not a CVS receipt
            self.dump_phrase_categories(phrase_groups)

            self.dump_footer_boilerplate(list(phrase_groups.keys()))

        await self.send_pages()
コード例 #8
0
ファイル: formatter.py プロジェクト: slice/kmn
    async def format(self):
        """Handles the actual behaviour involved with formatting.

        To change the behaviour, this method should be overridden.

        Returns
        --------
        list
            A paginated output of the help command.
        """
        self._paginator = Paginator()

        # we need a padding of ~80 or so

        description = self.command.description if not self.is_cog() else inspect.getdoc(self.command)

        if description:
            # <description> portion
            self._paginator.add_line(description, empty=True)

        if isinstance(self.command, Command):
            # <signature portion>
            signature = self.get_command_signature()
            self._paginator.add_line(signature, empty=True)

            # <long doc> section
            if self.command.help:
                self._paginator.add_line(self.command.help, empty=True)

            # end it here if it's just a regular command
            if not self.has_subcommands():
                self._paginator.close_page()
                return self._paginator.pages

        max_width = self.max_name_size

        def category(tup):
            cog = tup[1].cog_name
            # we insert the zero width space there to give it approximate
            # last place sorting position.
            return cog.lower() + ':' if cog is not None else '\u200bcommands:'

        filtered = await self.filter_command_list()
        if self.is_bot():
            data = sorted(filtered, key=category)
            for category, commands in itertools.groupby(data, key=category):
                # there simply is no prettier way of doing this.
                commands = sorted(commands)
                if len(commands) > 0:
                    self._paginator.add_line(category)

                self._add_subcommands_to_page(max_width, commands)
        else:
            filtered = sorted(filtered)
            if filtered:
                self._paginator.add_line('commands:')
                self._add_subcommands_to_page(max_width, filtered)

        return self._paginator.pages
コード例 #9
0
    def __init__(self, **options):
        self.asciidoc_prefix = "```asciidoc"
        self.paginator = Paginator(prefix=self.asciidoc_prefix)
        self.commands_heading = "Commands\n--------"

        super().__init__(paginator=self.paginator,
                         commands_heading=self.commands_heading,
                         **options)
コード例 #10
0
    def __init__(self, text, *, prefix="```", suffix="```", max_size=2000):
        pages = CommandPaginator(prefix=prefix,
                                 suffix=suffix,
                                 max_size=max_size - 200)
        for line in text.split("\n"):
            pages.add_line(line)

        super().__init__(entries=pages, per_page=1)
コード例 #11
0
    async def roles(self, ctx: CommandContext):
        guild: Guild = ctx.guild
        paginator = Paginator()
        for role in guild.roles:
            paginator.add_line(role.name + ' ' + str(role.id))

        for page in paginator.pages:
            await ctx.send(embed=Embed(color=Color.blurple(), description=page)
                           )
コード例 #12
0
    async def format(self):
        """A modified copy of Discord.py rewrite's vanilla HelpFormatter.format()."""
        self._paginator = Paginator()

        # we need a padding of ~80 or so
        description = self.command.description if not self.is_cog(
        ) else inspect.getdoc(self.command)

        if description:
            # <description> portion
            self._paginator.add_line(description, empty=True)

        if isinstance(self.command, Command):
            # <signature portion>
            signature = self.get_command_signature()
            self._paginator.add_line(signature, empty=True)

            # <long doc> section
            if self.command.help:
                self._paginator.add_line(self.format_help(self.command.help),
                                         empty=True)

            # end it here if it's just a regular command
            if not self.has_subcommands():
                self._paginator.close_page()
                return self._paginator.pages

        max_width = self.max_name_size

        def category(tup):
            cog = tup[1].cog_name
            # we insert the zero width space there to give it approximate
            # last place sorting position.
            return cog + ':' if cog is not None else '\u200bCommands:'

        filtered = await self.filter_command_list()
        if self.is_bot():
            data = sorted(filtered, key=category)
            for category, commands in itertools.groupby(data, key=category):
                # there simply is no prettier way of doing this.
                commands = sorted(commands)
                if len(commands) > 0:
                    self._paginator.add_line(category)

                self._add_subcommands_to_page(max_width, commands)
        else:
            filtered = sorted(filtered)
            if filtered:
                self._paginator.add_line('Commands:')
                self._add_subcommands_to_page(max_width, filtered)

        # add the ending note
        self._paginator.add_line()
        ending_note = self.get_ending_note()
        self._paginator.add_line(ending_note)
        return self._paginator.pages
コード例 #13
0
    async def ext_list(self, ctx: Context):
        """List all loaded extensions."""

        paginator = Paginator(prefix='```\n')

        for name in sorted(self.bot.extensions):
            paginator.add_line(name)

        for page in paginator.pages:
            await ctx.send(page)
コード例 #14
0
    def __init__(self, entries: typing.List[str], *, per_page=None):
        self.initial_page = True

        if per_page is None:
            pages = CommandPaginator(prefix="", suffix="", max_size=1800)
            for line in entries:
                pages.add_line(line)

            super().__init__(pages.pages, per_page=1)

        else:
            super().__init__(entries, per_page=per_page)
コード例 #15
0
    def __init__(self, **options):
        self.sort_commands = options.pop('sort_commands', True)
        self.commands_heading = options.pop('commands_heading', "Commands")
        self.dm_help = options.pop('dm_help', False)
        self.dm_help_threshold = options.pop('dm_help_threshold', 1000)
        self.aliases_heading = options.pop('aliases_heading', "Aliases:")
        self.no_category = options.pop('no_category', '')
        self.paginator = options.pop('paginator', None)

        if self.paginator is None:
            self.paginator = Paginator(suffix=None, prefix=None)

        super().__init__(**options)
コード例 #16
0
async def paginate(message: str,
                   pag: disextc.Paginator = None) -> disextc.Paginator:
    """ Helper to use the Paginator.

    Given a line of text it will format it and return the paginator to add
    more lines.

    :param message -> str type with message to send
    :param pag -> Pagenator to add to, or none to create a new.
    :return -> Paginator containing line of text.
    """
    if pag is None:
        pag = disextc.Paginator()
    pag.add_line(message)
    return pag
コード例 #17
0
def run():
    """This command runs the bot."""

    paginator = Paginator(max_size=1336)
    bot = CrimsoBOT(help_command=PaginatedHelpCommand(paginator=paginator))
    bot.load_extensions()
    bot.run(TOKEN)
コード例 #18
0
    async def raw(self, ctx: Context, *, message: Message, json: bool = False) -> None:
        """Shows information about the raw API response."""
        # I *guess* it could be deleted right as the command is invoked but I felt like it wasn't worth handling
        # doing this extra request is also much easier than trying to convert everything back into a dictionary again
        raw_data = await ctx.bot.http.get_message(message.channel.id, message.id)

        paginator = Paginator()

        def add_content(title: str, content: str) -> None:
            paginator.add_line(f'== {title} ==\n')
            # replace backticks as it breaks out of code blocks. Spaces seemed to be the most reasonable solution.
            # we hope it's not close to 2000
            paginator.add_line(content.replace('```', '`` `'))
            paginator.close_page()

        if message.content:
            add_content('Raw message', message.content)

        transformer = pprint.pformat if json else self.format_fields
        for field_name in ('embeds', 'attachments'):
            data = raw_data[field_name]

            if not data:
                continue

            total = len(data)
            for current, item in enumerate(data, start=1):
                title = f'Raw {field_name} ({current}/{total})'
                add_content(title, transformer(item))

        for page in paginator.pages:
            await ctx.send(page)
コード例 #19
0
ファイル: embeds.py プロジェクト: Kshitiz-Arya/dpytools
def paginate_to_embeds(
        description: str,
        title: Optional[str] = None,
        max_size: int = 2000,
        prefix: Optional[str] = "",
        suffix: Optional[str] = "",
        color: Union[discord.Color, int, None] = None) -> List[Embed]:
    """
    Facilitates sequential embed menus.
    Returned embeds share title, have description split at :max_length: and are added index/pages at footer to keep
    track of pages.

    Parameters
    ----------
    description: :class:`str`
        String to be split at :max_length: per embed.
    title: :class:`str`
        Shared by all embeds
    max_size: :class:`int`
        Maximum amount of characters per embed. Discord's limit is 2000.
    prefix: :class:`str`
        Defaults to "" it will be appended at the start of the description of each embed.
        Useful for codeblocks (use triple back quotes).
    suffix: :class:`str`
        Same as :prefix: but at the end of the text.
    color: :class:`Union[discord.Color, int, None]`
        color to use for the embed. Accepts int (decimal or hex) or discord.Color/discord.Colour.

    Returns
    -------
    The rendered list of embeds :class:`List[Embed]`
    """
    embeds = []
    to_list = description.split("\n")
    paginator = Paginator(prefix=prefix, suffix=suffix, max_size=max_size)
    for line in to_list:
        paginator.add_line(line)
    for i, page in enumerate(paginator.pages):
        embed = Embed(description=page).set_footer(
            text=f"page: {i + 1}/{len(paginator.pages)}")
        d = {'title': title, 'colour': color}
        for attr in d:
            if value := d[attr]:
                setattr(embed, attr, value)

        embeds.append(embed)
コード例 #20
0
ファイル: errorhandler.py プロジェクト: davfsa/nekosquared
async def _dm_me_error(*, bot, cog, ctx, error, event_method):
    embed = discord.Embed(
        title=f"An error occurred: `{type(error).__qualname__}`",
        description=f'Supplied error message: `{error or "—"}`',
        colour=0xFF0000,
    )

    trace = traceback.format_tb(error.__traceback__)
    trace = "".join(trace)
    should_pag = len(trace) > 1010

    if not should_pag:
        trace = f"```\n{trace}\n```"
        embed.add_field(name="Traceback", value=trace, inline=False)

    if ctx:
        whom_info = (
            f"{ctx.command.qualified_name} invoked as {ctx.invoked_with}\n"
            f"Invoked by: {ctx.author} (`{ctx.author.id}`)\n"
            f'{"Guild: " + str(ctx.guild) if ctx.guild else "in DMs"}\n'
            f"Channel: #{ctx.channel}\n"
            f"When: {ctx.message.created_at}"
        )
        body = ctx.message.content
        body = body.replace("`", "’")
        if len(body) > 1000:
            body = f"{body[:997]}..."
        body = f"```\n{body}\n```"
        embed.add_field(name="Command info", value=whom_info)
        embed.add_field(name="Command body", value=body)
    if cog:
        embed.add_field(name="Cog", value=str(cog))
    if event_method:
        embed.add_field(name="Event method", value=str(event_method))

    owner = bot.get_user(bot.owner_id)
    await owner.send(embed=embed)

    if should_pag:
        p = Paginator()
        for line in trace.split("\n"):
            p.add_line(line)
        for page in p.pages:
            # Send the last 15 only. Prevents a hell of a lot
            # of spam if the bot hits a stack overflow.
            await owner.send(page[-15:])
コード例 #21
0
ファイル: menus.py プロジェクト: Stormtorch002/HappyBot
    async def send_initial_message(self, ctx, channel):
        query = '''SELECT name
                   FROM tags
                   ORDER BY name
                '''
        res = await ctx.bot.db.fetch(query)
        if not res:
            await ctx.send('No tags found.')
            self.stop()

        paginator = Paginator(prefix=None, suffix=None, max_size=420)
        i = 1
        for name in [row['name'] for row in res]:
            paginator.add_line(f'**{i}.** {name}')
            i += 1
        self.pages = paginator.pages
        return await ctx.send(embed=self.embed)
コード例 #22
0
ファイル: awaiter.py プロジェクト: vZockeR1/RulesBot
    async def guild_role(self,
                         text: str,
                         check=lambda role: True,
                         list_ids=False) -> Role:
        async def converter(mes: Message):
            return discord.utils.get(self.guild.roles,
                                     id=int(
                                         mes.content.translate(digit_keeper)))

        if list_ids:
            guild: Guild = self.ctx.guild
            paginator = Paginator()
            for role in guild.roles:
                paginator.add_line(role.name + ' ' + str(role.id))

            for page in paginator.pages:
                await self.ctx.send(
                    embed=Embed(color=Color.blurple(), description=page))
        return await self.by_converter(text, check=check, converter=converter)
コード例 #23
0
    async def code(self, ctx: Context) -> None:
        """Return stats about the bot's code."""
        total = 0
        file_amount = 0
        list_of_files = []

        for filepath, _, files in os.walk("bot"):
            for name in files:
                if name.endswith(".py"):
                    file_lines = 0
                    file_amount += 1
                    with codecs.open(
                            "./" + str(pathlib.PurePath(filepath, name)), "r",
                            "utf-8") as file:
                        for _, line in enumerate(file):
                            if line.strip().startswith("#") or len(
                                    line.strip()) == 0:
                                pass
                            else:
                                total += 1
                                file_lines += 1

                    final_path = filepath + os.path.sep + name
                    list_of_files.append(
                        final_path.split("." + os.path.sep)[-1] +
                        f" : {file_lines} lines")

        paginator = Paginator(max_size=2048, )

        for line in list_of_files:
            paginator.add_line(line)

        pages = menus.MenuPages(
            source=CodeInfoSource(
                f"{self.bot.user.name}'s structure",
                f"I am made of {total} lines of Python, spread across {file_amount} files !",
                paginator.pages,
                per_page=1,
            ),
            clear_reactions_after=True,
        )
        await pages.start(ctx)
コード例 #24
0
ファイル: owner.py プロジェクト: AkiraSama/novelty
    async def eval(self, ctx: Context, *, code: str):
        """Owner evaluation.

        With great power comes great responsibility."""

        bot = self.bot  # noqa: F841
        eval_paginator = Paginator(prefix='```\n')

        try:
            out = str(eval(code))

            if not out:
                await ctx.send('\u200b')
                return

            for line in itertools.zip_longest(*([iter(out)] *
                                                self.MAX_LINE_SIZE)):
                eval_paginator.add_line(''.join(c for c in line if c).replace(
                    '```', '\\`\\`\\`'))

        except Exception as exception:
            eval_paginator.add_line(f"{type(exception).__name__}: {exception}")

        for page in eval_paginator.pages:
            await ctx.send(page)
コード例 #25
0
ファイル: embeds.py プロジェクト: fuyu78/dpytools
def paginate_to_embeds(
        title: str,
        description: str,
        max_size: int = 2000,
        prefix: Optional[str] = "",
        suffix: Optional[str] = "",
        color: Union[discord.Color, int, None] = None) -> List[Embed]:
    """
    Facilitates sequential embed menus.
    Returned embeds share title, have description split at :max_length: and are added index/pages at footer to keep
    track of pages.

    Args:
        title: Shared by all embeds
        description: String to be split at :max_length: per embed.
        max_size: Maximum amount of characters per embed. Discord's limit is 2000.
        prefix: Defaults to "" it will be appended at the start of the description of each embed.
                Useful for codeblocks (use triple back quotes).
        suffix: Same as :prefix: but at the end of the text.
        color: color to use for the embed. Accepts int (decimal or hex) or discord.Color/discord.Colour.

    Returns:
        List[Embed]
    """
    embeds = []
    to_list = description.split("\n")
    paginator = Paginator(prefix=prefix, suffix=suffix, max_size=max_size)
    for line in to_list:
        paginator.add_line(line)
    for i, page in enumerate(paginator.pages):
        embeds.append(
            Embed(
                title=title,
                description=page,
                color=color,
            ).set_footer(text=f"page: {i + 1}/{len(paginator.pages)}"))
    return embeds
コード例 #26
0
async def list_users(ctx: CommandContext, *, role: Role):
    author: Member = ctx.author
    roles: List[Role] = author.roles
    role_ids: Set[int] = set([role.id for role in roles])
    admin: bool = len(role_ids.intersection(config.admin_roles))
    if not admin and not check_is_mentor(author, role):
        return await ctx.send(
            embed=Embed(
                title='Missing permissions',
                color=Color.red(),
                description='You are neither an admin nor a mentor for this role.'))
    paginator = Paginator(prefix='', suffix='')
    for user in get_members_with_role(role):
        paginator.add_line(f'{user} - {user.mention}')
    for page in paginator.pages:
        await ctx.send(
            embed=Embed(
                title=f'Users with the role @{role.name}',
                description=page))
    if len(paginator.pages) == 0:
        await ctx.send(
            embed=Embed(
                title=f'Users with the role @{role.name}',
                description='No users found.'))
コード例 #27
0
    async def send_bot_help(self, mapping):
        '''The main bot help command (overridden)'''

        ## Initial setup
        self.paginator = Paginator()
        clip_groups = self.context.bot.get_cog("Clips").clip_groups

        self.dump_header_boilerplate()

        ## Dump the non-clip commands
        self.dump_commands()

        ## Dump the base clip commands
        clips_group = clip_groups["internet"]
        if (clips_group):
            self.dump_clip_group(clips_group)

        ## Dump the names of the additional clips. Don't print their commands because that's too much info.
        ## This is a help interface, not a CVS receipt
        self.dump_clip_categories(clip_groups)

        self.dump_footer_boilerplate(list(clip_groups.keys()))

        await self.send_pages()
コード例 #28
0
ファイル: formatter.py プロジェクト: TheSkyDomain/Modufur
async def paginate(ctx,
                   i,
                   start='',
                   prefix='',
                   kprefix='`',
                   ksuffix='`\n',
                   eprefix='```\n',
                   ejoin=' ',
                   esuffix='\n```',
                   suffix='',
                   end=''):
    paginator = Paginator(prefix=prefix, suffix=suffix)
    messages = []
    i = copy.deepcopy(i)

    if start:
        paginator.add_line(start + ('' if type(i) is not dict else '\n'))

    if type(i) in (tuple, list, set):
        if not i:
            i = (' ')
        paginator.add_line(eprefix + f'{ejoin}'.join(sorted(i)) + esuffix)
    elif type(i) is dict:
        if not i:
            i = {' ': ' '}
        for k, e in sorted(i.items()):
            paginator.add_line(kprefix + k + ksuffix + eprefix +
                               f'{ejoin}'.join(e) + esuffix)

    if end:
        paginator.add_line(end)

    for page in paginator.pages:
        messages.append(await ctx.send(page))

    return messages
コード例 #29
0
ファイル: information.py プロジェクト: gsdev215/bot
    async def send_raw_content(self,
                               ctx: Context,
                               message: Message,
                               json: bool = False) -> None:
        """
        Send information about the raw API response for a `discord.Message`.

        If `json` is True, send the information in a copy-pasteable Python format.
        """
        if ctx.author not in message.channel.members:
            await ctx.send(
                ":x: You do not have permissions to see the channel this message is in."
            )
            return

        # I *guess* it could be deleted right as the command is invoked but I felt like it wasn't worth handling
        # doing this extra request is also much easier than trying to convert everything back into a dictionary again
        raw_data = await ctx.bot.http.get_message(message.channel.id,
                                                  message.id)

        paginator = Paginator()

        def add_content(title: str, content: str) -> None:
            paginator.add_line(f'== {title} ==\n')
            # Replace backticks as it breaks out of code blocks.
            # An invisible character seemed to be the most reasonable solution. We hope it's not close to 2000.
            paginator.add_line(content.replace('`', '`\u200b'))
            paginator.close_page()

        if message.content:
            add_content('Raw message', message.content)

        transformer = pprint.pformat if json else self.format_fields
        for field_name in ('embeds', 'attachments'):
            data = raw_data[field_name]

            if not data:
                continue

            total = len(data)
            for current, item in enumerate(data, start=1):
                title = f'Raw {field_name} ({current}/{total})'
                add_content(title, transformer(item))

        for page in paginator.pages:
            await ctx.send(page, allowed_mentions=AllowedMentions.none())
コード例 #30
0
 def assemblePage(self):
     p = Paginator()
     for line in self.grid:
         p.add_line(''.join(line))
     return p.pages
コード例 #31
0
ファイル: over.py プロジェクト: EJH2/ViralBot-Discord
    async def format(self):
        """Handles the actual behaviour involved with formatting.

        To change the behaviour, this method should be overridden.

        Returns
        --------
        list
            A paginated output of the help command.
        """
        self._paginator = Paginator()

        # we need a padding of ~80 or so

        description = self.command.description if not self.is_cog() else inspect.getdoc(self.command)

        if description:
            # <description> portion
            self._paginator.add_line(description, empty=True)

        if isinstance(self.command, Command):
            # <signature portion>
            if self.command.params.get("args", None) and type(self.command.params['args'].annotation) == ArgPC:
                self.command.usage = create_help(self.command, self.command.params['args'].annotation.parser)
            signature = self.get_command_signature()
            self._paginator.add_line(signature, empty=True)

            # <long doc> section
            if self.command.help:
                self._paginator.add_line(self.command.help, empty=True)

            # end it here if it's just a regular command
            if not self.has_subcommands():
                self._paginator.close_page()
                return self._paginator.pages

        max_width = self.max_name_size

        def category(tup):
            """Splits the help command into categories for easier readability"""
            cog = tup[1].cog_name
            # we insert the zero width space there to give it approximate
            # last place sorting position.
            return cog + ':' if cog is not None else '\u200bNo Category:'

        filtered = await self.filter_command_list()
        if self.is_bot():
            data = sorted(filtered, key=category)
            for category, commands in itertools.groupby(data, key=category):
                # there simply is no prettier way of doing this.
                commands = sorted(commands)
                if len(commands) > 0:
                    self._paginator.add_line(category)

                self._add_subcommands_to_page(max_width, commands)
        else:
            filtered = sorted(filtered)
            if filtered:
                self._paginator.add_line('Commands:')
                self._add_subcommands_to_page(max_width, filtered)

        # add the ending note
        self._paginator.add_line()
        ending_note = self.get_ending_note()
        self._paginator.add_line(ending_note)
        return self._paginator.pages