async def _format_command_category(self, paginator: LinePaginator, category: str, cmds: List[Command]) -> None: cmds = sorted(cmds, key=lambda c: c.name) cat_cmds = [] for command in cmds: cat_cmds += await self._format_command(command) # state var for if the category should be added next print_cat = 1 new_page = True for details in cat_cmds: # keep details together, paginating early if it won"t fit lines_adding = len(details.split("\n")) + print_cat if paginator._linecount + lines_adding > self._max_lines: paginator._linecount = 0 new_page = True paginator.close_page() # new page so print category title again print_cat = 1 if print_cat: if new_page: paginator.add_line("") paginator.add_line(category) print_cat = 0 paginator.add_line(details)
async def _add_command_signature(self, paginator: LinePaginator) -> None: prefix = constants.Client.prefix signature = self._get_command_params(self.query) parent = self.query.full_parent_name + " " if self.query.parent else "" paginator.add_line(f"**```{prefix}{parent}{signature}```**") aliases = [ f"`{alias}`" if not parent else f"`{parent} {alias}`" for alias in self.query.aliases ] aliases += [ f"`{alias}`" for alias in getattr(self.query, "root_aliases", ()) ] aliases = ", ".join(sorted(aliases)) if aliases: paginator.add_line(f"**Can also use:** {aliases}\n") if not await self.query.can_run(self._ctx): paginator.add_line("***You cannot run this command.***\n")
async def build_pages(self) -> None: """Builds the list of content pages to be paginated through in the help message, as a list of str.""" # Use LinePaginator to restrict embed line height paginator = LinePaginator(prefix="", suffix="", max_lines=self._max_lines) # show signature if query is a command if isinstance(self.query, commands.Command): await self._add_command_signature(paginator) if isinstance(self.query, Cog): paginator.add_line(f"**{self.query.name}**") if self.description: paginator.add_line(f"*{self.description}*") # list all children commands of the queried object if isinstance(self.query, (commands.GroupMixin, Cog)): await self._list_child_commands(paginator) self._pages = paginator.pages
async def build_pages(self) -> None: """Builds the list of content pages to be paginated through in the help message, as a list of str.""" # Use LinePaginator to restrict embed line height paginator = LinePaginator(prefix='', suffix='', max_lines=self._max_lines) prefix = constants.Client.prefix # show signature if query is a command if isinstance(self.query, commands.Command): signature = self._get_command_params(self.query) parent = self.query.full_parent_name + ' ' if self.query.parent else '' paginator.add_line(f'**```{prefix}{parent}{signature}```**') aliases = ', '.join(f'`{a}`' for a in self.query.aliases) if aliases: paginator.add_line(f'**Can also use:** {aliases}\n') if not await self.query.can_run(self._ctx): paginator.add_line('***You cannot run this command.***\n') if isinstance(self.query, Cog): paginator.add_line(f'**{self.query.name}**') if self.description: paginator.add_line(f'*{self.description}*') # list all children commands of the queried object if isinstance(self.query, (commands.GroupMixin, Cog)): # remove hidden commands if session is not wanting hiddens if not self._show_hidden: filtered = [c for c in self.query.commands if not c.hidden] else: filtered = self.query.commands # if after filter there are no commands, finish up if not filtered: self._pages = paginator.pages return if isinstance(self.query, Cog): grouped = (('**Commands:**', self.query.commands), ) elif isinstance(self.query, commands.Command): grouped = (('**Subcommands:**', self.query.commands), ) # don't show prefix for subcommands prefix = '' # otherwise sort and organise all commands into categories else: cat_sort = sorted(filtered, key=self._category_key) grouped = itertools.groupby(cat_sort, key=self._category_key) for category, cmds in grouped: cmds = sorted(cmds, key=lambda c: c.name) if len(cmds) == 0: continue cat_cmds = [] for command in cmds: # skip if hidden and hide if session is set to if command.hidden and not self._show_hidden: continue # see if the user can run the command strikeout = '' # Patch to make the !help command work outside of #bot-commands again # This probably needs a proper rewrite, but this will make it work in # the mean time. try: can_run = await command.can_run(self._ctx) except CheckFailure: can_run = False if not can_run: # skip if we don't show commands they can't run if self._only_can_run: continue strikeout = '~~' signature = self._get_command_params(command) info = f"{strikeout}**`{prefix}{signature}`**{strikeout}" # handle if the command has no docstring if command.short_doc: cat_cmds.append(f'{info}\n*{command.short_doc}*') else: cat_cmds.append(f'{info}\n*No details provided.*') # state var for if the category should be added next print_cat = 1 new_page = True for details in cat_cmds: # keep details together, paginating early if it won't fit lines_adding = len(details.split('\n')) + print_cat if paginator._linecount + lines_adding > self._max_lines: paginator._linecount = 0 new_page = True paginator.close_page() # new page so print category title again print_cat = 1 if print_cat: if new_page: paginator.add_line('') paginator.add_line(category) print_cat = 0 paginator.add_line(details) self._pages = paginator.pages
async def disambiguate( ctx: Context, entries: List[str], *, timeout: float = 30, entries_per_page: int = 20, empty: bool = False, embed: discord.Embed = None ) -> str: """ Has the user choose between multiple entries in case one could not be chosen automatically. Disambiguation will be canceled after `timeout` seconds. This will raise a BadArgument if entries is empty, if the disambiguation event times out, or if the user makes an invalid choice. """ if len(entries) == 0: raise BadArgument('No matches found.') if len(entries) == 1: return entries[0] choices = (f'{index}: {entry}' for index, entry in enumerate(entries, start=1)) def check(message: discord.Message) -> bool: return (message.content.isdigit() and message.author == ctx.author and message.channel == ctx.channel) try: if embed is None: embed = discord.Embed() coro1 = ctx.bot.wait_for('message', check=check, timeout=timeout) coro2 = LinePaginator.paginate(choices, ctx, embed=embed, max_lines=entries_per_page, empty=empty, max_size=6000, timeout=9000) # wait_for timeout will go to except instead of the wait_for thing as I expected futures = [asyncio.ensure_future(coro1), asyncio.ensure_future(coro2)] done, pending = await asyncio.wait(futures, return_when=asyncio.FIRST_COMPLETED, loop=ctx.bot.loop) # :yert: result = list(done)[0].result() # Pagination was canceled - result is None if result is None: for coro in pending: coro.cancel() raise BadArgument('Canceled.') # Pagination was not initiated, only one page if result.author == ctx.bot.user: # Continue the wait_for result = await list(pending)[0] # Love that duplicate code for coro in pending: coro.cancel() except asyncio.TimeoutError: raise BadArgument('Timed out.') # Guaranteed to not error because of isdigit() in check index = int(result.content) try: return entries[index - 1] except IndexError: raise BadArgument('Invalid choice.')