Skip to content

Commit 59b342b

Browse files
authored
Adds a crude debug command (#1110)
* Adds a crude debug command * Make some changes * Formatting * Formatting * More formatting * Even more formatting
1 parent eb97d64 commit 59b342b

File tree

1 file changed

+184
-0
lines changed

1 file changed

+184
-0
lines changed

techsupport_bot/commands/debug.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
"""
2+
This is a development and issue tracking command designed to dump all attributes
3+
of a given object type.
4+
Current supported is message, member and channel
5+
"""
6+
7+
from __future__ import annotations
8+
9+
from typing import TYPE_CHECKING, Self
10+
11+
import discord
12+
import ui
13+
from core import auxiliary, cogs
14+
from discord import app_commands
15+
16+
if TYPE_CHECKING:
17+
import bot
18+
19+
20+
async def setup(bot: bot.TechSupportBot) -> None:
21+
"""Registers the debugger cog
22+
23+
Args:
24+
bot (bot.TechSupportBot): The bot to register the cog to
25+
"""
26+
await bot.add_cog(Debugger(bot=bot))
27+
28+
29+
class Debugger(cogs.BaseCog):
30+
"""The cog that holds the slowmode commands and helper functions
31+
32+
Attributes:
33+
debug_group (app_commands.Group): The group for the /debug commands
34+
"""
35+
36+
debug_group: app_commands.Group = app_commands.Group(
37+
name="debug", description="...", extras={"module": "debug"}
38+
)
39+
40+
@app_commands.check(auxiliary.bot_admin_check_interaction)
41+
@debug_group.command(
42+
name="message",
43+
description="Searches and displays all the message properties",
44+
extras={
45+
"module": "debug",
46+
},
47+
)
48+
async def debug_message(
49+
self: Self,
50+
interaction: discord.Interaction,
51+
channel: discord.TextChannel,
52+
id: str,
53+
) -> None:
54+
"""Searches for a message by ID in the given channel.
55+
56+
Args:
57+
interaction (discord.Interaction): The interaction that called this command
58+
channel (discord.TextChannel): The channel to find the message in
59+
id (str): The ID of the message to search for
60+
"""
61+
await interaction.response.defer(ephemeral=False)
62+
message = await channel.fetch_message(str(id))
63+
embeds = build_debug_embed(message)
64+
65+
view = ui.PaginateView()
66+
await view.send(interaction.channel, interaction.user, embeds, interaction)
67+
68+
@app_commands.check(auxiliary.bot_admin_check_interaction)
69+
@debug_group.command(
70+
name="member",
71+
description="Searches and displays all the member properties",
72+
extras={
73+
"module": "debug",
74+
},
75+
)
76+
async def debug_member(
77+
self: Self, interaction: discord.Interaction, member: discord.Member
78+
) -> None:
79+
"""Displays attributes for a member of the guild where the command was run.
80+
81+
Args:
82+
interaction (discord.Interaction): The interaction that called this command
83+
member (discord.Member): The member to search for information on
84+
"""
85+
await interaction.response.defer(ephemeral=False)
86+
embeds = build_debug_embed(member)
87+
view = ui.PaginateView()
88+
await view.send(interaction.channel, interaction.user, embeds, interaction)
89+
90+
@app_commands.check(auxiliary.bot_admin_check_interaction)
91+
@debug_group.command(
92+
name="channel",
93+
description="Searches and displays all the channel properties",
94+
extras={
95+
"module": "debug",
96+
},
97+
)
98+
async def debug_channel(
99+
self: Self, interaction: discord.Interaction, channel: discord.abc.GuildChannel
100+
) -> None:
101+
"""Displays attributes for a channel of the guild where the command was run.
102+
103+
Args:
104+
interaction (discord.Interaction): The interaction that called this command
105+
channel (discord.abc.GuildChannel): The channel to search for information on
106+
"""
107+
await interaction.response.defer(ephemeral=False)
108+
embeds = build_debug_embed(channel)
109+
view = ui.PaginateView()
110+
await view.send(interaction.channel, interaction.user, embeds, interaction)
111+
112+
113+
def build_debug_embed(discord_object: object) -> list[discord.Embed]:
114+
"""Builds a list of embeds, with each one at a max of 4000 characters
115+
This will be every attribute of the given object.
116+
117+
Args:
118+
discord_object (object): A discord object that needs to be explored
119+
120+
Returns:
121+
list[discord.Embed]: A list of embeds to be displayed in a paginated display
122+
"""
123+
all_strings = []
124+
properties_string = ""
125+
126+
for attribute in dir(discord_object):
127+
if not attribute.startswith("_"):
128+
try:
129+
value = getattr(discord_object, attribute)
130+
except AttributeError:
131+
continue
132+
133+
temp_string = f"**{attribute}:** {value}\n"
134+
if temp_string.startswith(f"**{attribute}:** <bound method"):
135+
continue
136+
137+
all_add_strings = format_attribute_chunks(
138+
attribute=attribute, value=str(value)
139+
)
140+
for print_string in all_add_strings:
141+
142+
if (len(properties_string) + len(print_string)) > 1500:
143+
all_strings.append(properties_string)
144+
properties_string = ""
145+
146+
properties_string += print_string
147+
148+
all_strings.append(properties_string)
149+
150+
embeds = []
151+
for string in all_strings:
152+
embeds.append(discord.Embed(description=string[:4000]))
153+
154+
return embeds
155+
156+
157+
def format_attribute_chunks(attribute: str, value: str) -> list[str]:
158+
"""This makes a simple paginated split fields, to break up long attributes
159+
160+
Args:
161+
attribute (str): The name of the attribute to be formatted.
162+
value (str): The string representation of the attribute
163+
164+
Returns:
165+
list[str]: A list of attributes, split if needed
166+
"""
167+
168+
def make_chunk_label(index: int, total: int) -> str:
169+
return f"**{attribute} ({index}):** " if total > 1 else f"**{attribute}:** "
170+
171+
max_length = 750
172+
173+
temp_prefix = f"**{attribute} (999):** "
174+
chunk_size = max_length - len(temp_prefix) - 1 # Reserve space for prefix and \n
175+
176+
# Create raw chunks of value
177+
raw_chunks = [value[i : i + chunk_size] for i in range(0, len(value), chunk_size)]
178+
total_chunks = len(raw_chunks)
179+
180+
# Format each chunk with appropriate prefix
181+
return [
182+
f"{make_chunk_label(i + 1, total_chunks)}{chunk}\n"
183+
for i, chunk in enumerate(raw_chunks)
184+
]

0 commit comments

Comments
 (0)