mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-07-03 16:50:03 +00:00
Add support for setting interaction responses
This commit is contained in:
parent
85758a75b3
commit
3b83f60b35
@ -446,6 +446,7 @@ class InteractionResponseType(Enum):
|
|||||||
channel_message = 4 # (with source)
|
channel_message = 4 # (with source)
|
||||||
deferred_channel_message = 5 # (with source)
|
deferred_channel_message = 5 # (with source)
|
||||||
deferred_message_update = 6 # for components
|
deferred_message_update = 6 # for components
|
||||||
|
message_update = 7 # for components
|
||||||
|
|
||||||
class VideoQualityMode(Enum):
|
class VideoQualityMode(Enum):
|
||||||
auto = 1
|
auto = 1
|
||||||
|
@ -25,18 +25,21 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import Optional, TYPE_CHECKING, Tuple, Union
|
from discord.types.interactions import InteractionResponse
|
||||||
|
from typing import Any, Dict, List, Optional, TYPE_CHECKING, Tuple, Union
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
from .enums import try_enum, InteractionType
|
from .enums import try_enum, InteractionType, InteractionResponseType
|
||||||
|
|
||||||
from .user import User
|
from .user import User
|
||||||
from .member import Member
|
from .member import Member
|
||||||
from .message import Message
|
from .message import Message, Attachment
|
||||||
from .object import Object
|
from .object import Object
|
||||||
|
from .webhook.async_ import async_context
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Interaction',
|
'Interaction',
|
||||||
|
'InteractionResponse',
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -45,6 +48,12 @@ if TYPE_CHECKING:
|
|||||||
)
|
)
|
||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .abc import GuildChannel
|
from .abc import GuildChannel
|
||||||
|
from .state import ConnectionState
|
||||||
|
from aiohttp import ClientSession
|
||||||
|
from .embeds import Embed
|
||||||
|
from .ui.view import View
|
||||||
|
|
||||||
|
MISSING: Any = utils.MISSING
|
||||||
|
|
||||||
|
|
||||||
class Interaction:
|
class Interaction:
|
||||||
@ -89,10 +98,13 @@ class Interaction:
|
|||||||
'token',
|
'token',
|
||||||
'version',
|
'version',
|
||||||
'_state',
|
'_state',
|
||||||
|
'_session',
|
||||||
|
'_cs_response',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *, data: InteractionPayload, state=None):
|
def __init__(self, *, data: InteractionPayload, state: ConnectionState):
|
||||||
self._state = state
|
self._state = state
|
||||||
|
self._session: ClientSession = state.http._HTTPClient__session
|
||||||
self._from_data(data)
|
self._from_data(data)
|
||||||
|
|
||||||
def _from_data(self, data: InteractionPayload):
|
def _from_data(self, data: InteractionPayload):
|
||||||
@ -126,7 +138,6 @@ class Interaction:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def guild(self) -> Optional[Guild]:
|
def guild(self) -> Optional[Guild]:
|
||||||
"""Optional[:class:`Guild`]: The guild the interaction was sent from."""
|
"""Optional[:class:`Guild`]: The guild the interaction was sent from."""
|
||||||
@ -141,3 +152,231 @@ class Interaction:
|
|||||||
"""
|
"""
|
||||||
guild = self.guild
|
guild = self.guild
|
||||||
return guild and guild.get_channel(self.channel_id)
|
return guild and guild.get_channel(self.channel_id)
|
||||||
|
|
||||||
|
@utils.cached_slot_property('_cs_response')
|
||||||
|
def response(self) -> InteractionResponse:
|
||||||
|
""":class:`InteractionResponse`: Returns an object responsible for handling responding to the interaction."""
|
||||||
|
return InteractionResponse(self)
|
||||||
|
|
||||||
|
|
||||||
|
class InteractionResponse:
|
||||||
|
"""Represents a Discord interaction response.
|
||||||
|
|
||||||
|
This type can be accessed through :attr:`Interaction.response`.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__: Tuple[str, ...] = (
|
||||||
|
'_responded',
|
||||||
|
'_parent',
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, parent: Interaction):
|
||||||
|
self._parent: Interaction = parent
|
||||||
|
self._responded: bool = False
|
||||||
|
|
||||||
|
async def defer(self, *, ephemeral: bool = False) -> None:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Defers the interaction response.
|
||||||
|
|
||||||
|
This is typically used when the interaction is acknowledged
|
||||||
|
and a secondary action will be done later.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
ephemeral: :class:`bool`
|
||||||
|
Indicates whether the deferred message will eventually be ephemeral.
|
||||||
|
This only applies for interactions of type :attr:`InteractionType.application_command`.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
HTTPException
|
||||||
|
Deferring the interaction failed.
|
||||||
|
"""
|
||||||
|
if self._responded:
|
||||||
|
return
|
||||||
|
|
||||||
|
defer_type: int = 0
|
||||||
|
data: Optional[Dict[str, Any]] = None
|
||||||
|
parent = self._parent
|
||||||
|
if parent.type is InteractionType.component:
|
||||||
|
defer_type = InteractionResponseType.deferred_message_update.value
|
||||||
|
elif parent.type is InteractionType.application_command:
|
||||||
|
defer_type = InteractionResponseType.deferred_channel_message.value
|
||||||
|
if ephemeral:
|
||||||
|
data = {'flags': 64}
|
||||||
|
|
||||||
|
if defer_type:
|
||||||
|
adapter = async_context.get()
|
||||||
|
await adapter.create_interaction_response(
|
||||||
|
parent.id, parent.token, session=parent._session, type=defer_type, data=data
|
||||||
|
)
|
||||||
|
self._responded = True
|
||||||
|
|
||||||
|
async def pong(self) -> None:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Pongs the ping interaction.
|
||||||
|
|
||||||
|
This should rarely be used.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
HTTPException
|
||||||
|
Ponging the interaction failed.
|
||||||
|
"""
|
||||||
|
if self._responded:
|
||||||
|
return
|
||||||
|
|
||||||
|
parent = self._parent
|
||||||
|
if parent.type is InteractionType.ping:
|
||||||
|
adapter = async_context.get()
|
||||||
|
await adapter.create_interaction_response(
|
||||||
|
parent.id, parent.token, session=parent._session, type=InteractionResponseType.pong.value
|
||||||
|
)
|
||||||
|
self._responded = True
|
||||||
|
|
||||||
|
async def send_message(
|
||||||
|
self,
|
||||||
|
content: Optional[Any] = None,
|
||||||
|
*,
|
||||||
|
embed: Embed = MISSING,
|
||||||
|
embeds: List[Embed] = MISSING,
|
||||||
|
tts: bool = False,
|
||||||
|
ephemeral: bool = False,
|
||||||
|
) -> None:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Responds to this interaction by sending a message.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
content: Optional[:class:`str`]
|
||||||
|
The content of the message to send.
|
||||||
|
embeds: List[:class:`Embed`]
|
||||||
|
A list of embeds to send with the content. Maximum of 10. This cannot
|
||||||
|
be mixed with the ``embed`` parameter.
|
||||||
|
embed: :class:`Embed`
|
||||||
|
The rich embed for the content to send. This cannot be mixed with
|
||||||
|
``embeds`` parameter.
|
||||||
|
tts: :class:`bool`
|
||||||
|
Indicates if the message should be sent using text-to-speech.
|
||||||
|
ephemeral: :class:`bool`
|
||||||
|
Indicates if the message should only be visible to the user who started the interaction.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
HTTPException
|
||||||
|
Sending the message failed.
|
||||||
|
TypeError
|
||||||
|
You specified both ``embed`` and ``embeds``.
|
||||||
|
ValueError
|
||||||
|
The length of ``embeds`` was invalid.
|
||||||
|
"""
|
||||||
|
if self._responded:
|
||||||
|
return
|
||||||
|
|
||||||
|
payload: Dict[str, Any] = {
|
||||||
|
'tts': tts,
|
||||||
|
}
|
||||||
|
|
||||||
|
if embed is not MISSING and embeds is not MISSING:
|
||||||
|
raise TypeError('cannot mix embed and embeds keyword arguments')
|
||||||
|
|
||||||
|
if embed is not MISSING:
|
||||||
|
embeds = [embed]
|
||||||
|
|
||||||
|
if embeds:
|
||||||
|
if len(embeds) > 10:
|
||||||
|
raise ValueError('embeds cannot exceed maximum of 10 elements')
|
||||||
|
payload['embeds'] = [e.to_dict() for e in embeds]
|
||||||
|
|
||||||
|
if content is not None:
|
||||||
|
payload['content'] = str(content)
|
||||||
|
|
||||||
|
if ephemeral:
|
||||||
|
payload['flags'] = 64
|
||||||
|
|
||||||
|
parent = self._parent
|
||||||
|
adapter = async_context.get()
|
||||||
|
await adapter.create_interaction_response(
|
||||||
|
parent.id,
|
||||||
|
parent.token,
|
||||||
|
session=parent._session,
|
||||||
|
type=InteractionResponseType.channel_message.value,
|
||||||
|
data=payload,
|
||||||
|
)
|
||||||
|
self._responded = True
|
||||||
|
|
||||||
|
async def edit_message(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
content: Optional[Any] = MISSING,
|
||||||
|
embed: Optional[Embed] = MISSING,
|
||||||
|
attachments: List[Attachment] = MISSING,
|
||||||
|
view: Optional[View] = MISSING,
|
||||||
|
) -> None:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Responds to this interaction by editing the original message of
|
||||||
|
a component interaction.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
content: Optional[:class:`str`]
|
||||||
|
The new content to replace the message with. ``None`` removes the content.
|
||||||
|
embed: Optional[:class:`Embed`]
|
||||||
|
The new embed to replace the embed with. ``None`` removes the embed.
|
||||||
|
attachments: List[:class:`Attachment`]
|
||||||
|
A list of attachments to keep in the message. If ``[]`` is passed
|
||||||
|
then all attachments are removed.
|
||||||
|
view: Optional[:class:`~discord.ui.View`]
|
||||||
|
The updated view to update this message with. If ``None`` is passed then
|
||||||
|
the view is removed.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
HTTPException
|
||||||
|
Editing the message failed.
|
||||||
|
"""
|
||||||
|
if self._responded:
|
||||||
|
return
|
||||||
|
|
||||||
|
parent = self._parent
|
||||||
|
if parent.type is not InteractionType.component:
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO: embeds: List[Embed]?
|
||||||
|
payload = {}
|
||||||
|
if content is not MISSING:
|
||||||
|
if content is None:
|
||||||
|
payload['content'] = None
|
||||||
|
else:
|
||||||
|
payload['content'] = str(content)
|
||||||
|
|
||||||
|
if embed is not MISSING:
|
||||||
|
if embed is None:
|
||||||
|
payload['embed'] = None
|
||||||
|
else:
|
||||||
|
payload['embed'] = embed.to_dict()
|
||||||
|
|
||||||
|
if attachments is not MISSING:
|
||||||
|
payload['attachments'] = [a.to_dict() for a in attachments]
|
||||||
|
|
||||||
|
if view is not MISSING:
|
||||||
|
if view is None:
|
||||||
|
payload['components'] = []
|
||||||
|
else:
|
||||||
|
payload['components'] = view.to_components()
|
||||||
|
|
||||||
|
adapter = async_context.get()
|
||||||
|
await adapter.create_interaction_response(
|
||||||
|
parent.id,
|
||||||
|
parent.token,
|
||||||
|
session=parent._session,
|
||||||
|
type=InteractionResponseType.message_update.value,
|
||||||
|
data=payload,
|
||||||
|
)
|
||||||
|
self._responded = True
|
||||||
|
25
docs/api.rst
25
docs/api.rst
@ -1188,17 +1188,30 @@ of :class:`enum.Enum`.
|
|||||||
.. attribute:: pong
|
.. attribute:: pong
|
||||||
|
|
||||||
Pongs the interaction when given a ping.
|
Pongs the interaction when given a ping.
|
||||||
|
|
||||||
|
See also :meth:`InteractionResponse.pong`
|
||||||
.. attribute:: channel_message
|
.. attribute:: channel_message
|
||||||
|
|
||||||
Respond to a slash command with a message.
|
Respond to the interaction with a message.
|
||||||
|
|
||||||
|
See also :meth:`InteractionResponse.send_message`
|
||||||
.. attribute:: deferred_channel_message
|
.. attribute:: deferred_channel_message
|
||||||
|
|
||||||
Responds to a slash command with a message at a later time.
|
Responds to the interaction with a message at a later time.
|
||||||
|
|
||||||
|
See also :meth:`InteractionResponse.defer`
|
||||||
.. attribute:: deferred_message_update
|
.. attribute:: deferred_message_update
|
||||||
|
|
||||||
Acknowledges the component interaction with a promise that
|
Acknowledges the component interaction with a promise that
|
||||||
the message will update later (though there is no need to actually update the message).
|
the message will update later (though there is no need to actually update the message).
|
||||||
|
|
||||||
|
See also :meth:`InteractionResponse.defer`
|
||||||
|
.. attribute:: message_update
|
||||||
|
|
||||||
|
Responds to the interaction by editing the message.
|
||||||
|
|
||||||
|
See also :meth:`InteractionResponse.edit_message`
|
||||||
|
|
||||||
.. class:: ComponentType
|
.. class:: ComponentType
|
||||||
|
|
||||||
Represents the component type of a component.
|
Represents the component type of a component.
|
||||||
@ -2951,6 +2964,14 @@ Interaction
|
|||||||
.. autoclass:: Interaction()
|
.. autoclass:: Interaction()
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
InteractionResponse
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. attributetable:: InteractionResponse
|
||||||
|
|
||||||
|
.. autoclass:: InteractionResponse()
|
||||||
|
:members:
|
||||||
|
|
||||||
Member
|
Member
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user