Skip to content

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

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
def __init__(self, bot):
    self.add_extension_prerun(self.example)

async def example(self, context: Context):
    await ctx.send("I ran first")

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
def __init__(self, bot):
    self.add_extension_postrun(self.example)

async def example(self, context: Context):
    await ctx.send("I ran first")

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

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
def __init__(self, bot):
    self.add_extension_prerun(self.example)

async def example(self, context: Context):
    await ctx.send("I ran first")

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
def __init__(self, bot):
    self.add_extension_postrun(self.example)

async def example(self, context: Context):
    await ctx.send("I ran first")

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

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
def __init__(self, bot):
    self.add_extension_prerun(self.example)

async def example(self, context: Context):
    await ctx.send("I ran first")

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
def __init__(self, bot):
    self.add_extension_postrun(self.example)

async def example(self, context: Context):
    await ctx.send("I ran first")

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

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
def __init__(self, bot):
    self.add_extension_prerun(self.example)

async def example(self, context: Context):
    await ctx.send("I ran first")

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
def __init__(self, bot):
    self.add_extension_postrun(self.example)

async def example(self, context: Context):
    await ctx.send("I ran first")

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