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 ﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹﯹ
```

Changed files:
bot/cogs/utils.py

diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py
index 697bf60c..017f3419 100644
--- a/bot/cogs/utils.py
+++ b/bot/cogs/utils.py
@@ -12,6 +12,8 @@ from discord.ext.commands import BadArgument, Cog, Context, command
 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 @@ class Utils(Cog):
     @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 @@ class Utils(Cog):
             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: