Skip to content

Hybrid commands

attrs class HybridCommand (SlashCommand)

A subclass of SlashCommand that handles the logic for hybrid commands.

Attr attributes:

Name Type Description
extension Any

The extension this command belongs to

enabled bool

Whether this can be run at all

checks list

Any checks that must be checked before the command can run

cooldown Cooldown

An optional cooldown to apply to the command

max_concurrency MaxConcurrency

An optional maximum number of concurrent instances to apply to the command

error_callback Callable[..., Coroutine]

The coroutine to be called when an error occurs

pre_run_callback Callable[..., Coroutine]

The coroutine to be called before the command is executed, but after the checks

post_run_callback Callable[..., Coroutine]

The coroutine to be called after the command has executed

scopes List[Snowflake_Type]

The scopes of this interaction. Global or guild ids

default_member_permissions Optional[Permissions]

What permissions members need to have by default to use this command

dm_permission bool

Whether this command is enabled in DMs

cmd_id Dict[str, Snowflake_Type]

The unique IDs of this commands

callback Callable[..., Coroutine]

The coroutine to call when this interaction is received

auto_defer AutoDefer

A system to automatically defer this command after a set duration

nsfw bool

This command should only work in NSFW channels

Source code in naff/models/naff/hybrid_commands.py
@define()
class HybridCommand(SlashCommand):
    """A subclass of SlashCommand that handles the logic for hybrid commands."""

    async def __call__(self, context: InteractionContext, *args, **kwargs) -> None:
        new_ctx = context.bot.hybrid_context.from_interaction_context(context)
        return await super().__call__(new_ctx, *args, **kwargs)

    def group(self, name: str = None, description: str = "No Description Set") -> "HybridCommand":
        return HybridCommand(
            name=self.name,
            description=self.description,
            group_name=name,
            group_description=description,
            scopes=self.scopes,
        )

    def subcommand(
        self,
        sub_cmd_name: LocalisedName | str,
        group_name: LocalisedName | str = None,
        sub_cmd_description: Absent[LocalisedDesc | str] = MISSING,
        group_description: Absent[LocalisedDesc | str] = MISSING,
        options: list[SlashCommandOption | dict] = None,
        nsfw: bool = False,
    ) -> Callable[..., "HybridCommand"]:
        def wrapper(call: Callable[..., Coroutine]) -> "HybridCommand":
            nonlocal sub_cmd_description

            if not asyncio.iscoroutinefunction(call):
                raise TypeError("Subcommand must be coroutine")

            if sub_cmd_description is MISSING:
                sub_cmd_description = call.__doc__ or "No Description Set"

            return HybridCommand(
                name=self.name,
                description=self.description,
                group_name=group_name or self.group_name,
                group_description=group_description or self.group_description,
                sub_cmd_name=sub_cmd_name,
                sub_cmd_description=sub_cmd_description,
                default_member_permissions=self.default_member_permissions,
                dm_permission=self.dm_permission,
                options=options,
                callback=call,
                scopes=self.scopes,
                nsfw=nsfw,
            )

        return wrapper

async inherited method call_with_binding(self, callback, *args, **kwargs)

Call a given method using this objects _binding.

Parameters:

Name Type Description Default
callback Callable[..., Coroutine[Any, Any, Any]]

The callback to call.

required
Source code in naff/models/naff/hybrid_commands.py
async def call_with_binding(self, callback: Callable[..., Coroutine[Any, Any, Any]], *args, **kwargs) -> Any:
    """
    Call a given method using this objects _binding.

    Args:
        callback: The callback to call.
    """
    if self._binding:
        return await callback(self._binding, *args, **kwargs)
    return await callback(*args, **kwargs)

inherited method update_from_dict(self, data)

Updates object attribute(s) with new json data received from discord api.

Parameters:

Name Type Description Default
data Dict[str, Any]

The json data received from discord api.

required

Returns:

Type Description
~T

The updated object class instance.

Source code in naff/models/naff/hybrid_commands.py
def update_from_dict(self: Type[const.T], data: Dict[str, Any]) -> const.T:
    """
    Updates object attribute(s) with new json data received from discord api.

    Args:
        data: The json data received from discord api.

    Returns:
        The updated object class instance.

    """
    data = self._process_dict(data)
    for key, value in self._filter_kwargs(data, self._get_keys()).items():
        # todo improve
        setattr(self, key, value)

    return self

