aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/cogs/information.py86
1 files changed, 83 insertions, 3 deletions
diff --git a/bot/cogs/information.py b/bot/cogs/information.py
index 1afb37103..19f3bf7e6 100644
--- a/bot/cogs/information.py
+++ b/bot/cogs/information.py
@@ -1,13 +1,19 @@
import colorsys
import logging
+import pprint
import textwrap
import typing
+from collections import Mapping
+from typing import Any, Optional
-from discord import CategoryChannel, Colour, Embed, Member, Role, TextChannel, VoiceChannel, utils
-from discord.ext.commands import Bot, Cog, Context, command
+import discord
+from discord import CategoryChannel, Colour, Embed, Member, TextChannel, VoiceChannel
+from discord import Role, utils
+from discord.ext import commands
+from discord.ext.commands import Bot, Cog, Context, command, group
from bot.constants import Channels, Emojis, MODERATION_ROLES, STAFF_ROLES
-from bot.decorators import InChannelCheckFailure, with_role
+from bot.decorators import InChannelCheckFailure, with_role, in_channel
from bot.utils.checks import with_role_check
from bot.utils.time import time_since
@@ -229,6 +235,80 @@ class Information(Cog):
await ctx.send(embed=embed)
+ def format_fields(self, mapping: Mapping[str, Any], field_width: Optional[int] = None):
+ # sorting is technically superfluous but nice if you want to look for a specific field
+ fields = sorted(mapping.items(), key=lambda item: item[0])
+
+ if field_width is None:
+ field_width = len(max(mapping.keys(), key=len))
+
+ out = ''
+
+ for key, val in fields:
+ if isinstance(val, dict):
+ # if we have dicts inside dicts we want to apply the same treatment to the inner dictionaries
+ inner_width = int(field_width * 1.6)
+ val = '\n' + self.format_fields(val, field_width=inner_width)
+
+ elif isinstance(val, str):
+ # split up text since it might be long
+ text = textwrap.fill(val, width=100, replace_whitespace=False)
+
+ # indent it, I guess you could do this with `wrap` and `join` but this is nicer
+ val = textwrap.indent(text, ' ' * (field_width + len(': ')))
+
+ # the first line is already indented so we `str.lstrip` it
+ val = val.lstrip()
+
+ if key == 'color':
+ # makes the base 10 representation of a hex number readable to humans
+ val = hex(val)
+
+ out += '{0:>{width}}: {1}\n'.format(key, val, width=field_width)
+
+ # remove trailing whitespace
+ return out.rstrip()
+
+ @group(invoke_without_command=True)
+ @in_channel(Channels.bot, bypass_roles=STAFF_ROLES)
+ async def raw(self, ctx: Context, *, message: discord.Message, json: bool = False):
+ """Shows information about the raw API response."""
+
+ # I *guess* it could be deleted right as the command is invoked but I felt like it wasn't worth handling
+ # doing this extra request is also much easier than trying to convert everything back into a dictionary again
+ raw_data = await ctx.bot.http.get_message(message.channel.id, message.id)
+
+ paginator = commands.Paginator()
+
+ def add_content(title, content):
+ paginator.add_line(f'== {title} ==\n')
+ # replace backticks as it breaks out of code blocks. Spaces seemed to be the most reasonable solution.
+ # we hope it's not close to 2000
+ paginator.add_line(content.replace('```', '`` `'))
+ paginator.close_page()
+
+ if message.content:
+ add_content('Raw message', message.content)
+
+ transformer = pprint.pformat if json else self.format_fields
+ for field_name in 'embeds attachments'.split():
+ data = raw_data[field_name]
+
+ if not data:
+ continue
+
+ total = len(data)
+ for current, item in enumerate(data, start=1):
+ title = f'Raw {field_name} ({current}/{total})'
+ add_content(title, transformer(item))
+
+ for page in paginator.pages:
+ await ctx.send(page)
+
+ @raw.command()
+ async def json(self, ctx: Context, message: discord.Message):
+ await ctx.invoke(self.raw, message=message, json=True)
+
def setup(bot: Bot) -> None:
"""Information cog load."""