From 8b18526f46ed46c4d0ac27097e87003b48495e25 Mon Sep 17 00:00:00 2001 From: Michael H Date: Thu, 26 Dec 2019 16:53:03 -0500 Subject: [PATCH] [Help] Fix embed size calculation for additional text (#3208) * Fix size calculation for additional text * changelog * ffs * because of course that's a thing * *sigh* * prevent an edge case * more * ... * ... --- changelog.d/3208.bugfix.rst | 1 + redbot/core/commands/help.py | 59 +++++++++++++++++++++++++++++------- redbot/core/core_commands.py | 12 ++++---- 3 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 changelog.d/3208.bugfix.rst diff --git a/changelog.d/3208.bugfix.rst b/changelog.d/3208.bugfix.rst new file mode 100644 index 000000000..548dc9330 --- /dev/null +++ b/changelog.d/3208.bugfix.rst @@ -0,0 +1 @@ +Fixes a way for help to end up a little too large for discord limits diff --git a/redbot/core/commands/help.py b/redbot/core/commands/help.py index a4a4f1c32..cc3e087e3 100644 --- a/redbot/core/commands/help.py +++ b/redbot/core/commands/help.py @@ -108,7 +108,7 @@ class RedHelpFormatter: While currently, there is a global formatter, later plans include a context specific formatter selector as well as an API for cogs to register/un-register a formatter with the bot. - When implementing your own formatter, at minimum you must provide an implementation of + When implementing your own formatter, at minimum you must provide an implementation of `send_help` with identical signature. While this exists as a class for easy partial overriding, most implementations @@ -116,9 +116,9 @@ class RedHelpFormatter: """ async def send_help(self, ctx: Context, help_for: HelpTarget = None): - """ - This delegates to other functions. - + """ + This delegates to other functions. + For most cases, you should use this and only this directly. """ if help_for is None or isinstance(help_for, dpy_commands.bot.BotBase): @@ -271,16 +271,23 @@ class RedHelpFormatter: @staticmethod def group_embed_fields(fields: List[EmbedField], max_chars=1000): + curr_group = [] ret = [] + current_count = 0 + for f in fields: - curr_group.append(f) - if sum(len(f.value) for f in curr_group) > max_chars: + f_len = len(f.value) + len(f.name) + if curr_group and (f_len + current_count > max_chars): ret.append(curr_group) curr_group = [] - - if len(curr_group) > 0: - ret.append(curr_group) + current_count = 0 + curr_group.append(f) + current_count += f_len + else: + # Loop cleanup here + if curr_group: + ret.append(curr_group) return ret @@ -289,13 +296,43 @@ class RedHelpFormatter: pages = [] page_char_limit = await ctx.bot._config.help.page_char_limit() + page_char_limit = min(page_char_limit, 5990) # Just in case someone was manually... + + author_info = {"name": f"{ctx.me.display_name} 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}* + offset = len(author_info["name"]) + 20 + foot_text = embed_dict["footer"]["text"] + if foot_text: + offset += len(foot_text) + offset += len(embed_dict["embed"]["description"]) + offset += len(embed_dict["embed"]["title"]) + + # In order to only change the size of embeds when neccessary for this rather + # than change the existing behavior for people uneffected by this + # we're only modifying the page char limit should they be impacted. + # We could consider changing this to always just subtract the offset, + # But based on when this is being handled (very end of 3.2 release) + # I'd rather not stick a major visual behavior change in at the last moment. + if page_char_limit + offset > 5990: + # This is still neccessary with the max interaction above + # While we could subtract 100% of the time the offset from page_char_limit + # the intent here is to shorten again + # *only* when neccessary, by the exact neccessary amount + # To retain a visual match with prior behavior. + page_char_limit = 5990 - offset + elif page_char_limit < 250: + # Prevents an edge case where a combination of long cog help and low limit + # Could prevent anything from ever showing up. + # This lower bound is safe based on parts of embed in use. + page_char_limit = 250 + field_groups = self.group_embed_fields(embed_dict["fields"], page_char_limit) color = await ctx.embed_color() page_count = len(field_groups) - author_info = {"name": f"{ctx.me.display_name} Help Menu", "icon_url": ctx.me.avatar_url} - if not field_groups: # This can happen on single command without a docstring embed = discord.Embed(color=color, **embed_dict["embed"]) embed.set_author(**author_info) diff --git a/redbot/core/core_commands.py b/redbot/core/core_commands.py index c3a6c1875..a46087059 100644 --- a/redbot/core/core_commands.py +++ b/redbot/core/core_commands.py @@ -1313,14 +1313,14 @@ class Core(commands.Cog, CoreLogic): This setting only applies to embedded help. - Please note that setting a relitavely small character limit may - mean some pages will exceed this limit. This is because categories - are never spread across multiple pages in the help message. + The default value is 1000 characters. The minimum value is 500. + The maximum is based on the lower of what you provide and what discord allows. - The default value is 1000 characters. + Please note that setting a relatively small character limit may + mean some pages will exceed this limit. """ - if limit <= 0: - await ctx.send(_("You must give a positive value!")) + if limit < 500: + await ctx.send(_("You must give a value of at least 500 characters.")) return await ctx.bot._config.help.page_char_limit.set(limit)