inherited method mention(self, scope)

Returns a string that would mention the interaction.

Parameters:

Name Type Description Default
scope Optional[Snowflake_Type]

If the command is available in multiple scope, specify which scope to get the mention for. Defaults to the first available one if not specified.

None

Returns:

Type Description
str

The markdown mention.

Source code in naff/models/naff/hybrid_commands.py
def mention(self, scope: Optional["Snowflake_Type"] = None) -> str:
    """
    Returns a string that would mention the interaction.

    Args:
        scope: If the command is available in multiple scope, specify which scope to get the mention for. Defaults to the first available one if not specified.

    Returns:
        The markdown mention.
    """
    if scope:
        cmd_id = self.get_cmd_id(scope=scope)
    else:
        cmd_id = list(self.cmd_id.values())[0]

    return f"</{self.resolved_name}:{cmd_id}>"

inherited method error(self, call)

A decorator to declare a coroutine as one that will be run upon an error.

Source code in naff/models/naff/hybrid_commands.py
def error(self, call: Callable[..., Coroutine]) -> Callable[..., Coroutine]:
    """A decorator to declare a coroutine as one that will be run upon an error."""
    if not asyncio.iscoroutinefunction(call):
        raise TypeError("Error handler must be coroutine")
    self.error_callback = call
    return call

inherited method pre_run(self, call)

A decorator to declare a coroutine as one that will be run before the command.

Source code in naff/models/naff/hybrid_commands.py
def pre_run(self, call: Callable[..., Coroutine]) -> Callable[..., Coroutine]:
    """A decorator to declare a coroutine as one that will be run before the command."""
    if not asyncio.iscoroutinefunction(call):
        raise TypeError("pre_run must be coroutine")
    self.pre_run_callback = call
    return call

inherited method post_run(self, call)

A decorator to declare a coroutine as one that will be run after the command has.

Source code in naff/models/naff/hybrid_commands.py
def post_run(self, call: Callable[..., Coroutine]) -> Callable[..., Coroutine]:
    """A decorator to declare a coroutine as one that will be run after the command has."""
    if not asyncio.iscoroutinefunction(call):
        raise TypeError("post_run must be coroutine")
    self.post_run_callback = call
    return call

inherited property readonly resolved_name: str

A representation of this interaction's name.

inherited method to_dict(self)

Exports object into dictionary representation, ready to be sent to discord api.

Returns:

Type Description
dict

The exported dictionary.

Source code in naff/models/naff/hybrid_commands.py
def to_dict(self) -> dict:
    data = super().to_dict()

    if self.is_subcommand:
        data["name"] = str(self.sub_cmd_name)
        data["description"] = str(self.sub_cmd_description)
        data["name_localizations"] = self.sub_cmd_name.to_locale_dict()
        data["description_localizations"] = self.sub_cmd_description.to_locale_dict()
        data.pop("default_member_permissions", None)
        data.pop("dm_permission", None)
        data.pop("nsfw", None)
    else:
        data["name_localizations"] = self.name.to_locale_dict()
        data["description_localizations"] = self.description.to_locale_dict()
    return data

inherited method autocomplete(self, option_name)

A decorator to declare a coroutine as an option autocomplete.

Source code in naff/models/naff/hybrid_commands.py
def autocomplete(self, option_name: str) -> Callable[..., Coroutine]:
    """A decorator to declare a coroutine as an option autocomplete."""

    def wrapper(call: Callable[..., Coroutine]) -> Callable[..., Coroutine]:
        if not asyncio.iscoroutinefunction(call):
            raise TypeError("autocomplete must be coroutine")
        self.autocomplete_callbacks[option_name] = call

        # automatically set the option's autocomplete attribute to True
        for opt in self.options:
            if isinstance(opt, dict) and str(opt["name"]) == option_name:
                opt["autocomplete"] = True
            elif isinstance(opt, SlashCommandOption) and str(opt.name) == option_name:
                opt.autocomplete = True

        return call

    option_name = option_name.lower()
    return wrapper

function hybrid_command(name, *, description, scopes, options, default_member_permissions, dm_permission, sub_cmd_name, group_name, sub_cmd_description, group_description, nsfw)

A decorator to declare a coroutine as a hybrid command.

