Repository: Cog-Creators/Red-DiscordBot

Issue:
[Mod] userinfo Invalid Form Body in role section (to many characters)
# Command bugs

#### Command name

userinfo

#### What cog is this command from?

mod

#### What were you expecting to happen?

for userinfo to output the info of the user

#### What actually happened?

an error
```py
Exception in command 'userinfo'
Traceback (most recent call last):
  File "/home/redbot/.pyenv/versions/redbot/lib/python3.7/site-packages/discord/ext/commands/core.py", line 79, in wrapped
    ret = await coro(*args, **kwargs)
  File "/home/redbot/.pyenv/versions/redbot/lib/python3.7/site-packages/redbot/cogs/mod/names.py", line 164, in userinfo
    await ctx.send(embed=data)
  File "/home/redbot/.pyenv/versions/redbot/lib/python3.7/site-packages/redbot/core/commands/context.py", line 64, in send
    return await super().send(content=content, **kwargs)
  File "/home/redbot/.pyenv/versions/redbot/lib/python3.7/site-packages/discord/abc.py", line 823, in send
    data = await state.http.send_message(channel.id, content, tts=tts, embed=embed, nonce=nonce)
  File "/home/redbot/.pyenv/versions/redbot/lib/python3.7/site-packages/discord/http.py", line 222, in request
    raise HTTPException(r, data)
discord.errors.HTTPException: 400 BAD REQUEST (error code: 50035): Invalid Form Body
In embed.fields.2.value: Must be 1024 or fewer in length.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/redbot/.pyenv/versions/redbot/lib/python3.7/site-packages/discord/ext/commands/bot.py", line 863, in invoke
    await ctx.command.invoke(ctx)
  File "/home/redbot/.pyenv/versions/redbot/lib/python3.7/site-packages/discord/ext/commands/core.py", line 728, in invoke
    await injected(*ctx.args, **ctx.kwargs)
  File "/home/redbot/.pyenv/versions/redbot/lib/python3.7/site-packages/discord/ext/commands/core.py", line 88, in wrapped
    raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: HTTPException: 400 BAD REQUEST (error code: 50035): Invalid Form Body
In embed.fields.2.value: Must be 1024 or fewer in length.
```

#### How can we reproduce this issue?

1. Create a cancerous amount of roles (50)
2. Give all roles to desired user
3. Get userinfo from that user
<details>
  <summary>Images</summary>
  
![image](https://user-images.githubusercontent.com/39572468/62189084-5a368980-b32b-11e9-86c0-dded94161021.png)
![image](https://user-images.githubusercontent.com/39572468/62189092-5f93d400-b32b-11e9-91cd-034de2e01f06.png)
![image](https://user-images.githubusercontent.com/39572468/62189099-64588800-b32b-11e9-81e3-ce84250419cf.png)
![image](https://user-images.githubusercontent.com/39572468/62189135-7cc8a280-b32b-11e9-8a9a-0f9cb7d67834.png)
  
</details>




Discussion:
Oh boy. Rather than attempt embed pagination, I think there's a certain point where it's acceptable to tell the server owner we wont support displaying that many roles in user info.



Resolving patch:
diff --git a/redbot/cogs/mod/names.py b/redbot/cogs/mod/names.py
--- a/redbot/cogs/mod/names.py
+++ b/redbot/cogs/mod/names.py
@@ -125,15 +125,46 @@ async def userinfo(self, ctx, *, user: discord.Member = None):
             activity = _("Watching {}").format(user.activity.name)
 
         if roles:
-            roles = ", ".join([x.mention for x in roles])
+
+            role_str = ", ".join([x.mention for x in roles])
+            # 400 BAD REQUEST (error code: 50035): Invalid Form Body
+            # In embed.fields.2.value: Must be 1024 or fewer in length.
+            if len(role_str) > 1024:
+                # Alternative string building time.
+                # This is not the most optimal, but if you're hitting this, you are losing more time
+                # to every single check running on users than the occasional user info invoke
+                # We don't start by building this way, since the number of times we hit this should be
+                # infintesimally small compared to when we don't across all uses of Red.
+                continuation_string = _(
+                    "and {numeric_number} more roles not displayed due to embed limits."
+                )
+                available_length = 1024 - len(continuation_string)  # do not attempt to tweak, i18n
+
+                role_chunks = []
+                remaining_roles = 0
+
+                for r in roles:
+                    chunk = f"{r.mention}, "
+                    chunk_size = len(chunk)
+
+                    if chunk_size < available_length:
+                        available_length -= chunk_size
+                        role_chunks.append(chunk)
+                    else:
+                        remaining_roles += 1
+
+                role_chunks.append(continuation_string.format(numeric_number=remaining_roles))
+
+                role_str = "".join(role_chunks)
+
         else:
-            roles = None
+            role_str = None
 
         data = discord.Embed(description=activity, colour=user.colour)
         data.add_field(name=_("Joined Discord on"), value=created_on)
         data.add_field(name=_("Joined this server on"), value=joined_on)
-        if roles is not None:
-            data.add_field(name=_("Roles"), value=roles, inline=False)
+        if role_str is not None:
+            data.add_field(name=_("Roles"), value=role_str, inline=False)
         if names:
             # May need sanitizing later, but mentions do not ping in embeds currently
             val = filter_invites(", ".join(names))


Test patch: