mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-10-22 16:32:59 +00:00
Add Interaction.command and Interaction.namespace attributes
This commit is contained in:
@@ -47,7 +47,6 @@ from textwrap import TextWrapper
|
||||
import re
|
||||
|
||||
from ..enums import AppCommandOptionType, AppCommandType
|
||||
from ..interactions import Interaction
|
||||
from .models import Choice
|
||||
from .transformers import annotation_to_parameter, CommandParameter, NoneType
|
||||
from .errors import AppCommandError, CommandInvokeError, CommandSignatureMismatch, CommandAlreadyRegistered
|
||||
@@ -58,6 +57,7 @@ from ..utils import resolve_annotation, MISSING, is_inside_class
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import ParamSpec, Concatenate
|
||||
from ..interactions import Interaction
|
||||
from ..abc import Snowflake
|
||||
from .namespace import Namespace
|
||||
from .models import ChoiceT
|
||||
@@ -88,32 +88,32 @@ T = TypeVar('T')
|
||||
GroupT = TypeVar('GroupT', bound='Union[Group, Cog]')
|
||||
Coro = Coroutine[Any, Any, T]
|
||||
Error = Union[
|
||||
Callable[[GroupT, Interaction, AppCommandError], Coro[Any]],
|
||||
Callable[[Interaction, AppCommandError], Coro[Any]],
|
||||
Callable[[GroupT, 'Interaction', AppCommandError], Coro[Any]],
|
||||
Callable[['Interaction', AppCommandError], Coro[Any]],
|
||||
]
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
CommandCallback = Union[
|
||||
Callable[Concatenate[GroupT, Interaction, P], Coro[T]],
|
||||
Callable[Concatenate[Interaction, P], Coro[T]],
|
||||
Callable[Concatenate[GroupT, 'Interaction', P], Coro[T]],
|
||||
Callable[Concatenate['Interaction', P], Coro[T]],
|
||||
]
|
||||
|
||||
ContextMenuCallback = Union[
|
||||
# If groups end up support context menus these would be uncommented
|
||||
# Callable[[GroupT, Interaction, Member], Coro[Any]],
|
||||
# Callable[[GroupT, Interaction, User], Coro[Any]],
|
||||
# Callable[[GroupT, Interaction, Message], Coro[Any]],
|
||||
# Callable[[GroupT, Interaction, Union[Member, User]], Coro[Any]],
|
||||
Callable[[Interaction, Member], Coro[Any]],
|
||||
Callable[[Interaction, User], Coro[Any]],
|
||||
Callable[[Interaction, Message], Coro[Any]],
|
||||
Callable[[Interaction, Union[Member, User]], Coro[Any]],
|
||||
# Callable[[GroupT, 'Interaction', Member], Coro[Any]],
|
||||
# Callable[[GroupT, 'Interaction', User], Coro[Any]],
|
||||
# Callable[[GroupT, 'Interaction', Message], Coro[Any]],
|
||||
# Callable[[GroupT, 'Interaction', Union[Member, User]], Coro[Any]],
|
||||
Callable[['Interaction', Member], Coro[Any]],
|
||||
Callable[['Interaction', User], Coro[Any]],
|
||||
Callable[['Interaction', Message], Coro[Any]],
|
||||
Callable[['Interaction', Union[Member, User]], Coro[Any]],
|
||||
]
|
||||
|
||||
AutocompleteCallback = Union[
|
||||
Callable[[GroupT, Interaction, ChoiceT, Namespace], Coro[List[Choice[ChoiceT]]]],
|
||||
Callable[[Interaction, ChoiceT, Namespace], Coro[List[Choice[ChoiceT]]]],
|
||||
Callable[[GroupT, 'Interaction', ChoiceT, Namespace], Coro[List[Choice[ChoiceT]]]],
|
||||
Callable[['Interaction', ChoiceT, Namespace], Coro[List[Choice[ChoiceT]]]],
|
||||
]
|
||||
else:
|
||||
CommandCallback = Callable[..., Coro[T]]
|
||||
|
@@ -25,7 +25,6 @@ DEALINGS IN THE SOFTWARE.
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, NamedTuple, Tuple
|
||||
from ..interactions import Interaction
|
||||
from ..member import Member
|
||||
from ..object import Object
|
||||
from ..role import Role
|
||||
@@ -35,6 +34,7 @@ from ..enums import AppCommandOptionType
|
||||
from .models import AppCommandChannel, AppCommandThread
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..interactions import Interaction
|
||||
from ..types.interactions import ResolvedData, ApplicationCommandInteractionDataOption
|
||||
|
||||
__all__ = ('Namespace',)
|
||||
|
@@ -878,10 +878,69 @@ class CommandTree(Generic[ClientT]):
|
||||
|
||||
self.client.loop.create_task(wrapper(), name='CommandTree-invoker')
|
||||
|
||||
def _get_context_menu(self, data: ApplicationCommandInteractionData) -> Optional[ContextMenu]:
|
||||
name = data['name']
|
||||
guild_id = _get_as_snowflake(data, 'guild_id')
|
||||
return self._context_menus.get((name, guild_id, data.get('type', 1)))
|
||||
|
||||
def _get_app_command_options(
|
||||
self, data: ApplicationCommandInteractionData
|
||||
) -> Tuple[Command[Any, ..., Any], List[ApplicationCommandInteractionDataOption]]:
|
||||
parents: List[str] = []
|
||||
name = data['name']
|
||||
|
||||
command_guild_id = _get_as_snowflake(data, 'guild_id')
|
||||
if command_guild_id:
|
||||
try:
|
||||
guild_commands = self._guild_commands[command_guild_id]
|
||||
except KeyError:
|
||||
command = None
|
||||
else:
|
||||
command = guild_commands.get(name)
|
||||
else:
|
||||
command = self._global_commands.get(name)
|
||||
|
||||
# If it's not found at this point then it's not gonna be found at any point
|
||||
if command is None:
|
||||
raise CommandNotFound(name, parents)
|
||||
|
||||
# This could be done recursively but it'd be a bother due to the state needed
|
||||
# to be tracked above like the parents, the actual command type, and the
|
||||
# resulting options we care about
|
||||
searching = True
|
||||
options: List[ApplicationCommandInteractionDataOption] = data.get('options', [])
|
||||
while searching:
|
||||
for option in options:
|
||||
# Find subcommands
|
||||
if option.get('type', 0) in (1, 2):
|
||||
parents.append(name)
|
||||
name = option['name']
|
||||
command = command._get_internal_command(name)
|
||||
if command is None:
|
||||
raise CommandNotFound(name, parents)
|
||||
options = option.get('options', [])
|
||||
break
|
||||
else:
|
||||
searching = False
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
if isinstance(command, Group):
|
||||
# Right now, groups can't be invoked. This is a Discord limitation in how they
|
||||
# do slash commands. So if we're here and we have a Group rather than a Command instance
|
||||
# then something in the code is out of date from the data that Discord has.
|
||||
raise CommandSignatureMismatch(command)
|
||||
|
||||
return (command, options)
|
||||
|
||||
async def _call_context_menu(self, interaction: Interaction, data: ApplicationCommandInteractionData, type: int) -> None:
|
||||
name = data['name']
|
||||
guild_id = _get_as_snowflake(data, 'guild_id')
|
||||
ctx_menu = self._context_menus.get((name, guild_id, type))
|
||||
# Pre-fill the cached slot to prevent re-computation
|
||||
interaction._cs_command = ctx_menu
|
||||
|
||||
if ctx_menu is None:
|
||||
raise CommandNotFound(name, [], AppCommandType(type))
|
||||
|
||||
@@ -936,56 +995,18 @@ class CommandTree(Generic[ClientT]):
|
||||
await self._call_context_menu(interaction, data, type)
|
||||
return
|
||||
|
||||
parents: List[str] = []
|
||||
name = data['name']
|
||||
command, options = self._get_app_command_options(data)
|
||||
|
||||
command_guild_id = _get_as_snowflake(data, 'guild_id')
|
||||
if command_guild_id:
|
||||
try:
|
||||
guild_commands = self._guild_commands[command_guild_id]
|
||||
except KeyError:
|
||||
command = None
|
||||
else:
|
||||
command = guild_commands.get(name)
|
||||
else:
|
||||
command = self._global_commands.get(name)
|
||||
|
||||
# If it's not found at this point then it's not gonna be found at any point
|
||||
if command is None:
|
||||
raise CommandNotFound(name, parents)
|
||||
|
||||
# This could be done recursively but it'd be a bother due to the state needed
|
||||
# to be tracked above like the parents, the actual command type, and the
|
||||
# resulting options we care about
|
||||
searching = True
|
||||
options: List[ApplicationCommandInteractionDataOption] = data.get('options', [])
|
||||
while searching:
|
||||
for option in options:
|
||||
# Find subcommands
|
||||
if option.get('type', 0) in (1, 2):
|
||||
parents.append(name)
|
||||
name = option['name']
|
||||
command = command._get_internal_command(name)
|
||||
if command is None:
|
||||
raise CommandNotFound(name, parents)
|
||||
options = option.get('options', [])
|
||||
break
|
||||
else:
|
||||
searching = False
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
if isinstance(command, Group):
|
||||
# Right now, groups can't be invoked. This is a Discord limitation in how they
|
||||
# do slash commands. So if we're here and we have a Group rather than a Command instance
|
||||
# then something in the code is out of date from the data that Discord has.
|
||||
raise CommandSignatureMismatch(command)
|
||||
# Pre-fill the cached slot to prevent re-computation
|
||||
interaction._cs_command = command
|
||||
|
||||
# At this point options refers to the arguments of the command
|
||||
# and command refers to the class type we care about
|
||||
namespace = Namespace(interaction, data.get('resolved', {}), options)
|
||||
|
||||
# Same pre-fill as above
|
||||
interaction._cs_namespace = namespace
|
||||
|
||||
# Auto complete handles the namespace differently... so at this point this is where we decide where that is.
|
||||
if interaction.type is InteractionType.autocomplete:
|
||||
focused = next((opt['name'] for opt in options if opt.get('focused')), None)
|
||||
|
Reference in New Issue
Block a user