Source code for ezcord.components

"""Classes that are adding some functionality for default components.

.. hint::

        Components in your code are automatically replaced with the ones from this module.
        You can use `discord.ui.View` instead of `ezcord.View`.
"""

from __future__ import annotations

import inspect
from collections.abc import Callable

from .internal.dc import discord
from .logs import log

_view_checks: list[Callable] = []
_view_check_failures: list[Callable] = []

__all__ = ("DropdownPaginator", "Modal", "View", "event")


def _check_coro(func):
    if not inspect.iscoroutinefunction(func):
        raise TypeError(f"Event registered must be a coroutine function, not {type(func)}")


def _check_params(func, amount):
    parameters = inspect.signature(func).parameters
    if len(parameters) != amount:
        raise ValueError(
            f"Event method must have '{amount}' parameters, has '{len(parameters)}' instead"
        )


[docs] def event(coro): """A decorator to register custom checks and error handlers for Discord components. Example ------- .. code-block:: python @ezcord.event async def view_check(interaction): return interaction.user.id == 12345 # Returns True or False @ezcord.event async def on_view_check_failure(interaction): await interaction.response.send_message("You can't use this!") """ _check_coro(coro) name = coro.__name__.lstrip("_") if name == "view_check": _check_params(coro, 1) _view_checks.append(coro) elif name == "on_view_check_failure": _check_params(coro, 1) _view_check_failures.append(coro) elif name == "on_view_error": raise ValueError( "This event was removed in version 0.7.5, use 'Bot.on_view_error' instead: " "https://docs.pycord.dev/en/master/api/clients.html#discord.Bot.on_modal_error" ) elif name == "on_modal_error": raise ValueError( "This event was removed in version 0.7.5, use 'Bot.on_modal_error' instead: " "https://docs.pycord.dev/en/master/api/clients.html#discord.Bot.on_view_error" ) else: raise ValueError(f"Invalid event name: '{coro.__name__}'") log.debug(f"Registered event **{coro.__name__}**")
[docs] class View(discord.ui.View): ignore_timeout_errors: bool = False """Base class for all :class:`discord.ui.View` classes that adds some functionality. Parameters ---------- ignore_timeout_error: If ``True``, views will not raise an exception if an error occurs during the timeout event. """ def __init__(self, *args, ignore_timeout_error: bool = False, **kwargs): self.ignore_timeout_error = ignore_timeout_error super().__init__(*args, **kwargs)
[docs] async def interaction_check(self, interaction: discord.Interaction) -> bool: """Return ``True`` if all custom checks return ``True`` or if no custom checks are registered. """ for coro in _view_checks: if not await coro(interaction): return False return True
[docs] async def on_check_failure(self, interaction: discord.Interaction) -> None: """Called if :meth:`interaction_check` returns ``False``.""" for coro in _view_check_failures: await coro(interaction)
[docs] async def on_timeout(self) -> None: """If ``disable_on_timeout`` is set to ``True``, this will disable all components, unless the viw has been explicitly stopped. """ try: return await super().on_timeout() except discord.NotFound: return except discord.HTTPException as e: if self.ignore_timeout_error or View.ignore_timeout_errors: return log.exception( f"Error in View **{type(self).__name__}** ({type(self).__module__}) ```{e}```", exc_info=e, )
# replace all default components with Ezcord components discord.ui.View = View discord.ui.Modal = Modal