Hybrid commands are a slash command that can also function as a prefixed command. These use a HybridContext instead of an InteractionContext, but otherwise are mostly identical to normal slash commands.

Note that hybrid commands do not support autocompletes. They also only partially support attachments, allowing one attachment option for a command.

Note

While the base and group descriptions aren't visible in the discord client, currently. We strongly advise defining them anyway, if you're using subcommands, as Discord has said they will be visible in one of the future ui updates. They are also visible as the description for their prefixed command counterparts.

Parameters:

Name Type Description Default
name str | naff.models.naff.application_commands.LocalisedName

1-32 character name of the command

required
description Union[naff.models.naff.application_commands.LocalisedDesc, str, naff.client.const.Missing]

1-100 character description of the command

Missing
scopes Union[list['Snowflake_Type'], naff.client.const.Missing]

The scope this command exists within

Missing
options Optional[list[naff.models.naff.application_commands.SlashCommandOption | dict]]

The parameters for the command, max 25

None
default_member_permissions Optional[Permissions]

What permissions members need to have by default to use this command.

None
dm_permission bool

Should this command be available in DMs.

True
sub_cmd_name str | naff.models.naff.application_commands.LocalisedName

1-32 character name of the subcommand

None
sub_cmd_description str | naff.models.naff.application_commands.LocalisedDesc

1-100 character description of the subcommand

'No Description Set'
group_name str | naff.models.naff.application_commands.LocalisedName

1-32 character name of the group

None
group_description str | naff.models.naff.application_commands.LocalisedDesc

1-100 character description of the group

'No Description Set'
nsfw bool

This command should only work in NSFW channels

False

Returns:

Type Description
Callable[[Callable[..., Coroutine]], naff.models.naff.hybrid_commands.HybridCommand]

HybridCommand Object

Source code in naff/models/naff/hybrid_commands.py
def hybrid_command(
    name: str | LocalisedName,
    *,
    description: Absent[str | LocalisedDesc] = MISSING,
    scopes: Absent[list["Snowflake_Type"]] = MISSING,
    options: Optional[list[SlashCommandOption | dict]] = None,
    default_member_permissions: Optional["Permissions"] = None,
    dm_permission: bool = True,
    sub_cmd_name: str | LocalisedName = None,
    group_name: str | LocalisedName = None,
    sub_cmd_description: str | LocalisedDesc = "No Description Set",
    group_description: str | LocalisedDesc = "No Description Set",
    nsfw: bool = False,
) -> Callable[[Callable[..., Coroutine]], HybridCommand]:
    """
    A decorator to declare a coroutine as a hybrid command.

    Hybrid commands are a slash command that can also function as a prefixed command.
    These use a HybridContext instead of an InteractionContext, but otherwise are mostly identical to normal slash commands.

    Note that hybrid commands do not support autocompletes.
    They also only partially support attachments, allowing one attachment option for a command.

    note:
        While the base and group descriptions aren't visible in the discord client, currently.
        We strongly advise defining them anyway, if you're using subcommands, as Discord has said they will be visible in
        one of the future ui updates.
        They are also visible as the description for their prefixed command counterparts.

    Args:
        name: 1-32 character name of the command
        description: 1-100 character description of the command
        scopes: The scope this command exists within
        options: The parameters for the command, max 25
        default_member_permissions: What permissions members need to have by default to use this command.
        dm_permission: Should this command be available in DMs.
        sub_cmd_name: 1-32 character name of the subcommand
        sub_cmd_description: 1-100 character description of the subcommand
        group_name: 1-32 character name of the group
        group_description: 1-100 character description of the group
        nsfw: This command should only work in NSFW channels

    Returns:
        HybridCommand Object

    """

    def wrapper(func: Callable[..., Coroutine]) -> HybridCommand:
        if not asyncio.iscoroutinefunction(func):
            raise ValueError("Commands must be coroutines")

        perm = default_member_permissions
        if hasattr(func, "default_member_permissions"):
            if perm:
                perm = perm | func.default_member_permissions
            else:
                perm = func.default_member_permissions

        _description = description
        if _description is MISSING:
            _description = func.__doc__ or "No Description Set"

        return HybridCommand(
            name=name,
            group_name=group_name,
            group_description=group_description,
            sub_cmd_name=sub_cmd_name,
            sub_cmd_description=sub_cmd_description,
            description=_description,
            scopes=scopes or [GLOBAL_SCOPE],
            default_member_permissions=perm,
            dm_permission=dm_permission,
            callback=func,
            options=options,
            nsfw=nsfw,
        )

    return wrapper

