Source code for acord.ext.application_commands.model_based

# m_based simply stands for model based
# meaning command that are dependent on a model such as a message
from __future__ import annotations
from email.mime import application

from typing import List, Optional
import inspect

from .base import UDAppCommand
from .types import ApplicationCommandType
from acord.errors import ApplicationCommandError
from acord.bases import _C


VALID_ATTR_NAMES = ("name", "default_permission", "guild_ids", "overwrite", "extend")

EXTENDED_CALLS = ("callback", "on_error")


[docs]class GenericModelCommand(UDAppCommand): name: str """ name of command """ type: ApplicationCommandType """ Type of command """ default_permission: Optional[bool] = True """ whether the command is enabled by default when the app is added to a guild """ guild_ids: Optional[List[int]] = None """ list of guilds to restrict command to """ overwrite: bool = False extend: bool = True __pre_calls__: dict = {} def __new__(cls, *args, **kwds): # Generates new command on call # Adds pre-existing args from cls to kwds and calls init # returning generated slash command extend = kwds.get("extend", False) or getattr(cls, "extend", False) for attr in VALID_ATTR_NAMES: a_ = getattr(cls, attr, None) if attr in kwds and extend: if hasattr(a_, "extend"): a_.extend(kwds[attr]) if not a_: continue kwds.update({attr: a_}) return super(GenericModelCommand, cls).__new__(cls) def __init__(self, **kwds) -> None: __pre_calls__ = {**self.__pre_calls__, **kwds.pop("calls", {})} for call_identifier in EXTENDED_CALLS: if call_identifier in kwds: __pre_calls__[call_identifier] = kwds[call_identifier] if "callback" not in __pre_calls__: raise ApplicationCommandError("Command missing callback") super().__init__(**kwds) def __init_subclass__(cls, **kwds) -> None: # kwds is validated in the second for loop extend = kwds.pop("extendable", True) overwrite = kwds.pop("overwritable", False) cls.extend = extend cls.overwrite = overwrite for attr in kwds: if attr in VALID_ATTR_NAMES: setattr(cls, attr, kwds[attr]) for attr in VALID_ATTR_NAMES: n_attr = getattr(cls, attr, None) field = cls.__fields__[attr] field.validate(n_attr, {}, loc=field.alias) setattr(cls, "__pre_calls__", {}) for attr in EXTENDED_CALLS: if hasattr(cls, attr): cls.__pre_calls__[attr] = getattr(cls, attr) return cls def dict(self, **kwds) -> dict: """:meta private:""" d = super(GenericModelCommand, self).dict(**kwds) to_pop = ["guild_ids", "overwrite", "extend", "__pre_calls__"] to_pop.extend(getattr(self, "__ignore__", [])) for key in to_pop: d.pop(key, None) return d def set_call(self, name: str, function: _C) -> None: f"""|coro| Registers a function to be called on a certain condition, class must have "callback" registered and is done when intiating the class. Parameters ---------- name: :class:`str` Name of call, any from: {", ".join(EXTENDED_CALLS)} function: Callable[..., Coroutine] A coroutine function to be called on dispatch """ if not inspect.iscoroutinefunction(function): raise TypeError("Function must be a coroutine function") self.__pre_calls__.update({name: function}) async def dispatcher(self, interaction, future, **kwds) -> int: """|coro| Default dispatch handler for when the slash command is used, handles callback and on_error calls. .. rubric:: Return Codes 0: Dispatched without error 1: Dispatched but an error occurred, on_error was called if accessible Exception of any type: Dispatched but an error occurred with both callback and error handler """ # 0 => Dispatched without error # 1 => Dispatched but an error occurred # Exception => Dispatched but an error occurred with both callback and error handler callback = self.__pre_calls__.get("callback") on_error = self.__pre_calls__.get("on_error") # Weird behaviour during testing, but oh well try: await callback(self, interaction, **kwds) except Exception as exc: if on_error: try: await on_error( self, interaction, (type(exc), exc, exc.__traceback__) ) except Exception as e_exc: return future.set_result(e_exc) else: future.set_result(exc) else: return future.set_result(0) return future.set_result(1) @classmethod def from_function(cls, function: _C, **kwds) -> None: """Generates slash command from a function, taking same kwargs and options as intiating the command normally. Parameters ---------- function: Callable[..., Coroutine] An async function which acts like the callback **kwds: Additional kwargs such as "name", "description". """ sc_ff = cls(**kwds) sc_ff.set_callback(function) return sc_ff
# NOTE: The actual commands
[docs]class UserCommand(GenericModelCommand): """User commands are commands that can be ran by right clicking a user They follow the same structure as slash commands and parameters, which can be found here at :class:`SlashCommands`. .. rubric:: Callback Function Callback function with user have the following signiture: [:class:`Interaction`, :class:`User`] .. note:: User may be the ID if cannot be fetched from cache """ def __init__(self, **kwds) -> None: kwds.update({"type": ApplicationCommandType.USER}) super().__init__(**kwds)
[docs]class MessageCommand(GenericModelCommand): """User commands are commands that can be ran by right clicking a message They follow the same structure as slash commands and parameters, which can be found here at :class:`SlashCommands`. .. rubric:: Callback Function Callback function with message have the following signiture: [:class:`Interaction`, :class:`Message`] .. note:: Message may be the ID if cannot be fetched from cache """ def __init__(self, **kwds) -> None: kwds.update({"type": ApplicationCommandType.MESSAGE}) super().__init__(**kwds)