Repository: python-discord/bot

Issue:
Truncate the charinfo embed to fit within 2048 characters
Sentry Issue: [BOT-3D](https://sentry.io/organizations/python-discord/issues/1621405078/?referrer=github_integration)

```
HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body
In embed.description: Must be 2048 or fewer in length.
  File "discord/ext/commands/core.py", line 83, in wrapped
    ret = await coro(*args, **kwargs)
  File "bot/cogs/utils.py", line 162, in charinfo
    await ctx.send(embed=embed)
  File "discord/abc.py", line 856, in send
    data = await state.http.send_message(channel.id, content, tts=tts, embed=embed, nonce=nonce)
  File "discord/http.py", line 225, in request
    raise HTTPException(r, data)

Error executing command invoked by <REDACTED>: !charinfo ﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹ
```


Discussion:
Perhaps we should come up with something more generic than editing individual embed generation code whenever an exception happens to be raised.
I actually think this should be paginated. I don't know in what situation we'd want to simply cut off embed text at a specific boundary, let alone at whatever arbitrary position happens to be character number 2048.
A related issue recently occurred while searching the infraction history of a user with an infraction message that presumably exceeded the limit.

Command: `!infr search ...` (specific user ID omitted)
Error: `RuntimeError: Line exceeds maximum page size 998`
I'll look into working on this issue when I get the chance (should be within the next couple of weeks or so), so I'll assign it to myself. Seems relatively simple enough and would be a good first PyDis issue for me to work on. If it needs to be fixed more immediately and I don't have the time, someone else can feel free to take over.
That other issue is only related in concept. The causes are in two different places.
> The causes are in two different places.

Yeah, but the fix should fairly similar for both, no? Other than the character limits differing, the problem seems to be virtually identical. If not, I can open a separate issue for the infraction messages (which happens to be the one I'm primarily interested in).

Edit: To clarify, I think it should be done in two separate PRs. But, we can probably group them in the same issue.
Maybe - I'm not sure. One is due to Discord's limitations on embeds (no paginator involved). The other is due to our configured max length for an page in a paginated embed.
> One is due to Discord's limitations on embeds (no paginator involved). The other is due to our configured max length for an page in a paginated embed.

Ah, okay. It seems that I likely misunderstood the original issue; I had assumed the solution to that would also involve pagination. For simplicity, I'll just open a separate issue for the infraction message problem and work on that separately. That's the one I'm primarily concerned about and more motivated to fix since it has a direct impact on PyDis moderation.

Edit: Here's the new issue: https://github.com/python-discord/bot/issues/926.

Resolving patch:
diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py
--- a/bot/cogs/utils.py
+++ b/bot/cogs/utils.py
@@ -12,6 +12,8 @@
 from bot.bot import Bot
 from bot.constants import Channels, MODERATION_ROLES, STAFF_ROLES
 from bot.decorators import in_whitelist, with_role
+from bot.pagination import LinePaginator
+from bot.utils import messages
 
 log = logging.getLogger(__name__)
 
@@ -117,25 +119,18 @@ async def pep_command(self, ctx: Context, pep_number: str) -> None:
     @command()
     @in_whitelist(channels=(Channels.bot_commands,), roles=STAFF_ROLES)
     async def charinfo(self, ctx: Context, *, characters: str) -> None:
-        """Shows you information on up to 25 unicode characters."""
+        """Shows you information on up to 50 unicode characters."""
         match = re.match(r"<(a?):(\w+):(\d+)>", characters)
         if match:
-            embed = Embed(
-                title="Non-Character Detected",
-                description=(
-                    "Only unicode characters can be processed, but a custom Discord emoji "
-                    "was found. Please remove it and try again."
-                )
+            return await messages.send_denial(
+                ctx,
+                "**Non-Character Detected**\n"
+                "Only unicode characters can be processed, but a custom Discord emoji "
+                "was found. Please remove it and try again."
             )
-            embed.colour = Colour.red()
-            await ctx.send(embed=embed)
-            return
 
-        if len(characters) > 25:
-            embed = Embed(title=f"Too many characters ({len(characters)}/25)")
-            embed.colour = Colour.red()
-            await ctx.send(embed=embed)
-            return
+        if len(characters) > 50:
+            return await messages.send_denial(ctx, f"Too many characters ({len(characters)}/50)")
 
         def get_info(char: str) -> Tuple[str, str]:
             digit = f"{ord(char):x}"
@@ -148,15 +143,14 @@ def get_info(char: str) -> Tuple[str, str]:
             info = f"`{u_code.ljust(10)}`: {name} - {utils.escape_markdown(char)}"
             return info, u_code
 
-        charlist, rawlist = zip(*(get_info(c) for c in characters))
-
-        embed = Embed(description="\n".join(charlist))
-        embed.set_author(name="Character Info")
+        char_list, raw_list = zip(*(get_info(c) for c in characters))
+        embed = Embed().set_author(name="Character Info")
 
         if len(characters) > 1:
-            embed.add_field(name='Raw', value=f"`{''.join(rawlist)}`", inline=False)
+            # Maximum length possible is 502 out of 1024, so there's no need to truncate.
+            embed.add_field(name='Full Raw Text', value=f"`{''.join(raw_list)}`", inline=False)
 
-        await ctx.send(embed=embed)
+        await LinePaginator.paginate(char_list, ctx, embed, max_lines=10, max_size=2000, empty=False)
 
     @command()
     async def zen(self, ctx: Context, *, search_value: Union[int, str, None] = None) -> None:


Test patch: