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)
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)
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)
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)
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)
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)
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) )
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)
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)
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
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)
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:])
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)
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)
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)
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
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.'))
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
async def invite(self, ctx): '''Posts invite links for Hawking, and its Discord server.''' self.dynamo_db.put(dynamo_manager.CommandItem( ctx, ctx.message.content, inspect.currentframe().f_code.co_name, True)) paginator = Paginator() paginator.add_line('Add Hawking to your server with this link: https://discordapp.com/oauth2/authorize?client_id=334894709292007424&scope=bot&permissions=53803072') paginator.close_page() paginator.add_line('Also, join my Discord server via: https://discord.gg/JJqx8C4') paginator.add_line('- Help me test unstable versions of Hawking and my other bots') paginator.add_line('- Let me know if something\'s broken') paginator.add_line('- Post suggestions for improving Hawking and my other bots') paginator.add_line('- Got a funny phrase you want added? Suggest it in there!') paginator.close_page() await self.send_pages(ctx, paginator)
class MyHelpFormatter(HelpFormatter): def _add_subcommands_to_page(self, max_width, commands, mobile_format): for name, command in commands: if name in command.aliases: # skip aliases continue if mobile_format: entry = ' {0}'.format(name) else: entry = ' {0:<{width}} {1}'.format(name, command.short_doc, width=max_width) shortened = self.shorten(entry) self._paginator.add_line(shortened) def format_help_for(self, context, command_or_bot, mobileFormat): self.context = context self.command = command_or_bot return self.format(mobileFormat) def format(self, mobileFormat): """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) elif self.command.brief: self._paginator.add_line(self.command.brief, 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 '\u200bNo Category:' if self.is_bot(): data = sorted(self.filter_command_list(), key=category) for category, commands in itertools.groupby(data, key=category): # there simply is no prettier way of doing this. commands = list(commands) if len(commands) > 0: self._paginator.add_line(category) self._add_subcommands_to_page(max_width, commands, mobileFormat) else: self._paginator.add_line('Commands:') self._add_subcommands_to_page(max_width, self.filter_command_list(), mobileFormat) if not mobileFormat: # add the ending note self._paginator.add_line() ending_note = self.get_ending_note() self._paginator.add_line(ending_note) return self._paginator.pages
def assemblePage(self): p = Paginator() for line in self.grid: p.add_line(''.join(line)) return p.pages
class ClipsterHelpCommand(commands.DefaultHelpCommand): @property def max_name_size(self): """ int : Returns the largest name length of the bot's commands. """ size = 0 try: commands = self.context.bot.commands if commands: size = max( map( lambda c: len(c.name) if self.show_hidden or not c.hidden else 0, commands)) except AttributeError as e: size = 15 return size + len(CONFIG_OPTIONS.get('activation_string', '')) def dump_header_boilerplate(self): """ Adds the header boilerplate text (Description, Version, How to activate) to the paginator """ self.paginator.add_line(CONFIG_OPTIONS.get("description"), empty=False) ## Append the version info into the help screen version_note = "Clipster version: {}".format( CONFIG_OPTIONS.get("version", "Beta")) self.paginator.add_line(version_note, empty=True) ## Append (additional) activation note activation_note = "Activate with the '{0}' character (ex. '{0}help')".format( self.clean_prefix) self.paginator.add_line(activation_note, empty=True) def dump_footer_boilerplate(self, categories): """ Adds the footer boilerplate text (Using the help interface) to the paginator """ # Ending note logic from HelpFormatter.format command_name = self.context.invoked_with ending_note = "Check out the other clip categories! Why not try '{0}{1} {2}'?".format( self.clean_prefix, command_name, random.choice(categories)) self.paginator.add_line(ending_note) def dump_commands(self): """ Adds information about the bot's available commands (unrelated to the clip commands) to the paginator """ self.paginator.add_line("Basic Commands:") for command in sorted(self.context.bot.commands, key=lambda cmd: cmd.name): if ((command.module != "clips" or command.name == 'random' or command.name == 'find') and not command.hidden): entry = ' {0}{1:<{width}} {2}'.format( CONFIG_OPTIONS.get('activation_string', ''), command.name, command.short_doc, width=self.max_name_size) self.paginator.add_line(self.shorten_text(entry)) self.paginator.add_line() def dump_clip_group(self, clip_group, width=None): """ Adds information about the supplied clip group (Group name, tabbed list of clip commands) to the paginator """ if (not width): width = self.max_name_size self.paginator.add_line(clip_group.name + ":") for name, clip in sorted(clip_group.clips.items(), key=lambda tup: tup[0]): entry = ' {0}{1:<{width}} {2}'.format(CONFIG_OPTIONS.get( 'activation_string', ''), name, clip.kwargs.get("help"), width=width) self.paginator.add_line(self.shorten_text(entry)) self.paginator.add_line() def dump_clip_categories(self, clip_groups, width=None): """ Adds information about the bot's clip categories, that the user can drill down into with the help interface, to the paginator """ if (not width): width = self.max_name_size help_string = '{}help '.format( CONFIG_OPTIONS.get('activation_string', '')) width += len(help_string) self.paginator.add_line('Clip Category Help:') for name, group in sorted(clip_groups.items(), key=lambda tup: tup[0]): ## Don't insert empty groups if (len(group.clips) > 0): entry = ' {0}{1:<{width}} {2}'.format(help_string, group.key, group.description, width=width) self.paginator.add_line(self.shorten_text(entry)) self.paginator.add_line() async def send_clip_category_help(self, command): '''Sends help information for a given command representing a Clip Category''' ## Initial setup max_width = self.max_name_size clip_groups = self.context.bot.get_cog("Clips").clip_groups self.dump_header_boilerplate() # self.dump_commands() self.dump_clip_group(clip_groups[command.name], max_width) self.dump_clip_categories(clip_groups, max_width) self.dump_footer_boilerplate(list(clip_groups.keys())) self.paginator.close_page() await self.send_pages() 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() 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()
class Formatter(HelpFormatter): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def _add_subcommands_to_page(self, max_width: int, commands: list): """ basically the same function from d.py but changed: - to make the helptext appear as a comment - to change the indentation to the PEP8 standard: 4 spaces """ for name, command in commands: if name in command.aliases: # skip aliases continue entry = " {0}{1:<{width}} # {2}".format(HELP_PREFIX, name, command.short_doc, width=max_width) shortened = self.shorten(entry) self._paginator.add_line(shortened) async def format(self): """ rewritten help command to make it more python-y example of specific command: async def <command>(ctx, <args>): \""" <help text> \""" await do_<command>(ctx, <args>) example of standard help page: class <cog1>: bot.<command1>() # <command1 help> class <cog2>: bot.<command2>() # <command2 help> # <ending help note> """ self._paginator = Paginator(prefix="```py") if isinstance(self.command, Command): # strip the command off bot. and () stripped_command = self.command.name.replace(HELP_PREFIX, "").replace("()", "") # get the args using the handy inspect module argspec = getfullargspec(self.command.callback) arguments = formatargspec(*argspec) for arg, annotation in argspec.annotations.items(): # remove module name to only show class name # discord.ext.commands.context.Context -> Context arguments = arguments.replace(f"{annotation.__module__}.", "") # manipulate the argspec to make it valid python when 'calling' the do_<command> args_no_type_hints = argspec.args for kwarg in argspec.kwonlyargs: args_no_type_hints.append("{0}={0}".format(kwarg)) args_no_type_hints = "({0})".format(", ".join(args_no_type_hints)) # remove self from the args arguments = arguments.replace("self, ", "") args_no_type_hints = args_no_type_hints.replace("self, ", "") # indent every line in the help message helptext = "\n ".join(self.command.help.split("\n")) # prepare the different sections of the help output, and add them to the paginator definition = f"async def {stripped_command}{arguments}:" doc_elems = [ '"""', helptext, '"""' ] docstring = "" for elem in doc_elems: docstring += f' {elem}\n' invocation = f" await do_{stripped_command}{args_no_type_hints}" self._paginator.add_line(definition) self._paginator.add_line(docstring) self._paginator.add_line(invocation) return self._paginator.pages max_width = self.max_name_size def category_check(tup): cog = tup[1].cog_name # zero width character to make it appear last when put in alphabetical order return cog if cog is not None else "\u200bNoCategory" command_list = await self.filter_command_list() data = sorted(command_list, key=category_check) for category, commands in itertools.groupby(data, key=category_check): commands = sorted(commands) if len(commands) > 0: self._paginator.add_line(f"class {category}:") self._add_subcommands_to_page(max_width, commands) self._paginator.add_line() ending_note = self.get_ending_note() # make the ending note appear as comments ending_note = "# "+ending_note.replace("\n", "\n# ") self._paginator.add_line(ending_note) return self._paginator.pages
class HelpFormatter(HelpF): """Custom override for the default help command""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._paginator = None 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
class HuskyHelpFormatter(DefaultHelpCommand): """ A modified help formatter that does some things differently. """ 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) def get_command_signature(self, command): parent = command.full_parent_name alias = command.name if not parent else parent + ' ' + command.name signature = command.signature + " " if command.signature else "" return '%s%s %s:: **%s**' % (self.clean_prefix, alias, signature, command.brief) def add_indented_commands(self, commands, *, heading, max_size=None): if not commands: return self.paginator.add_line(heading) max_size = max_size or self.get_max_size(commands) # noinspection PyProtectedMember get_width = discord.utils._string_width for command in commands: name = command.name width = max_size - (get_width(name) - len(name)) entry = '{0}{1:<{width}} :: {2}'.format(self.indent * ' ', name, command.short_doc, width=width) self.paginator.add_line(self.shorten_text(entry)) async def prepare_help_command(self, ctx, command): # The more upstream changes break my help formatter, the more I think I'm insane for making # the help formatter so terrible. # # Oh well. self.context = ctx await super().prepare_help_command(ctx, command) async def send_bot_help(self, mapping): ctx = self.context bot = ctx.bot if bot.description: # <description> portion self.paginator.add_line(bot.description, empty=True) no_category = '\u200b{0.no_category}:'.format(self) def get_category(command): cog = command.cog name = cog.qualified_name if cog is not None else no_category return name + "\n" + "-" * len(name) filtered = await self.filter_commands(bot.commands, sort=True, key=get_category) max_size = self.get_max_size(filtered) to_iterate = itertools.groupby(filtered, key=get_category) # Now we can add the commands to the page. for category, commands in to_iterate: commands = sorted( commands, key=lambda c: c.name) if self.sort_commands else list(commands) self.add_indented_commands(commands, heading=category, max_size=max_size) note = self.get_ending_note() if note: self.paginator.add_line() self.paginator.add_line(note) await self.send_pages() def add_command_formatting(self, command): """A utility function to format the non-indented block of commands and groups. Parameters ------------ command: :class:`Command` The command to format. """ if command.description: self.paginator.add_line(command.description, empty=True) signature = self.get_command_signature(command) self.paginator.add_line(signature, empty=True) if command.help: for line in command.help.splitlines(): if "<!nodoc>" in line: continue self.paginator.add_line(line) self.paginator.add_line()
class DogbotHelpFormatter(HelpFormatter): def get_ending_note(self): if self.context.bot.is_private: return super().get_ending_note() note = super().get_ending_note() invite = self.context.bot.cfg['bot']['woof']['invite'] return note if not self.is_bot() else note + '\nNeed help? Visit the support server: ' + invite def format_help(self, description): # for each paragraph in the description, replace a newline with a space. return '\n\n'.join(para.replace('\n', ' ') for para in description.split('\n\n')) 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
class NewHelpFormatter(commands.HelpFormatter): """ Much of the code in the following functions has been taken from https://github.com/Rapptz/discord.py and modified to suit the purposes of this bot. """ def get_max_alias_length(self): """Returns the longest list of aliases possible for formatting purposes.""" max_len = 0 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 '\u200bUncategorized:' if self.is_bot(): data = sorted(self.filter_command_list(), key=category) for category, commands in itertools.groupby(data, key=category): commands = list(commands) if len(commands) > 0: for name, command in commands: aliases = '|'.join(command.aliases) if len(aliases) > max_len: max_len = len(aliases) return (max_len) def _add_subcommands_to_page(self, max_width, max_alias_width, commands): """An overridden function that changes up the formatting of commands that are to be added to a paginator.""" for name, command in commands: if name in command.aliases: # skip aliases continue aliases = '|'.join(command.aliases) if len(aliases) > 0: entry = ' {0:<{width}} [{2:<{alias_width}} {1}'.format( name, command.short_doc, aliases + "]", width=max_width, alias_width=max_alias_width + 3) else: entry = ' {0:<{width}} {2:<{alias_width}} {1}'.format( name, command.short_doc, " ", width=max_width, alias_width=max_alias_width + 4) shortened = self.shorten(entry) self._paginator.add_line(shortened) def format(self): """An overridden function that adds a few little aesthetic changes to the default help commands""" self._paginator = Paginator() 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 + ':' if cog is not None else '\u200bUncategorized:' max_alias_length = self.get_max_alias_length() key_line = ' {0:<{width}} {2:<{alias_width}} {1}'.format( "Command", "Description", "Aliases", width=max_width, alias_width=max_alias_length + 4) self._paginator.add_line(key_line) bar_line = '{0:-<{key_line_len}}'.format("", key_line_len=self.width) self._paginator.add_line(bar_line) if self.is_bot(): data = sorted(self.filter_command_list(), key=category) for category, commands in itertools.groupby(data, key=category): # there simply is no prettier way of doing this. commands = list(commands) if len(commands) > 0: self._paginator.add_line(category) self._add_subcommands_to_page(max_width, max_alias_length, commands) else: self._paginator.add_line('Commands:') self._add_subcommands_to_page(max_width, self.filter_command_list()) # add the ending note self._paginator.add_line() ending_note = self.get_ending_note() self._paginator.add_line(ending_note) return self._paginator.pages
class CustomHelpFormatter(HelpFormatter): def __init__(self, show_hidden=False, show_check_failure=False, width=80): self.width = width self.show_hidden = show_hidden self.show_check_failure = show_check_failure 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 + ':' if cog is not None else '\u200bNo Category:' if self.is_bot(): data = sorted(self.filter_command_list(), key=category) for category, commands in itertools.groupby(data, key=category): # there simply is no prettier way of doing this. if category == '\u200bNo Category:': continue commands = list(commands) if len(commands) > 0: fmt = "{0}\n {1:>{width}}" doc = inspect.getdoc(commands[0][1].instance) if doc: self._paginator.add_line('') self._paginator.add_line(fmt.format(category, doc, width=max_width)) else: self._paginator.add_line('') self._paginator.add_line(fmt.format(category, 'No Description.', width=max_width)) else: self._paginator.add_line('Commands:') self._add_subcommands_to_page(max_width, self.filter_command_list()) # add the ending note self._paginator.add_line() ending_note = self.get_ending_note() self._paginator.add_line(ending_note) return self._paginator.pages
class NabHelpFormat(HelpFormatter): def get_ending_note(self): command_name = self.context.invoked_with if self.has_subcommands() and not self.is_bot(): return "Type {0} <subcommand> for more info on a command.".format( self.context.message.content) else: return "Type {0}{1} <command> for more info on a command.\n" \ "You can also type {0}{1} category for more info on a category.".format(self.clean_prefix, command_name) 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 + ':' 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) self._add_subcommands_to_page(max_width, self.filter_command_list()) if filtered: self._paginator.add_line( 'Subcommands:' if self.has_subcommands() else '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
class KiaraFormatter(HelpFormatter): def __init__(self): super().__init__() def get_ending_note(self): return '' @asyncio.coroutine 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(prefix='```md') # 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(f'[ {description} ][-!`.]', empty=True) if isinstance(self.command, Command): # <long doc> section if self.command.help: self._paginator.add_line(self.command.help, empty=True) # <signature portion> signature = self.get_command_signature() self._paginator.add_line(f"<Usage>") self._paginator.add_line(f"{signature}", 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 f'<{cog}>' if cog is not None else '<Other>' filtered = yield from 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 def get_command_signature(self): """Retrieves the signature portion of the help page.""" prefix = self.clean_prefix cmd = self.command return prefix + signature(cmd) def _add_subcommands_to_page(self, max_width, commands): for name, command in commands: if name in command.aliases: # skip aliases continue entry = ' {0:>{width}} : {1}'.format(name, command.short_doc, width=max_width) shortened = self.shorten(entry) self._paginator.add_line(shortened)