Adding all files
This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Tuple, Union
|
||||
|
||||
from .. import utils
|
||||
from ..enums import ApplicationCommandType, Locale, OptionType, try_enum
|
||||
from ..guild import Guild
|
||||
from ..member import Member
|
||||
from ..message import Message
|
||||
from ..user import User
|
||||
from .base import ClientT, Interaction, InteractionDataResolved
|
||||
|
||||
__all__ = (
|
||||
"ApplicationCommandInteraction",
|
||||
"GuildCommandInteraction",
|
||||
"UserCommandInteraction",
|
||||
"MessageCommandInteraction",
|
||||
"ApplicationCommandInteractionData",
|
||||
"ApplicationCommandInteractionDataOption",
|
||||
"ApplicationCommandInteractionDataResolved",
|
||||
# aliases (we're trying to find out which one catches on)
|
||||
"CommandInteraction",
|
||||
"CmdInteraction",
|
||||
"CommandInter",
|
||||
"CmdInter",
|
||||
"AppCommandInteraction",
|
||||
"AppCommandInter",
|
||||
"AppCmdInter",
|
||||
)
|
||||
|
||||
MISSING = utils.MISSING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..ext.commands import InvokableApplicationCommand
|
||||
from ..state import ConnectionState
|
||||
from ..types.interactions import (
|
||||
ApplicationCommandInteraction as ApplicationCommandInteractionPayload,
|
||||
ApplicationCommandInteractionData as ApplicationCommandInteractionDataPayload,
|
||||
)
|
||||
|
||||
|
||||
class ApplicationCommandInteraction(Interaction[ClientT]):
|
||||
"""Represents an interaction with an application command.
|
||||
|
||||
Current examples are slash commands, user commands and message commands.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id: :class:`int`
|
||||
The interaction's ID.
|
||||
type: :class:`InteractionType`
|
||||
The interaction's type.
|
||||
application_id: :class:`int`
|
||||
The application ID that the interaction was for.
|
||||
guild_id: Optional[:class:`int`]
|
||||
The guild ID the interaction was sent from.
|
||||
channel: Union[:class:`abc.GuildChannel`, :class:`Thread`, :class:`abc.PrivateChannel`, :class:`PartialMessageable`]
|
||||
The channel the interaction was sent from.
|
||||
|
||||
Note that due to a Discord limitation, DM channels
|
||||
may not contain recipient information.
|
||||
Unknown channel types will be :class:`PartialMessageable`.
|
||||
|
||||
.. versionchanged:: 2.10
|
||||
If the interaction was sent from a thread and the bot cannot normally access the thread,
|
||||
this is now a proper :class:`Thread` object.
|
||||
Private channels are now proper :class:`DMChannel`/:class:`GroupChannel`
|
||||
objects instead of :class:`PartialMessageable`.
|
||||
|
||||
.. note::
|
||||
If you want to compute the interaction author's or bot's permissions in the channel,
|
||||
consider using :attr:`permissions` or :attr:`app_permissions`.
|
||||
|
||||
author: Union[:class:`User`, :class:`Member`]
|
||||
The user or member that sent the interaction.
|
||||
|
||||
.. note::
|
||||
In scenarios where an interaction occurs in a guild but :attr:`.guild` is unavailable,
|
||||
such as with user-installed applications in guilds, some attributes of :class:`Member`\\s
|
||||
that depend on the guild/role cache will not work due to an API limitation.
|
||||
This includes :attr:`~Member.roles`, :attr:`~Member.top_role`, :attr:`~Member.role_icon`,
|
||||
and :attr:`~Member.guild_permissions`.
|
||||
locale: :class:`Locale`
|
||||
The selected language of the interaction's author.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
.. versionchanged:: 2.5
|
||||
Changed to :class:`Locale` instead of :class:`str`.
|
||||
|
||||
guild_locale: Optional[:class:`Locale`]
|
||||
The selected language of the interaction's guild.
|
||||
This value is only meaningful in guilds with ``COMMUNITY`` feature and receives a default value otherwise.
|
||||
If the interaction was in a DM, then this value is ``None``.
|
||||
|
||||
.. versionadded:: 2.4
|
||||
|
||||
.. versionchanged:: 2.5
|
||||
Changed to :class:`Locale` instead of :class:`str`.
|
||||
|
||||
token: :class:`str`
|
||||
The token to continue the interaction. These are valid for 15 minutes.
|
||||
client: :class:`Client`
|
||||
The interaction client.
|
||||
entitlements: List[:class:`Entitlement`]
|
||||
The entitlements for the invoking user and guild,
|
||||
representing access to an application subscription.
|
||||
|
||||
.. versionadded:: 2.10
|
||||
|
||||
authorizing_integration_owners: :class:`AuthorizingIntegrationOwners`
|
||||
Details about the authorizing user/guild for the application installation
|
||||
related to the interaction.
|
||||
|
||||
.. versionadded:: 2.10
|
||||
|
||||
context: :class:`InteractionContextTypes`
|
||||
The context where the interaction was triggered from.
|
||||
|
||||
This is a flag object, with exactly one of the flags set to ``True``.
|
||||
To check whether an interaction originated from e.g. a :attr:`~InteractionContextTypes.guild`
|
||||
context, you can use ``if interaction.context.guild:``.
|
||||
|
||||
.. versionadded:: 2.10
|
||||
|
||||
attachment_size_limit: :class:`int`
|
||||
The maximum number of bytes files can have in responses to this interaction.
|
||||
|
||||
This may be higher than the default limit, depending on the guild's boost
|
||||
status or the invoking user's nitro status.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
|
||||
data: :class:`ApplicationCommandInteractionData`
|
||||
The wrapped interaction data.
|
||||
application_command: :class:`.InvokableApplicationCommand`
|
||||
The command invoked by the interaction.
|
||||
command_failed: :class:`bool`
|
||||
Whether the command failed to be checked or invoked.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, *, data: ApplicationCommandInteractionPayload, state: ConnectionState
|
||||
) -> None:
|
||||
super().__init__(data=data, state=state)
|
||||
self.data: ApplicationCommandInteractionData = ApplicationCommandInteractionData(
|
||||
data=data["data"], parent=self
|
||||
)
|
||||
self.application_command: InvokableApplicationCommand = MISSING
|
||||
self.command_failed: bool = False
|
||||
|
||||
@property
|
||||
def target(self) -> Optional[Union[User, Member, Message]]:
|
||||
"""Optional[Union[:class:`abc.User`, :class:`Message`]]: The user or message targeted by a user or message command"""
|
||||
return self.data.target
|
||||
|
||||
@property
|
||||
def options(self) -> Dict[str, Any]:
|
||||
"""Dict[:class:`str`, :class:`Any`]: The full option tree, including nestings"""
|
||||
return {opt.name: opt._simplified_value() for opt in self.data.options}
|
||||
|
||||
@property
|
||||
def filled_options(self) -> Dict[str, Any]:
|
||||
"""Dict[:class:`str`, :class:`Any`]: The options of the command (or sub-command) being invoked"""
|
||||
_, kwargs = self.data._get_chain_and_kwargs()
|
||||
return kwargs
|
||||
|
||||
|
||||
# TODO(3.0): consider making these classes @type_check_only and not affect runtime behavior, or even remove entirely
|
||||
class GuildCommandInteraction(ApplicationCommandInteraction[ClientT]):
|
||||
"""An :class:`ApplicationCommandInteraction` subclass, primarily meant for annotations.
|
||||
|
||||
This restricts the command to only be usable in guilds and only as a guild-installed command,
|
||||
by automatically setting :attr:`ApplicationCommand.contexts` to :attr:`~InteractionContextTypes.guild` only
|
||||
and :attr:`ApplicationCommand.install_types` to :attr:`~ApplicationInstallTypes.guild` only.
|
||||
Note that this does not apply to slash subcommands, subcommand groups, or autocomplete callbacks.
|
||||
|
||||
Additionally, the type annotations of :attr:`~Interaction.author`, :attr:`~Interaction.guild`,
|
||||
:attr:`~Interaction.guild_id`, :attr:`~Interaction.guild_locale`, and :attr:`~Interaction.me`
|
||||
are modified to match the expected types in guilds.
|
||||
"""
|
||||
|
||||
author: Member
|
||||
guild: Guild
|
||||
guild_id: int
|
||||
guild_locale: Locale
|
||||
me: Member
|
||||
|
||||
|
||||
class UserCommandInteraction(ApplicationCommandInteraction[ClientT]):
|
||||
"""An :class:`ApplicationCommandInteraction` subclass meant for annotations
|
||||
in user context menu commands.
|
||||
|
||||
No runtime behavior is changed, but the type annotations of :attr:`~ApplicationCommandInteraction.target`
|
||||
are modified to match the expected type with user commands.
|
||||
"""
|
||||
|
||||
target: Union[User, Member]
|
||||
|
||||
|
||||
class MessageCommandInteraction(ApplicationCommandInteraction[ClientT]):
|
||||
"""An :class:`ApplicationCommandInteraction` subclass meant for annotations
|
||||
in message context menu commands.
|
||||
|
||||
No runtime behavior is changed, but the type annotations of :attr:`~ApplicationCommandInteraction.target`
|
||||
are modified to match the expected type with message commands.
|
||||
"""
|
||||
|
||||
target: Message
|
||||
|
||||
|
||||
class ApplicationCommandInteractionData(Dict[str, Any]):
|
||||
"""Represents the data of an interaction with an application command.
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id: :class:`int`
|
||||
The application command ID.
|
||||
name: :class:`str`
|
||||
The application command name.
|
||||
type: :class:`ApplicationCommandType`
|
||||
The application command type.
|
||||
resolved: :class:`InteractionDataResolved`
|
||||
All resolved objects related to this interaction.
|
||||
options: List[:class:`ApplicationCommandInteractionDataOption`]
|
||||
A list of options from the API.
|
||||
target_id: :class:`int`
|
||||
ID of the user or message targeted by a user or message command
|
||||
target: Union[:class:`User`, :class:`Member`, :class:`Message`]
|
||||
The user or message targeted by a user or message command
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"id",
|
||||
"name",
|
||||
"type",
|
||||
"target_id",
|
||||
"target",
|
||||
"resolved",
|
||||
"options",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
data: ApplicationCommandInteractionDataPayload,
|
||||
parent: ApplicationCommandInteraction[ClientT],
|
||||
) -> None:
|
||||
super().__init__(data)
|
||||
self.id: int = int(data["id"])
|
||||
self.name: str = data["name"]
|
||||
self.type: ApplicationCommandType = try_enum(ApplicationCommandType, data["type"])
|
||||
|
||||
self.resolved = InteractionDataResolved(data=data.get("resolved", {}), parent=parent)
|
||||
self.target_id: Optional[int] = utils._get_as_snowflake(data, "target_id")
|
||||
target = self.resolved.get_by_id(self.target_id)
|
||||
self.target: Optional[Union[User, Member, Message]] = target # type: ignore
|
||||
|
||||
self.options: List[ApplicationCommandInteractionDataOption] = [
|
||||
ApplicationCommandInteractionDataOption(data=d, resolved=self.resolved)
|
||||
for d in data.get("options", [])
|
||||
]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<ApplicationCommandInteractionData id={self.id!r} name={self.name!r} type={self.type!r} "
|
||||
f"target_id={self.target_id!r} target={self.target!r} resolved={self.resolved!r} options={self.options!r}>"
|
||||
)
|
||||
|
||||
def _get_chain_and_kwargs(
|
||||
self, chain: Optional[Tuple[str, ...]] = None
|
||||
) -> Tuple[Tuple[str, ...], Dict[str, Any]]:
|
||||
"""Returns a chain of sub-command names and a dict of filled options."""
|
||||
if chain is None:
|
||||
chain = ()
|
||||
for option in self.options:
|
||||
if option.value is None:
|
||||
# Extend the chain and collect kwargs in the nesting
|
||||
return option._get_chain_and_kwargs((*chain, option.name))
|
||||
return chain, {o.name: o.value for o in self.options}
|
||||
return chain, {}
|
||||
|
||||
def _get_focused_option(self) -> Optional[ApplicationCommandInteractionDataOption]:
|
||||
for option in self.options:
|
||||
if option.focused:
|
||||
return option
|
||||
if option.value is None:
|
||||
# This means that we're inside a group/subcmd now
|
||||
# We can use 'return' here because user can only
|
||||
# choose one subcommand per interaction
|
||||
return option._get_focused_option()
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def focused_option(self) -> ApplicationCommandInteractionDataOption:
|
||||
"""The focused option"""
|
||||
# don't annotate as None for user experience
|
||||
return self._get_focused_option() # type: ignore
|
||||
|
||||
|
||||
class ApplicationCommandInteractionDataOption(Dict[str, Any]):
|
||||
"""Represents the structure of an interaction data option from the API.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name: :class:`str`
|
||||
The option's name.
|
||||
type: :class:`OptionType`
|
||||
The option's type.
|
||||
value: :class:`Any`
|
||||
The option's value.
|
||||
options: List[:class:`ApplicationCommandInteractionDataOption`]
|
||||
The list of options of this option. Only exists for subcommands and groups.
|
||||
focused: :class:`bool`
|
||||
Whether this option is focused by the user. May be ``True`` in
|
||||
autocomplete interactions.
|
||||
"""
|
||||
|
||||
__slots__ = ("name", "type", "value", "options", "focused")
|
||||
|
||||
def __init__(self, *, data: Mapping[str, Any], resolved: InteractionDataResolved) -> None:
|
||||
super().__init__(data)
|
||||
self.name: str = data["name"]
|
||||
self.type: OptionType = try_enum(OptionType, data["type"])
|
||||
|
||||
self.value: Any = None
|
||||
if (value := data.get("value")) is not None:
|
||||
self.value: Any = resolved.get_with_type(value, self.type, value)
|
||||
|
||||
self.options: List[ApplicationCommandInteractionDataOption] = [
|
||||
ApplicationCommandInteractionDataOption(data=d, resolved=resolved)
|
||||
for d in data.get("options", [])
|
||||
]
|
||||
self.focused: bool = data.get("focused", False)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<ApplicationCommandInteractionDataOption name={self.name!r} type={self.type!r}>"
|
||||
f"value={self.value!r} focused={self.focused!r} options={self.options!r}>"
|
||||
)
|
||||
|
||||
def _simplified_value(self) -> Any:
|
||||
if self.value is not None:
|
||||
return self.value
|
||||
return {opt.name: opt._simplified_value() for opt in self.options}
|
||||
|
||||
def _get_focused_option(self) -> Optional[ApplicationCommandInteractionDataOption]:
|
||||
for option in self.options:
|
||||
if option.focused:
|
||||
return option
|
||||
if option.value is None:
|
||||
return option._get_focused_option()
|
||||
return None
|
||||
|
||||
def _get_chain_and_kwargs(
|
||||
self, chain: Optional[Tuple[str, ...]] = None
|
||||
) -> Tuple[Tuple[str, ...], Dict[str, Any]]:
|
||||
if chain is None:
|
||||
chain = ()
|
||||
for option in self.options:
|
||||
if option.value is None:
|
||||
# Extend the chain and collect kwargs in the nesting
|
||||
return option._get_chain_and_kwargs((*chain, option.name))
|
||||
return chain, {o.name: o.value for o in self.options}
|
||||
return chain, {}
|
||||
|
||||
|
||||
# backwards compatibility
|
||||
ApplicationCommandInteractionDataResolved = InteractionDataResolved
|
||||
|
||||
|
||||
# People asked about shorter aliases, let's see which one catches on the most
|
||||
CommandInteraction = ApplicationCommandInteraction
|
||||
CmdInteraction = ApplicationCommandInteraction
|
||||
CommandInter = ApplicationCommandInteraction
|
||||
CmdInter = ApplicationCommandInteraction
|
||||
AppCommandInteraction = ApplicationCommandInteraction
|
||||
AppCommandInter = ApplicationCommandInteraction
|
||||
AppCmdInter = ApplicationCommandInteraction
|
||||
Reference in New Issue
Block a user