function hybrid_subcommand(base, *, subcommand_group, name, description, base_description, base_desc, base_default_member_permissions, base_dm_permission, subcommand_group_description, sub_group_desc, scopes, options, nsfw)

A decorator specifically tailored for creating hybrid subcommands.

See the hybrid_command decorator for more information.

Parameters:

Name Type Description Default
base str | naff.models.naff.application_commands.LocalisedName

The name of the base command

required
subcommand_group Union[str, naff.models.naff.application_commands.LocalisedName]

The name of the subcommand group, if any.

None
name Union[str, naff.models.naff.application_commands.LocalisedName]

The name of the subcommand, defaults to the name of the coroutine.

None
description Union[naff.models.naff.application_commands.LocalisedDesc, str, naff.client.const.Missing]

The description of the subcommand

Missing
base_description Union[str, naff.models.naff.application_commands.LocalisedDesc]

The description of the base command

None
base_desc Union[str, naff.models.naff.application_commands.LocalisedDesc]

An alias of base_description

None
base_default_member_permissions Optional[Permissions]

What permissions members need to have by default to use this command.

None
base_dm_permission bool

Should this command be available in DMs.

True
subcommand_group_description Union[str, naff.models.naff.application_commands.LocalisedDesc]

Description of the subcommand group

None
sub_group_desc Union[str, naff.models.naff.application_commands.LocalisedDesc]

An alias for subcommand_group_description

None
scopes list

The scopes of which this command is available, defaults to GLOBAL_SCOPE

None
options list

The options for this command

None
nsfw bool

This command should only work in NSFW channels

False

Returns:

Type Description
Callable[[Coroutine], naff.models.naff.hybrid_commands.HybridCommand]

A HybridCommand object

Source code in naff/models/naff/hybrid_commands.py
def hybrid_subcommand(
    base: str | LocalisedName,
    *,
    subcommand_group: Optional[str | LocalisedName] = None,
    name: Optional[str | LocalisedName] = None,
    description: Absent[str | LocalisedDesc] = MISSING,
    base_description: Optional[str | LocalisedDesc] = None,
    base_desc: Optional[str | LocalisedDesc] = None,
    base_default_member_permissions: Optional["Permissions"] = None,
    base_dm_permission: bool = True,
    subcommand_group_description: Optional[str | LocalisedDesc] = None,
    sub_group_desc: Optional[str | LocalisedDesc] = None,
    scopes: list["Snowflake_Type"] = None,
    options: list[dict] = None,
    nsfw: bool = False,
) -> Callable[[Coroutine], HybridCommand]:
    """
    A decorator specifically tailored for creating hybrid subcommands.

    See the hybrid_command decorator for more information.

    Args:
        base: The name of the base command
        subcommand_group: The name of the subcommand group, if any.
        name: The name of the subcommand, defaults to the name of the coroutine.
        description: The description of the subcommand
        base_description: The description of the base command
        base_desc: An alias of `base_description`
        base_default_member_permissions: What permissions members need to have by default to use this command.
        base_dm_permission: Should this command be available in DMs.
        subcommand_group_description: Description of the subcommand group
        sub_group_desc: An alias for `subcommand_group_description`
        scopes: The scopes of which this command is available, defaults to GLOBAL_SCOPE
        options: The options for this command
        nsfw: This command should only work in NSFW channels

    Returns:
        A HybridCommand object

    """

    def wrapper(func) -> HybridCommand:
        if not asyncio.iscoroutinefunction(func):
            raise ValueError("Commands must be coroutines")

        _description = description
        if _description is MISSING:
            _description = func.__doc__ or "No Description Set"

        return HybridCommand(
            name=base,
            description=(base_description or base_desc) or "No Description Set",
            group_name=subcommand_group,
            group_description=(subcommand_group_description or sub_group_desc) or "No Description Set",
            sub_cmd_name=name,
            sub_cmd_description=_description,
            default_member_permissions=base_default_member_permissions,
            dm_permission=base_dm_permission,
            scopes=scopes or [GLOBAL_SCOPE],
            callback=func,
            options=options,
            nsfw=nsfw,
        )

    return wrapper