diff --git a/redbot/core/commands/commands.py b/redbot/core/commands/commands.py index 97649747c..fcd5c390e 100644 --- a/redbot/core/commands/commands.py +++ b/redbot/core/commands/commands.py @@ -4,6 +4,7 @@ This module contains extended classes and functions which are intended to replace those from the `discord.ext.commands` module. """ import inspect +import re import weakref from typing import Awaitable, Callable, Dict, List, Optional, Tuple, Union, TYPE_CHECKING @@ -57,6 +58,49 @@ class CogCommandMixin: checks=getattr(decorated, "__requires_checks__", []), ) + def format_help_for_context(self, ctx: "Context") -> str: + """ + This formats the help string based on values in context + + The steps are (currently, roughly) the following: + + - get the localized help + - substitute ``[p]`` with ``ctx.clean_prefix`` + - substitute ``[botname]`` with ``ctx.me.display_name`` + + More steps may be added at a later time. + + Cog creators may override this in their own command classes + as long as the method signature stays the same. + + Parameters + ---------- + ctx: Context + + Returns + ------- + str + Localized help with some formatting + """ + + help_str = self.help + if not help_str: + # Short circuit out on an empty help string + return help_str + + formatting_pattern = re.compile(r"\[p\]|\[botname\]") + + def replacement(m: re.Match) -> str: + s = m.group(0) + if s == "[p]": + return ctx.clean_prefix + if s == "[botname]": + return ctx.me.display_name + # We shouldnt get here: + return s + + return formatting_pattern.sub(replacement, help_str) + def allow_for(self, model_id: Union[int, str], guild_id: int) -> None: """Actively allow this command for the given model. diff --git a/redbot/core/commands/help.py b/redbot/core/commands/help.py index 13ce22d6e..783ef1dc5 100644 --- a/redbot/core/commands/help.py +++ b/redbot/core/commands/help.py @@ -162,10 +162,10 @@ class RedHelpFormatter: @staticmethod def get_default_tagline(ctx: Context): - return ( - f"Type {ctx.clean_prefix}help for more info on a command. " - f"You can also type {ctx.clean_prefix}help for more info on a category." - ) + return T_( + "Type {ctx.clean_prefix}help for more info on a command. " + "You can also type {ctx.clean_prefix}help for more info on a category." + ).format(ctx=ctx) async def format_command_help(self, ctx: Context, obj: commands.Command): @@ -187,7 +187,9 @@ class RedHelpFormatter: description = command.description or "" tagline = (await ctx.bot._config.help.tagline()) or self.get_default_tagline(ctx) - signature = f"`Syntax: {ctx.clean_prefix}{command.qualified_name} {command.signature}`" + signature = ( + f"`{T_('Syntax')}: {ctx.clean_prefix}{command.qualified_name} {command.signature}`" + ) subcommands = None if hasattr(command, "all_commands"): @@ -203,10 +205,11 @@ class RedHelpFormatter: emb["footer"]["text"] = tagline emb["embed"]["description"] = signature - if command.help: - splitted = command.help.split("\n\n") + command_help = command.format_help_for_context(ctx) + if command_help: + splitted = command_help.split("\n\n") name = splitted[0] - value = "\n\n".join(splitted[1:]).replace("[p]", ctx.clean_prefix) + value = "\n\n".join(splitted[1:]) if not value: value = EMPTY_STRING field = EmbedField(name[:250], value[:1024], False) @@ -225,9 +228,9 @@ class RedHelpFormatter: ) for i, page in enumerate(pagify(subtext, page_length=500, shorten_by=0)): if i == 0: - title = "**__Subcommands:__**" + title = T_("**__Subcommands:__**") else: - title = "**__Subcommands:__** (continued)" + title = T_("**__Subcommands:__** (continued)") field = EmbedField(title, page, False) emb["fields"].append(field) @@ -238,7 +241,7 @@ class RedHelpFormatter: subtext = None subtext_header = None if subcommands: - subtext_header = "Subcommands:" + subtext_header = T_("Subcommands:") max_width = max(discord.utils._string_width(name) for name in subcommands.keys()) def width_maker(cmds): @@ -261,7 +264,7 @@ class RedHelpFormatter: ( description, signature[1:-1], - command.help.replace("[p]", ctx.clean_prefix), + command.format_help_for_context(ctx), subtext_header, subtext, ), @@ -301,7 +304,10 @@ class RedHelpFormatter: page_char_limit = await ctx.bot._config.help.page_char_limit() page_char_limit = min(page_char_limit, 5500) # Just in case someone was manually... - author_info = {"name": f"{ctx.me.display_name} Help Menu", "icon_url": ctx.me.avatar_url} + author_info = { + "name": f"{ctx.me.display_name} {T_('Help Menu')}", + "icon_url": ctx.me.avatar_url, + } # Offset calculation here is for total embed size limit # 20 accounts for# *Page {i} of {page_count}* @@ -346,7 +352,9 @@ class RedHelpFormatter: embed = discord.Embed(color=color, **embed_dict["embed"]) if page_count > 1: - description = f"*Page {i} of {page_count}*\n{embed.description}" + description = T_( + "*Page {page_num} of {page_count}*\n{content_description}" + ).format(content_description=embed.description, page_num=i, page_count=page_count) embed.description = description embed.set_author(**author_info) @@ -366,7 +374,7 @@ class RedHelpFormatter: if not (coms or await ctx.bot._config.help.verify_exists()): return - description = obj.help + description = obj.format_help_for_context(ctx) tagline = (await ctx.bot._config.help.tagline()) or self.get_default_tagline(ctx) if await ctx.embed_requested(): @@ -376,7 +384,7 @@ class RedHelpFormatter: if description: splitted = description.split("\n\n") name = splitted[0] - value = "\n\n".join(splitted[1:]).replace("[p]", ctx.clean_prefix) + value = "\n\n".join(splitted[1:]) if not value: value = EMPTY_STRING field = EmbedField(name[:252], value[:1024], False) @@ -395,9 +403,9 @@ class RedHelpFormatter: ) for i, page in enumerate(pagify(command_text, page_length=500, shorten_by=0)): if i == 0: - title = "**__Commands:__**" + title = T_("**__Commands:__**") else: - title = "**__Commands:__** (continued)" + title = T_("**__Commands:__** (continued)") field = EmbedField(title, page, False) emb["fields"].append(field) @@ -407,7 +415,7 @@ class RedHelpFormatter: subtext = None subtext_header = None if coms: - subtext_header = "Commands:" + subtext_header = T_("Commands:") max_width = max(discord.utils._string_width(name) for name in coms.keys()) def width_maker(cmds): @@ -449,7 +457,7 @@ class RedHelpFormatter: if cog_name: title = f"**__{cog_name}:__**" else: - title = f"**__No Category:__**" + title = f"**__{T_('No Category')}:__**" def shorten_line(a_line: str) -> str: if len(a_line) < 70: # embed max width needs to be lower @@ -462,7 +470,7 @@ class RedHelpFormatter: ) for i, page in enumerate(pagify(cog_text, page_length=1000, shorten_by=0)): - title = title if i < 1 else f"{title} (continued)" + title = title if i < 1 else f"{title} {T_('(continued)')}" field = EmbedField(title, page, False) emb["fields"].append(field) @@ -478,7 +486,7 @@ class RedHelpFormatter: names.extend(list(v.name for v in v.values())) max_width = max( - discord.utils._string_width((name or "No Category:")) for name in names + discord.utils._string_width((name or T_("No Category:"))) for name in names ) def width_maker(cmds): @@ -492,7 +500,7 @@ class RedHelpFormatter: for cog_name, data in coms: - title = f"{cog_name}:" if cog_name else "No Category:" + title = f"{cog_name}:" if cog_name else T_("No Category:") to_join.append(title) for name, doc, width in width_maker(sorted(data.items())): @@ -543,7 +551,9 @@ class RedHelpFormatter: if fuzzy_commands: ret = await format_fuzzy_results(ctx, fuzzy_commands, embed=use_embeds) if use_embeds: - ret.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url) + ret.set_author( + name=f"{ctx.me.display_name} {T_('Help Menu')}", icon_url=ctx.me.avatar_url + ) tagline = (await ctx.bot._config.help.tagline()) or self.get_default_tagline(ctx) ret.set_footer(text=tagline) await ctx.send(embed=ret) @@ -553,7 +563,9 @@ class RedHelpFormatter: ret = T_("Help topic for *{command_name}* not found.").format(command_name=help_for) if use_embeds: ret = discord.Embed(color=(await ctx.embed_color()), description=ret) - ret.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url) + ret.set_author( + name=f"{ctx.me.display_name} {T_('Help Menu')}", icon_url=ctx.me.avatar_url + ) tagline = (await ctx.bot._config.help.tagline()) or self.get_default_tagline(ctx) ret.set_footer(text=tagline) await ctx.send(embed=ret) @@ -569,7 +581,9 @@ class RedHelpFormatter: ) if await ctx.embed_requested(): ret = discord.Embed(color=(await ctx.embed_color()), description=ret) - ret.set_author(name=f"{ctx.me.display_name} Help Menu", icon_url=ctx.me.avatar_url) + ret.set_author( + name=f"{ctx.me.display_name} {T_('Help Menu')}", icon_url=ctx.me.avatar_url + ) tagline = (await ctx.bot._config.help.tagline()) or self.get_default_tagline(ctx) ret.set_footer(text=tagline) await ctx.send(embed=ret)