Skip to content
This repository was archived by the owner on Aug 28, 2019. It is now read-only.

Commit 8a1800b

Browse files
committed
[commands] Add support for setting a fallback slash subcommand
This allows the group callback to be invoked as a slash subcommand
1 parent e9ff6b4 commit 8a1800b

File tree

1 file changed

+88
-3
lines changed

1 file changed

+88
-3
lines changed

discord/ext/commands/hybrid.py

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def replace_parameters(parameters: Dict[str, Parameter], signature: inspect.Sign
202202

203203

204204
class HybridAppCommand(discord.app_commands.Command[CogT, P, T]):
205-
def __init__(self, wrapped: HybridCommand[CogT, Any, T]) -> None:
205+
def __init__(self, wrapped: Command[CogT, Any, T]) -> None:
206206
signature = inspect.signature(wrapped.callback)
207207
params = replace_parameters(wrapped.params, signature)
208208
wrapped.callback.__signature__ = signature.replace(parameters=params)
@@ -216,7 +216,7 @@ def __init__(self, wrapped: HybridCommand[CogT, Any, T]) -> None:
216216
finally:
217217
del wrapped.callback.__signature__
218218

219-
self.wrapped: HybridCommand[CogT, Any, T] = wrapped
219+
self.wrapped: Command[CogT, Any, T] = wrapped
220220
self.binding = wrapped.cog
221221

222222
def _copy_with(self, **kwargs) -> Self:
@@ -435,11 +435,19 @@ class HybridGroup(Group[CogT, P, T]):
435435
decorator or functional interface.
436436
437437
.. versionadded:: 2.0
438+
439+
Parameters
440+
-----------
441+
fallback: Optional[:class:`str`]
442+
The command name to use as a fallback for the application command. Since
443+
application command groups cannot be invoked, this creates a subcommand within
444+
the group that can be invoked with the given group callback. If ``None``
445+
then no fallback command is given. Defaults to ``None``.
438446
"""
439447

440448
__commands_is_hybrid__: ClassVar[bool] = True
441449

442-
def __init__(self, *args: Any, **attrs: Any) -> None:
450+
def __init__(self, *args: Any, fallback: Optional[str] = None, **attrs: Any) -> None:
443451
super().__init__(*args, **attrs)
444452
self.invoke_without_command = True
445453
parent = None
@@ -458,6 +466,83 @@ def __init__(self, *args: Any, **attrs: Any) -> None:
458466

459467
# This prevents the group from re-adding the command at __init__
460468
self.app_command.parent = parent
469+
self.fallback: Optional[str] = fallback
470+
471+
if fallback is not None:
472+
command = HybridAppCommand(self)
473+
command.name = fallback
474+
self.app_command.add_command(command)
475+
476+
@property
477+
def _fallback_command(self) -> Optional[HybridAppCommand[CogT, ..., T]]:
478+
return self.app_command.get_command(self.fallback) # type: ignore
479+
480+
@property
481+
def cog(self) -> CogT:
482+
return self._cog
483+
484+
@cog.setter
485+
def cog(self, value: CogT) -> None:
486+
self._cog = value
487+
fallback = self._fallback_command
488+
if fallback:
489+
fallback.binding = value
490+
491+
async def can_run(self, ctx: Context[BotT], /) -> bool:
492+
fallback = self._fallback_command
493+
if ctx.interaction is not None and fallback:
494+
return await fallback._check_can_run(ctx.interaction)
495+
else:
496+
return await super().can_run(ctx)
497+
498+
async def _parse_arguments(self, ctx: Context[BotT]) -> None:
499+
interaction = ctx.interaction
500+
fallback = self._fallback_command
501+
if interaction is not None and fallback:
502+
ctx.kwargs = await fallback._transform_arguments(interaction, interaction.namespace)
503+
else:
504+
return await super()._parse_arguments(ctx)
505+
506+
def _ensure_assignment_on_copy(self, other: Self) -> Self:
507+
copy = super()._ensure_assignment_on_copy(other)
508+
copy.fallback = self.fallback
509+
return copy
510+
511+
def autocomplete(
512+
self, name: str
513+
) -> Callable[[AutocompleteCallback[CogT, ChoiceT]], AutocompleteCallback[CogT, ChoiceT]]:
514+
"""A decorator that registers a coroutine as an autocomplete prompt for a parameter.
515+
516+
This is the same as :meth:`~discord.app_commands.Command.autocomplete`. It is only
517+
applicable for the application command and doesn't do anything if the command is
518+
a regular command.
519+
520+
This is only available if the group has a fallback application command registered.
521+
522+
.. note::
523+
524+
Similar to the :meth:`~discord.app_commands.Command.autocomplete` method, this
525+
takes :class:`~discord.Interaction` as a parameter rather than a :class:`Context`.
526+
527+
Parameters
528+
-----------
529+
name: :class:`str`
530+
The parameter name to register as autocomplete.
531+
532+
Raises
533+
-------
534+
TypeError
535+
The coroutine passed is not actually a coroutine or
536+
the parameter is not found or of an invalid type.
537+
"""
538+
if self._fallback_command:
539+
return self._fallback_command.autocomplete(name)
540+
else:
541+
542+
def decorator(func: AutocompleteCallback[CogT, ChoiceT]) -> AutocompleteCallback[CogT, ChoiceT]:
543+
return func
544+
545+
return decorator
461546

462547
def add_command(self, command: Union[HybridGroup[CogT, ..., Any], HybridCommand[CogT, ..., Any]], /) -> None:
463548
"""Adds a :class:`.HybridCommand` into the internal list of commands.

0 commit comments

Comments
 (0)