Debug ext
debug_application_cmd
¶
class
DebugAppCMD (Extension)
¶
Source code in naff/ext/debug_extension/debug_application_cmd.py
class DebugAppCMD(Extension):
def __init__(self, bot: Client) -> None:
self.add_ext_check(checks.is_owner())
@slash_command(
"debug",
sub_cmd_name="internal_info",
sub_cmd_description="Get Information about registered app commands",
**app_cmds_def,
)
async def app_cmd(self, ctx: InteractionContext) -> None:
await ctx.defer()
e = debug_embed("Application-Commands Cache")
cmds = 0
for v in self.bot.interactions.values():
cmds += len(v.keys())
e.add_field("Local application cmds (incld. Subcommands)", str(cmds))
e.add_field("Component callbacks", str(len(self.bot._component_callbacks)))
e.add_field("Prefixed commands", str(len(self.bot.prefixed_commands)))
e.add_field(
"Tracked Scopes", str(len(Counter(scope for scope in self.bot._interaction_scopes.values()).keys()))
)
await ctx.send(embeds=[e])
@app_cmd.subcommand(
"lookup",
sub_cmd_description="Search for a specified command and get its json representation",
**app_cmds_def,
)
@slash_option("cmd_id", "The ID of the command you want to lookup", opt_type=OptionTypes.STRING, required=True)
@slash_option(
"scope",
"The scope ID of the command, if you want to search for the cmd on remote",
opt_type=OptionTypes.STRING,
required=True,
)
@slash_option(
"remote",
"Should we search locally or remote for this command (default local)",
opt_type=OptionTypes.BOOLEAN,
required=False,
)
async def cmd_lookup(
self, ctx: InteractionContext, cmd_id: str = None, scope: str = None, remote: bool = False
) -> Optional[Message]:
await ctx.defer()
try:
cmd_id = int(cmd_id.strip())
scope = int(scope.strip())
# search internal registers for command
async def send(cmd_json: dict) -> None:
await ctx.send(
file=File(io.BytesIO(pprint.pformat(cmd_json, 2).encode("utf-8")), f"{cmd_json.get('name')}.json")
)
if not remote:
data = application_commands_to_dict(self.bot.interactions)[scope]
cmd_obj = self.bot.get_application_cmd_by_id(cmd_id)
for cmd in data:
if cmd["name"] == cmd_obj.name:
return await send(cmd)
else:
data = await self.bot.http.get_application_commands(self.bot.app.id, scope)
try:
perm_scope = scope
if scope == GLOBAL_SCOPE:
perm_scope = ctx.guild_id
perms = await self.bot.http.get_application_command_permissions(self.bot.app.id, perm_scope, cmd_id)
except HTTPException:
perms = None
for cmd in data:
if int(cmd["id"]) == cmd_id:
if perms:
cmd["permissions"] = perms.get("permissions")
return await send(cmd)
except Exception: # noqa: S110
pass
return await ctx.send(f"Unable to locate any commands in {scope} with ID {cmd_id}!")
@app_cmd.subcommand(
"list_scope",
sub_cmd_description="List all synced commands in a specified scope",
**app_cmds_def,
)
@slash_option(
"scope",
"The scope ID of the command, if it is not registered in the bot (0 for global)",
opt_type=OptionTypes.STRING,
required=True,
)
async def list_scope(self, ctx: InteractionContext, scope: str) -> Message:
await ctx.defer()
try:
cmds = await self.bot.http.get_application_commands(self.bot.app.id, int(scope.strip()))
if cmds:
e = debug_embed("Application Command Information")
e.description = f"**Listing Commands Registered in {scope}**\n\n" + "\n".join(
[f"`{c['id']}` : `{c['name']}`" for c in cmds]
)
return await ctx.send(embeds=e)
else:
return await ctx.send(f"No commands found in `{scope.strip()}`")
except Exception:
return await ctx.send(f"No commands found in `{scope.strip()}`")
inherited
property
readonly
commands: List[BaseCommand]
¶
Get the commands from this Extension.
inherited
property
readonly
listeners: List[Listener]
¶
Get the listeners from this Extension.
inherited
method
drop(self)
¶
Called when this Extension is being removed.
Source code in naff/ext/debug_extension/debug_application_cmd.py
def drop(self) -> None:
"""Called when this Extension is being removed."""
for func in self._commands:
if isinstance(func, naff.ModalCommand):
for listener in func.listeners:
# noinspection PyProtectedMember
self.bot._modal_callbacks.pop(listener)
elif isinstance(func, naff.ComponentCommand):
for listener in func.listeners:
# noinspection PyProtectedMember
self.bot._component_callbacks.pop(listener)
elif isinstance(func, naff.InteractionCommand):
for scope in func.scopes:
if self.bot.interactions.get(scope):
self.bot.interactions[scope].pop(func.resolved_name, [])
if isinstance(func, naff.HybridCommand):
# here's where things get complicated - we need to unload the prefixed command
# by necessity, there's a lot of logic here to determine what needs to be unloaded
if not func.callback: # not like it was added
return
if func.is_subcommand:
prefixed_base = self.bot.prefixed_commands.get(str(func.name))
_base_cmd = prefixed_base
if not prefixed_base:
# if something weird happened here, here's a safeguard
continue
if func.group_name:
prefixed_base = prefixed_base.subcommands.get(str(func.group_name))
if not prefixed_base:
continue
prefixed_base.remove_command(str(func.sub_cmd_name))
if not prefixed_base.subcommands:
# the base cmd is now empty, delete it
if func.group_name:
_base_cmd.remove_command(str(func.group_name)) # type: ignore
# and now the base command is empty
if not _base_cmd.subcommands: # type: ignore
# in case you're curious, i did try to put the below behavior
# in a function here, but then it turns out a weird python
# bug can happen if i did that
if cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif isinstance(func, naff.PrefixedCommand):
if not func.is_subcommand:
self.bot.prefixed_commands.pop(func.name, None)
for alias in func.aliases:
self.bot.prefixed_commands.pop(alias, None)
for func in self.listeners:
self.bot.listeners[func.event].remove(func)
self.bot.ext.pop(self.name, None)
logger.debug(f"{self.name} has been drop")
inherited
method
add_ext_auto_defer(self, ephemeral, time_until_defer)
¶
Add a auto defer for all commands in this extension.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ephemeral |
bool |
Should the command be deferred as ephemeral |
False |
time_until_defer |
float |
How long to wait before deferring automatically |
0.0 |
Source code in naff/ext/debug_extension/debug_application_cmd.py
def add_ext_auto_defer(self, ephemeral: bool = False, time_until_defer: float = 0.0) -> None:
"""
Add a auto defer for all commands in this extension.
Args:
ephemeral: Should the command be deferred as ephemeral
time_until_defer: How long to wait before deferring automatically
"""
self.auto_defer = naff.AutoDefer(enabled=True, ephemeral=ephemeral, time_until_defer=time_until_defer)
inherited
method
add_ext_check(self, coroutine)
¶
Add a coroutine as a check for all commands in this extension to run. This coroutine must take only the parameter context
.
Example Usage:
1 2 3 4 5 6 7 8 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[[Context], Awaitable[bool]] |
The coroutine to use as a check |
required |
Source code in naff/ext/debug_extension/debug_application_cmd.py
def add_ext_check(self, coroutine: Callable[["Context"], Awaitable[bool]]) -> None:
"""
Add a coroutine as a check for all commands in this extension to run. This coroutine must take **only** the parameter `context`.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_ext_check(self.example)
@staticmethod
async def example(context: Context):
if context.author.id == 123456789:
return True
return False
```
Args:
coroutine: The coroutine to use as a check
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Check must be a coroutine")
if not self.extension_checks:
self.extension_checks = []
self.extension_checks.append(coroutine)
inherited
method
add_extension_prerun(self, coroutine)
¶
Add a coroutine to be run before all commands in this Extension.
Note
Pre-runs will only be run if the commands checks pass
Example Usage:
1 2 3 4 5 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[..., Coroutine] |
The coroutine to run |
required |
Source code in naff/ext/debug_extension/debug_application_cmd.py
def add_extension_prerun(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to be run **before** all commands in this Extension.
Note:
Pre-runs will **only** be run if the commands checks pass
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_extension_prerun(self.example)
async def example(self, context: Context):
await ctx.send("I ran first")
```
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if not self.extension_prerun:
self.extension_prerun = []
self.extension_prerun.append(coroutine)
inherited
method
add_extension_postrun(self, coroutine)
¶
Add a coroutine to be run after all commands in this Extension.
Example Usage:
1 2 3 4 5 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[..., Coroutine] |
The coroutine to run |
required |
Source code in naff/ext/debug_extension/debug_application_cmd.py
def add_extension_postrun(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to be run **after** all commands in this Extension.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_extension_postrun(self.example)
async def example(self, context: Context):
await ctx.send("I ran first")
```
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if not self.extension_postrun:
self.extension_postrun = []
self.extension_postrun.append(coroutine)
inherited
method
set_extension_error(self, coroutine)
¶
Add a coroutine to handle any exceptions raised in this extension.
Example Usage:
```python def init(self, bot): self.set_extension_error(self.example)
Args: coroutine: The coroutine to run
Source code in naff/ext/debug_extension/debug_application_cmd.py
def set_extension_error(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to handle any exceptions raised in this extension.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.set_extension_error(self.example)
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if self.extension_error:
logger.warning("Extension error callback has been overridden!")
self.extension_error = coroutine
debug_exec
¶
class
DebugExec (Extension)
¶
Source code in naff/ext/debug_extension/debug_exec.py
class DebugExec(Extension):
def __init__(self, bot) -> None:
self.cache: dict[int, str] = {}
@slash_command("debug", sub_cmd_name="exec", sub_cmd_description="Run arbitrary code")
async def debug_exec(self, ctx: InteractionContext) -> Optional[Message]:
last_code = self.cache.get(ctx.author.id, MISSING)
env = {
"bot": self.bot,
"ctx": ctx,
"channel": ctx.channel,
"author": ctx.author,
"server": ctx.guild,
"guild": ctx.guild,
"message": ctx.message,
} | globals()
modal = Modal(
title="Debug-Exec",
components=[
ParagraphText(
label="Code to run", value=last_code, custom_id="body", placeholder="Write your code here!"
)
],
)
await ctx.send_modal(modal)
m_ctx = await self.bot.wait_for_modal(modal, ctx.author)
await m_ctx.defer()
body = m_ctx.kwargs["body"]
self.cache[ctx.author.id] = body
if body.startswith("```") and body.endswith("```"):
body = "\n".join(body.split("\n")[1:-1])
else:
body = body.strip("` \n")
stdout = io.StringIO()
to_compile = "async def func():\n%s" % textwrap.indent(body, " ")
try:
exec(to_compile, env) # noqa: S102
except SyntaxError:
return await ctx.send(f"```py\n{traceback.format_exc()}\n```")
func = env["func"]
try:
with redirect_stdout(stdout):
ret = await func() # noqa
except Exception:
return await m_ctx.send(f"```py\n{stdout.getvalue()}{traceback.format_exc()}\n```")
else:
return await self.handle_exec_result(m_ctx, ret, stdout.getvalue(), body)
async def handle_exec_result(self, ctx: ModalContext, result: Any, value: Any, body: str) -> Optional[Message]:
if len(body) <= 2000:
await ctx.send(f"```py\n{body}```")
else:
paginator = Paginator.create_from_string(self.bot, body, prefix="```py", suffix="```", page_size=4000)
await paginator.send(ctx)
if result is None:
result = value or "No Output!"
if isinstance(result, Message):
try:
e = debug_embed("Exec", timestamp=result.created_at, url=result.jump_url)
e.description = result.content
e.set_author(result.author.tag, icon_url=(result.author.guild_avatar or result.author.avatar).url)
e.add_field("\u200b", f"[Jump To]({result.jump_url})\n{result.channel.mention}")
return await ctx.send(embeds=e)
except Exception:
return await ctx.send(result.jump_url)
if isinstance(result, Embed):
return await ctx.send(embeds=[result])
if isinstance(result, File):
return await ctx.send(file=result)
if isinstance(result, Paginator):
return await result.send(ctx)
if hasattr(result, "__iter__"):
l_result = list(result)
if all([isinstance(r, Embed) for r in result]):
paginator = Paginator.create_from_embeds(self.bot, *l_result)
return await paginator.send(ctx)
if not isinstance(result, str):
result = repr(result)
# prevent token leak
result = result.replace(self.bot.http.token, "[REDACTED TOKEN]")
if len(result) <= 2000:
return await ctx.send(f"```py\n{result}```")
else:
paginator = Paginator.create_from_string(self.bot, result, prefix="```py", suffix="```", page_size=4000)
return await paginator.send(ctx)
inherited
property
readonly
commands: List[BaseCommand]
¶
Get the commands from this Extension.
inherited
property
readonly
listeners: List[Listener]
¶
Get the listeners from this Extension.
inherited
method
drop(self)
¶
Called when this Extension is being removed.
Source code in naff/ext/debug_extension/debug_exec.py
def drop(self) -> None:
"""Called when this Extension is being removed."""
for func in self._commands:
if isinstance(func, naff.ModalCommand):
for listener in func.listeners:
# noinspection PyProtectedMember
self.bot._modal_callbacks.pop(listener)
elif isinstance(func, naff.ComponentCommand):
for listener in func.listeners:
# noinspection PyProtectedMember
self.bot._component_callbacks.pop(listener)
elif isinstance(func, naff.InteractionCommand):
for scope in func.scopes:
if self.bot.interactions.get(scope):
self.bot.interactions[scope].pop(func.resolved_name, [])
if isinstance(func, naff.HybridCommand):
# here's where things get complicated - we need to unload the prefixed command
# by necessity, there's a lot of logic here to determine what needs to be unloaded
if not func.callback: # not like it was added
return
if func.is_subcommand:
prefixed_base = self.bot.prefixed_commands.get(str(func.name))
_base_cmd = prefixed_base
if not prefixed_base:
# if something weird happened here, here's a safeguard
continue
if func.group_name:
prefixed_base = prefixed_base.subcommands.get(str(func.group_name))
if not prefixed_base:
continue
prefixed_base.remove_command(str(func.sub_cmd_name))
if not prefixed_base.subcommands:
# the base cmd is now empty, delete it
if func.group_name:
_base_cmd.remove_command(str(func.group_name)) # type: ignore
# and now the base command is empty
if not _base_cmd.subcommands: # type: ignore
# in case you're curious, i did try to put the below behavior
# in a function here, but then it turns out a weird python
# bug can happen if i did that
if cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif isinstance(func, naff.PrefixedCommand):
if not func.is_subcommand:
self.bot.prefixed_commands.pop(func.name, None)
for alias in func.aliases:
self.bot.prefixed_commands.pop(alias, None)
for func in self.listeners:
self.bot.listeners[func.event].remove(func)
self.bot.ext.pop(self.name, None)
logger.debug(f"{self.name} has been drop")
inherited
method
add_ext_auto_defer(self, ephemeral, time_until_defer)
¶
Add a auto defer for all commands in this extension.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ephemeral |
bool |
Should the command be deferred as ephemeral |
False |
time_until_defer |
float |
How long to wait before deferring automatically |
0.0 |
Source code in naff/ext/debug_extension/debug_exec.py
def add_ext_auto_defer(self, ephemeral: bool = False, time_until_defer: float = 0.0) -> None:
"""
Add a auto defer for all commands in this extension.
Args:
ephemeral: Should the command be deferred as ephemeral
time_until_defer: How long to wait before deferring automatically
"""
self.auto_defer = naff.AutoDefer(enabled=True, ephemeral=ephemeral, time_until_defer=time_until_defer)
inherited
method
add_ext_check(self, coroutine)
¶
Add a coroutine as a check for all commands in this extension to run. This coroutine must take only the parameter context
.
Example Usage:
1 2 3 4 5 6 7 8 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[[Context], Awaitable[bool]] |
The coroutine to use as a check |
required |
Source code in naff/ext/debug_extension/debug_exec.py
def add_ext_check(self, coroutine: Callable[["Context"], Awaitable[bool]]) -> None:
"""
Add a coroutine as a check for all commands in this extension to run. This coroutine must take **only** the parameter `context`.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_ext_check(self.example)
@staticmethod
async def example(context: Context):
if context.author.id == 123456789:
return True
return False
```
Args:
coroutine: The coroutine to use as a check
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Check must be a coroutine")
if not self.extension_checks:
self.extension_checks = []
self.extension_checks.append(coroutine)
inherited
method
add_extension_prerun(self, coroutine)
¶
Add a coroutine to be run before all commands in this Extension.
Note
Pre-runs will only be run if the commands checks pass
Example Usage:
1 2 3 4 5 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[..., Coroutine] |
The coroutine to run |
required |
Source code in naff/ext/debug_extension/debug_exec.py
def add_extension_prerun(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to be run **before** all commands in this Extension.
Note:
Pre-runs will **only** be run if the commands checks pass
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_extension_prerun(self.example)
async def example(self, context: Context):
await ctx.send("I ran first")
```
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if not self.extension_prerun:
self.extension_prerun = []
self.extension_prerun.append(coroutine)
inherited
method
add_extension_postrun(self, coroutine)
¶
Add a coroutine to be run after all commands in this Extension.
Example Usage:
1 2 3 4 5 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[..., Coroutine] |
The coroutine to run |
required |
Source code in naff/ext/debug_extension/debug_exec.py
def add_extension_postrun(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to be run **after** all commands in this Extension.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_extension_postrun(self.example)
async def example(self, context: Context):
await ctx.send("I ran first")
```
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if not self.extension_postrun:
self.extension_postrun = []
self.extension_postrun.append(coroutine)
inherited
method
set_extension_error(self, coroutine)
¶
Add a coroutine to handle any exceptions raised in this extension.
Example Usage:
```python def init(self, bot): self.set_extension_error(self.example)
Args: coroutine: The coroutine to run
Source code in naff/ext/debug_extension/debug_exec.py
def set_extension_error(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to handle any exceptions raised in this extension.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.set_extension_error(self.example)
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if self.extension_error:
logger.warning("Extension error callback has been overridden!")
self.extension_error = coroutine
debug_exts
¶
class
DebugExts (Extension)
¶
Source code in naff/ext/debug_extension/debug_exts.py
class DebugExts(Extension):
@prefixed_command("debug_reload")
async def reload(self, ctx: PrefixedContext, module: str) -> None:
try:
await self.drop_ext.callback(ctx, module)
except ExtensionLoadException:
pass
await self.load_ext.callback(ctx, module)
@prefixed_command("debug_load")
async def load_ext(self, ctx: PrefixedContext, module: str) -> None:
self.bot.load_extension(module)
await ctx.message.add_reaction("🪴")
@prefixed_command("debug_drop")
async def drop_ext(self, ctx: PrefixedContext, module: str) -> None:
self.bot.unload_extension(module)
await ctx.message.add_reaction("💥")
@reload.error
@load_ext.error
@drop_ext.error
async def reload_error(self, error: Exception, ctx: Context, *args) -> None:
if isinstance(error, CommandCheckFailure):
return await ctx.send("You do not have permission to execute this command")
elif isinstance(error, ExtensionLoadException):
return await ctx.send(str(error))
raise
inherited
property
readonly
commands: List[BaseCommand]
¶
Get the commands from this Extension.
inherited
property
readonly
listeners: List[Listener]
¶
Get the listeners from this Extension.
inherited
method
drop(self)
¶
Called when this Extension is being removed.
Source code in naff/ext/debug_extension/debug_exts.py
def drop(self) -> None:
"""Called when this Extension is being removed."""
for func in self._commands:
if isinstance(func, naff.ModalCommand):
for listener in func.listeners:
# noinspection PyProtectedMember
self.bot._modal_callbacks.pop(listener)
elif isinstance(func, naff.ComponentCommand):
for listener in func.listeners:
# noinspection PyProtectedMember
self.bot._component_callbacks.pop(listener)
elif isinstance(func, naff.InteractionCommand):
for scope in func.scopes:
if self.bot.interactions.get(scope):
self.bot.interactions[scope].pop(func.resolved_name, [])
if isinstance(func, naff.HybridCommand):
# here's where things get complicated - we need to unload the prefixed command
# by necessity, there's a lot of logic here to determine what needs to be unloaded
if not func.callback: # not like it was added
return
if func.is_subcommand:
prefixed_base = self.bot.prefixed_commands.get(str(func.name))
_base_cmd = prefixed_base
if not prefixed_base:
# if something weird happened here, here's a safeguard
continue
if func.group_name:
prefixed_base = prefixed_base.subcommands.get(str(func.group_name))
if not prefixed_base:
continue
prefixed_base.remove_command(str(func.sub_cmd_name))
if not prefixed_base.subcommands:
# the base cmd is now empty, delete it
if func.group_name:
_base_cmd.remove_command(str(func.group_name)) # type: ignore
# and now the base command is empty
if not _base_cmd.subcommands: # type: ignore
# in case you're curious, i did try to put the below behavior
# in a function here, but then it turns out a weird python
# bug can happen if i did that
if cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif isinstance(func, naff.PrefixedCommand):
if not func.is_subcommand:
self.bot.prefixed_commands.pop(func.name, None)
for alias in func.aliases:
self.bot.prefixed_commands.pop(alias, None)
for func in self.listeners:
self.bot.listeners[func.event].remove(func)
self.bot.ext.pop(self.name, None)
logger.debug(f"{self.name} has been drop")
inherited
method
add_ext_auto_defer(self, ephemeral, time_until_defer)
¶
Add a auto defer for all commands in this extension.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ephemeral |
bool |
Should the command be deferred as ephemeral |
False |
time_until_defer |
float |
How long to wait before deferring automatically |
0.0 |
Source code in naff/ext/debug_extension/debug_exts.py
def add_ext_auto_defer(self, ephemeral: bool = False, time_until_defer: float = 0.0) -> None:
"""
Add a auto defer for all commands in this extension.
Args:
ephemeral: Should the command be deferred as ephemeral
time_until_defer: How long to wait before deferring automatically
"""
self.auto_defer = naff.AutoDefer(enabled=True, ephemeral=ephemeral, time_until_defer=time_until_defer)
inherited
method
add_ext_check(self, coroutine)
¶
Add a coroutine as a check for all commands in this extension to run. This coroutine must take only the parameter context
.
Example Usage:
1 2 3 4 5 6 7 8 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[[Context], Awaitable[bool]] |
The coroutine to use as a check |
required |
Source code in naff/ext/debug_extension/debug_exts.py
def add_ext_check(self, coroutine: Callable[["Context"], Awaitable[bool]]) -> None:
"""
Add a coroutine as a check for all commands in this extension to run. This coroutine must take **only** the parameter `context`.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_ext_check(self.example)
@staticmethod
async def example(context: Context):
if context.author.id == 123456789:
return True
return False
```
Args:
coroutine: The coroutine to use as a check
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Check must be a coroutine")
if not self.extension_checks:
self.extension_checks = []
self.extension_checks.append(coroutine)
inherited
method
add_extension_prerun(self, coroutine)
¶
Add a coroutine to be run before all commands in this Extension.
Note
Pre-runs will only be run if the commands checks pass
Example Usage:
1 2 3 4 5 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[..., Coroutine] |
The coroutine to run |
required |
Source code in naff/ext/debug_extension/debug_exts.py
def add_extension_prerun(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to be run **before** all commands in this Extension.
Note:
Pre-runs will **only** be run if the commands checks pass
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_extension_prerun(self.example)
async def example(self, context: Context):
await ctx.send("I ran first")
```
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if not self.extension_prerun:
self.extension_prerun = []
self.extension_prerun.append(coroutine)
inherited
method
add_extension_postrun(self, coroutine)
¶
Add a coroutine to be run after all commands in this Extension.
Example Usage:
1 2 3 4 5 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[..., Coroutine] |
The coroutine to run |
required |
Source code in naff/ext/debug_extension/debug_exts.py
def add_extension_postrun(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to be run **after** all commands in this Extension.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_extension_postrun(self.example)
async def example(self, context: Context):
await ctx.send("I ran first")
```
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if not self.extension_postrun:
self.extension_postrun = []
self.extension_postrun.append(coroutine)
inherited
method
set_extension_error(self, coroutine)
¶
Add a coroutine to handle any exceptions raised in this extension.
Example Usage:
```python def init(self, bot): self.set_extension_error(self.example)
Args: coroutine: The coroutine to run
Source code in naff/ext/debug_extension/debug_exts.py
def set_extension_error(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to handle any exceptions raised in this extension.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.set_extension_error(self.example)
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if self.extension_error:
logger.warning("Extension error callback has been overridden!")
self.extension_error = coroutine
utils
¶
function
debug_embed(title, **kwargs)
¶
Create a debug embed with a standard header and footer.
Source code in naff/ext/debug_extension/utils.py
def debug_embed(title: str, **kwargs) -> Embed:
"""Create a debug embed with a standard header and footer."""
e = Embed(
f"Naff Debug: {title}",
url="https://github.com/Discord-Snake-Pit/NAFF/tree/master/naff/ext/debug_extension",
color=MaterialColors.BLUE_GREY,
**kwargs,
)
e.set_footer(
"Naff Debug Extension",
icon_url="https://media.discordapp.net/attachments/907639005070377020/918600896433238097/sparkle-snekCUnetnoise_scaleLevel0x2.500000.png",
)
return e
function
get_cache_state(bot)
¶
Create a nicely formatted table of internal cache state.
Source code in naff/ext/debug_extension/utils.py
def get_cache_state(bot: "Client") -> str:
"""Create a nicely formatted table of internal cache state."""
caches = [
c[0]
for c in inspect.getmembers(bot.cache, predicate=lambda x: isinstance(x, dict))
if not c[0].startswith("__")
]
table = []
for cache in caches:
val = getattr(bot.cache, cache)
if isinstance(val, TTLCache):
amount = [len(val), f"{val.hard_limit}({val.soft_limit})"]
expire = f"{val.ttl}s"
else:
amount = [len(val), "∞"]
expire = "none"
row = [cache.removesuffix("_cache"), amount, expire]
table.append(row)
# http caches
table.append(["endpoints", [len(bot.http._endpoints), "∞"], "none"])
table.append(["ratelimits", [len(bot.http.ratelimit_locks), "∞"], "w_ref"])
adjust_subcolumn(table, 1, aligns=[">", "<"])
labels = ["Cache", "Amount", "Expire"]
return make_table(table, labels)
function
strf_delta(time_delta, show_seconds)
¶
Formats timedelta into a human readable string.
Source code in naff/ext/debug_extension/utils.py
def strf_delta(time_delta: datetime.timedelta, show_seconds: bool = True) -> str:
"""Formats timedelta into a human readable string."""
years, days = divmod(time_delta.days, 365)
hours, rem = divmod(time_delta.seconds, 3600)
minutes, seconds = divmod(rem, 60)
years_fmt = f"{years} year{'s' if years > 1 or years == 0 else ''}"
days_fmt = f"{days} day{'s' if days > 1 or days == 0 else ''}"
hours_fmt = f"{hours} hour{'s' if hours > 1 or hours == 0 else ''}"
minutes_fmt = f"{minutes} minute{'s' if minutes > 1 or minutes == 0 else ''}"
seconds_fmt = f"{seconds} second{'s' if seconds > 1 or seconds == 0 else ''}"
if years >= 1:
return f"{years_fmt} and {days_fmt}"
if days >= 1:
return f"{days_fmt} and {hours_fmt}"
if hours >= 1:
return f"{hours_fmt} and {minutes_fmt}"
if show_seconds:
return f"{minutes_fmt} and {seconds_fmt}"
return f"{minutes_fmt}"
function
adjust_subcolumn(rows, column_index, separator, aligns)
¶
Converts column composed of list of subcolumns into aligned str representation.
Source code in naff/ext/debug_extension/utils.py
def adjust_subcolumn(
rows: list[list[Any]], column_index: int, separator: str = "/", aligns: Union[list[str], str] = "<"
) -> None:
"""Converts column composed of list of subcolumns into aligned str representation."""
column = list(zip(*rows))[column_index]
subcolumn_widths = _get_column_widths(zip(*column))
if isinstance(aligns, str):
aligns = [aligns for _ in subcolumn_widths]
column = [_make_data_line(subcolumn_widths, row, "", separator, "", aligns) for row in column]
for row, new_item in zip(rows, column):
row[column_index] = new_item
function
make_table(rows, labels, centered)
¶
Converts 2D list to str representation as table
:param rows: 2D list containing objects that have a single-line representation (via str
). All rows must be of the same length.
:param labels: List containing the column labels. If present, the length must equal to that of each row.
:param centered: If the items should be aligned to the center, else they are left aligned.
:return: A table representing the rows passed in.
Source code in naff/ext/debug_extension/utils.py
def make_table(rows: list[list[Any]], labels: Optional[list[Any]] = None, centered: bool = False) -> str:
"""
Converts 2D list to str representation as table
:param rows: 2D list containing objects that have a single-line representation (via `str`). All rows must be of the same length.
:param labels: List containing the column labels. If present, the length must equal to that of each row.
:param centered: If the items should be aligned to the center, else they are left aligned.
:return: A table representing the rows passed in.
"""
columns = zip(*rows) if labels is None else zip(*rows, labels)
column_widths = _get_column_widths(columns)
align = "^" if centered else "<"
align = [align for _ in column_widths]
lines = [_make_solid_line(column_widths, "╭", "┬", "╮")]
data_left = "│ "
data_middle = " │ "
data_right = " │"
if labels is not None:
lines.append(_make_data_line(column_widths, labels, data_left, data_middle, data_right, align))
lines.append(_make_solid_line(column_widths, "├", "┼", "┤"))
for row in rows:
lines.append(_make_data_line(column_widths, row, data_left, data_middle, data_right, align))
lines.append(_make_solid_line(column_widths, "╰", "┴", "╯"))
return "\n".join(lines)
class
DebugExtension (DebugExec, DebugAppCMD, DebugExts, Extension)
¶
Source code in naff/ext/debug_extension/__init__.py
class DebugExtension(DebugExec, DebugAppCMD, DebugExts, Extension):
def __init__(self, bot: Client) -> None:
super().__init__(bot)
self.add_ext_check(checks.is_owner())
logger.info("Debug Extension is growing!")
@listen()
async def on_startup(self) -> None:
logger.info(f"Started {self.bot.user.tag} [{self.bot.user.id}] in Debug Mode")
logger.info(f"Caching System State: \n{get_cache_state(self.bot)}")
@slash_command(
"debug",
sub_cmd_name="info",
sub_cmd_description="Get basic information about the bot",
)
async def debug_info(self, ctx: InteractionContext) -> None:
await ctx.defer()
uptime = Timestamp.fromdatetime(self.bot.start_time)
e = debug_embed("General")
e.set_thumbnail(self.bot.user.avatar.url)
e.add_field("Operating System", platform.platform())
e.add_field("Version Info", f"Naff@{__version__} | Py@{__py_version__}")
e.add_field("Start Time", f"{uptime.format(TimestampStyles.RelativeTime)}")
privileged_intents = [i.name for i in self.bot.intents if i in Intents.PRIVILEGED]
if privileged_intents:
e.add_field("Privileged Intents", " | ".join(privileged_intents))
e.add_field("Loaded Exts", ", ".join(self.bot.ext))
e.add_field("Guilds", str(len(self.bot.guilds)))
await ctx.send(embeds=[e])
@debug_info.subcommand("cache", sub_cmd_description="Get information about the current cache state")
async def cache_info(self, ctx: InteractionContext) -> None:
await ctx.defer()
e = debug_embed("Cache")
e.description = f"```prolog\n{get_cache_state(self.bot)}\n```"
await ctx.send(embeds=[e])
@debug_info.subcommand("shutdown", sub_cmd_description="Shutdown the bot.")
async def shutdown(self, ctx: InteractionContext) -> None:
await ctx.send("Shutting down 😴")
await self.bot.stop()
inherited
property
readonly
commands: List[BaseCommand]
¶
Get the commands from this Extension.
inherited
property
readonly
listeners: List[Listener]
¶
Get the listeners from this Extension.
inherited
method
drop(self)
¶
Called when this Extension is being removed.
Source code in naff/ext/debug_extension/__init__.py
def drop(self) -> None:
"""Called when this Extension is being removed."""
for func in self._commands:
if isinstance(func, naff.ModalCommand):
for listener in func.listeners:
# noinspection PyProtectedMember
self.bot._modal_callbacks.pop(listener)
elif isinstance(func, naff.ComponentCommand):
for listener in func.listeners:
# noinspection PyProtectedMember
self.bot._component_callbacks.pop(listener)
elif isinstance(func, naff.InteractionCommand):
for scope in func.scopes:
if self.bot.interactions.get(scope):
self.bot.interactions[scope].pop(func.resolved_name, [])
if isinstance(func, naff.HybridCommand):
# here's where things get complicated - we need to unload the prefixed command
# by necessity, there's a lot of logic here to determine what needs to be unloaded
if not func.callback: # not like it was added
return
if func.is_subcommand:
prefixed_base = self.bot.prefixed_commands.get(str(func.name))
_base_cmd = prefixed_base
if not prefixed_base:
# if something weird happened here, here's a safeguard
continue
if func.group_name:
prefixed_base = prefixed_base.subcommands.get(str(func.group_name))
if not prefixed_base:
continue
prefixed_base.remove_command(str(func.sub_cmd_name))
if not prefixed_base.subcommands:
# the base cmd is now empty, delete it
if func.group_name:
_base_cmd.remove_command(str(func.group_name)) # type: ignore
# and now the base command is empty
if not _base_cmd.subcommands: # type: ignore
# in case you're curious, i did try to put the below behavior
# in a function here, but then it turns out a weird python
# bug can happen if i did that
if cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif cmd := self.bot.prefixed_commands.pop(str(func.name), None):
for alias in cmd.aliases:
self.bot.prefixed_commands.pop(alias, None)
elif isinstance(func, naff.PrefixedCommand):
if not func.is_subcommand:
self.bot.prefixed_commands.pop(func.name, None)
for alias in func.aliases:
self.bot.prefixed_commands.pop(alias, None)
for func in self.listeners:
self.bot.listeners[func.event].remove(func)
self.bot.ext.pop(self.name, None)
logger.debug(f"{self.name} has been drop")
inherited
method
add_ext_auto_defer(self, ephemeral, time_until_defer)
¶
Add a auto defer for all commands in this extension.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
ephemeral |
bool |
Should the command be deferred as ephemeral |
False |
time_until_defer |
float |
How long to wait before deferring automatically |
0.0 |
Source code in naff/ext/debug_extension/__init__.py
def add_ext_auto_defer(self, ephemeral: bool = False, time_until_defer: float = 0.0) -> None:
"""
Add a auto defer for all commands in this extension.
Args:
ephemeral: Should the command be deferred as ephemeral
time_until_defer: How long to wait before deferring automatically
"""
self.auto_defer = naff.AutoDefer(enabled=True, ephemeral=ephemeral, time_until_defer=time_until_defer)
inherited
method
add_ext_check(self, coroutine)
¶
Add a coroutine as a check for all commands in this extension to run. This coroutine must take only the parameter context
.
Example Usage:
1 2 3 4 5 6 7 8 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[[Context], Awaitable[bool]] |
The coroutine to use as a check |
required |
Source code in naff/ext/debug_extension/__init__.py
def add_ext_check(self, coroutine: Callable[["Context"], Awaitable[bool]]) -> None:
"""
Add a coroutine as a check for all commands in this extension to run. This coroutine must take **only** the parameter `context`.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_ext_check(self.example)
@staticmethod
async def example(context: Context):
if context.author.id == 123456789:
return True
return False
```
Args:
coroutine: The coroutine to use as a check
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Check must be a coroutine")
if not self.extension_checks:
self.extension_checks = []
self.extension_checks.append(coroutine)
inherited
method
add_extension_prerun(self, coroutine)
¶
Add a coroutine to be run before all commands in this Extension.
Note
Pre-runs will only be run if the commands checks pass
Example Usage:
1 2 3 4 5 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[..., Coroutine] |
The coroutine to run |
required |
Source code in naff/ext/debug_extension/__init__.py
def add_extension_prerun(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to be run **before** all commands in this Extension.
Note:
Pre-runs will **only** be run if the commands checks pass
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_extension_prerun(self.example)
async def example(self, context: Context):
await ctx.send("I ran first")
```
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if not self.extension_prerun:
self.extension_prerun = []
self.extension_prerun.append(coroutine)
inherited
method
add_extension_postrun(self, coroutine)
¶
Add a coroutine to be run after all commands in this Extension.
Example Usage:
1 2 3 4 5 |
|
Parameters:
Name | Type | Description | Default |
---|---|---|---|
coroutine |
Callable[..., Coroutine] |
The coroutine to run |
required |
Source code in naff/ext/debug_extension/__init__.py
def add_extension_postrun(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to be run **after** all commands in this Extension.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.add_extension_postrun(self.example)
async def example(self, context: Context):
await ctx.send("I ran first")
```
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if not self.extension_postrun:
self.extension_postrun = []
self.extension_postrun.append(coroutine)
inherited
method
set_extension_error(self, coroutine)
¶
Add a coroutine to handle any exceptions raised in this extension.
Example Usage:
```python def init(self, bot): self.set_extension_error(self.example)
Args: coroutine: The coroutine to run
Source code in naff/ext/debug_extension/__init__.py
def set_extension_error(self, coroutine: Callable[..., Coroutine]) -> None:
"""
Add a coroutine to handle any exceptions raised in this extension.
??? Hint "Example Usage:"
```python
def __init__(self, bot):
self.set_extension_error(self.example)
Args:
coroutine: The coroutine to run
"""
if not asyncio.iscoroutinefunction(coroutine):
raise TypeError("Callback must be a coroutine")
if self.extension_error:
logger.warning("Extension error callback has been overridden!")
self.extension_error = coroutine