807 lines
27 KiB
Python
807 lines
27 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
The MIT License (MIT)
|
|
|
|
Copyright (c) 2015-present Rapptz
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
from typing import Any, Dict, List, Optional, TYPE_CHECKING, Tuple, Union
|
|
import asyncio
|
|
|
|
from . import utils
|
|
from .enums import try_enum, InteractionType, InteractionResponseType
|
|
from .errors import InteractionResponded, HTTPException, ClientException
|
|
from .channel import PartialMessageable, ChannelType
|
|
|
|
from .user import User
|
|
from .member import Member
|
|
from .message import Message, Attachment
|
|
from .object import Object
|
|
from .permissions import Permissions
|
|
from .webhook.async_ import async_context, Webhook, handle_message_parameters
|
|
|
|
__all__ = (
|
|
"Interaction",
|
|
"InteractionMessage",
|
|
"InteractionResponse",
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from datetime import datetime
|
|
|
|
from .types.interactions import (
|
|
Interaction as InteractionPayload,
|
|
ApplicationCommandOptionChoice,
|
|
InteractionData,
|
|
)
|
|
from .guild import Guild
|
|
from .state import ConnectionState
|
|
from .file import File
|
|
from .mentions import AllowedMentions
|
|
from aiohttp import ClientSession
|
|
from .embeds import Embed
|
|
from .ui.view import View
|
|
from .channel import TextChannel, CategoryChannel, StoreChannel, PartialMessageable
|
|
from .threads import Thread
|
|
|
|
InteractionChannel = Union[TextChannel, CategoryChannel, StoreChannel, Thread, PartialMessageable]
|
|
|
|
MISSING: Any = utils.MISSING
|
|
|
|
|
|
class Interaction:
|
|
"""Represents a Discord interaction.
|
|
|
|
An interaction happens when a user does an action that needs to
|
|
be notified. Current examples are slash commands and components.
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
Attributes
|
|
-----------
|
|
id: :class:`int`
|
|
The interaction's ID.
|
|
type: :class:`InteractionType`
|
|
The interaction type.
|
|
guild_id: Optional[:class:`int`]
|
|
The guild ID the interaction was sent from.
|
|
channel_id: Optional[:class:`int`]
|
|
The channel ID the interaction was sent from.
|
|
application_id: :class:`int`
|
|
The application ID that the interaction was for.
|
|
user: Optional[Union[:class:`User`, :class:`Member`]]
|
|
The user or member that sent the interaction.
|
|
message: Optional[:class:`Message`]
|
|
The message that sent this interaction.
|
|
token: :class:`str`
|
|
The token to continue the interaction. These are valid
|
|
for 15 minutes.
|
|
data: :class:`dict`
|
|
The raw interaction data.
|
|
"""
|
|
|
|
__slots__: Tuple[str, ...] = (
|
|
"id",
|
|
"type",
|
|
"guild_id",
|
|
"channel_id",
|
|
"data",
|
|
"application_id",
|
|
"message",
|
|
"user",
|
|
"token",
|
|
"version",
|
|
"_permissions",
|
|
"_state",
|
|
"_session",
|
|
"_original_message",
|
|
"_cs_response",
|
|
"_cs_followup",
|
|
"_cs_channel",
|
|
)
|
|
|
|
def __init__(self, *, data: InteractionPayload, state: ConnectionState):
|
|
self._state: ConnectionState = state
|
|
self._session: ClientSession = state.http._HTTPClient__session
|
|
self._original_message: Optional[InteractionMessage] = None
|
|
self._from_data(data)
|
|
|
|
def _from_data(self, data: InteractionPayload):
|
|
self.id: int = int(data["id"])
|
|
self.type: InteractionType = try_enum(InteractionType, data["type"])
|
|
self.data: Optional[InteractionData] = data.get("data")
|
|
self.token: str = data["token"]
|
|
self.version: int = data["version"]
|
|
self.channel_id: Optional[int] = utils._get_as_snowflake(data, "channel_id")
|
|
self.guild_id: Optional[int] = utils._get_as_snowflake(data, "guild_id")
|
|
self.application_id: int = int(data["application_id"])
|
|
|
|
self.message: Optional[Message]
|
|
try:
|
|
self.message = Message(state=self._state, channel=self.channel, data=data["message"]) # type: ignore
|
|
except KeyError:
|
|
self.message = None
|
|
|
|
self.user: Optional[Union[User, Member]] = None
|
|
self._permissions: int = 0
|
|
|
|
# TODO: there's a potential data loss here
|
|
if self.guild_id:
|
|
guild = self.guild or Object(id=self.guild_id)
|
|
try:
|
|
member = data["member"] # type: ignore
|
|
except KeyError:
|
|
pass
|
|
else:
|
|
self.user = Member(state=self._state, guild=guild, data=member) # type: ignore
|
|
self._permissions = int(member.get("permissions", 0))
|
|
else:
|
|
try:
|
|
self.user = User(state=self._state, data=data["user"])
|
|
except KeyError:
|
|
pass
|
|
|
|
@property
|
|
def guild(self) -> Optional[Guild]:
|
|
"""Optional[:class:`Guild`]: The guild the interaction was sent from."""
|
|
return self._state and self._state._get_guild(self.guild_id)
|
|
|
|
@utils.cached_slot_property("_cs_channel")
|
|
def channel(self) -> Optional[InteractionChannel]:
|
|
"""Optional[Union[:class:`abc.GuildChannel`, :class:`PartialMessageable`, :class:`Thread`]]: The channel the interaction was sent from.
|
|
|
|
Note that due to a Discord limitation, DM channels are not resolved since there is
|
|
no data to complete them. These are :class:`PartialMessageable` instead.
|
|
"""
|
|
guild = self.guild
|
|
channel = guild and guild._resolve_channel(self.channel_id)
|
|
if channel is None:
|
|
if self.channel_id is not None:
|
|
type = ChannelType.text if self.guild_id is not None else ChannelType.private
|
|
return PartialMessageable(state=self._state, id=self.channel_id, type=type)
|
|
return None
|
|
return channel # type: ignore
|
|
|
|
@property
|
|
def permissions(self) -> Permissions:
|
|
""":class:`Permissions`: The resolved permissions of the member in the channel, including overwrites.
|
|
|
|
In a non-guild context where this doesn't apply, an empty permissions object is returned.
|
|
"""
|
|
return Permissions(self._permissions)
|
|
|
|
@utils.cached_slot_property("_cs_response")
|
|
def response(self) -> InteractionResponse:
|
|
""":class:`InteractionResponse`: Returns an object responsible for handling responding to the interaction.
|
|
|
|
A response can only be done once. If secondary messages need to be sent, consider using :attr:`followup`
|
|
instead.
|
|
"""
|
|
return InteractionResponse(self)
|
|
|
|
@utils.cached_slot_property("_cs_followup")
|
|
def followup(self) -> Webhook:
|
|
""":class:`Webhook`: Returns the follow up webhook for follow up interactions."""
|
|
payload = {
|
|
"id": self.application_id,
|
|
"type": 3,
|
|
"token": self.token,
|
|
}
|
|
return Webhook.from_state(data=payload, state=self._state)
|
|
|
|
async def original_message(self) -> InteractionMessage:
|
|
"""|coro|
|
|
|
|
Fetches the original interaction response message associated with the interaction.
|
|
|
|
If the interaction response was :meth:`InteractionResponse.send_message` then this would
|
|
return the message that was sent using that response. Otherwise, this would return
|
|
the message that triggered the interaction.
|
|
|
|
Repeated calls to this will return a cached value.
|
|
|
|
Raises
|
|
-------
|
|
HTTPException
|
|
Fetching the original response message failed.
|
|
ClientException
|
|
The channel for the message could not be resolved.
|
|
|
|
Returns
|
|
--------
|
|
InteractionMessage
|
|
The original interaction response message.
|
|
"""
|
|
|
|
if self._original_message is not None:
|
|
return self._original_message
|
|
|
|
# TODO: fix later to not raise?
|
|
channel = self.channel
|
|
if channel is None:
|
|
raise ClientException("Channel for message could not be resolved")
|
|
|
|
adapter = async_context.get()
|
|
data = await adapter.get_original_interaction_response(
|
|
application_id=self.application_id,
|
|
token=self.token,
|
|
session=self._session,
|
|
)
|
|
state = _InteractionMessageState(self, self._state)
|
|
message = InteractionMessage(state=state, channel=channel, data=data) # type: ignore
|
|
self._original_message = message
|
|
return message
|
|
|
|
async def edit_original_message(
|
|
self,
|
|
*,
|
|
content: Optional[str] = MISSING,
|
|
embeds: List[Embed] = MISSING,
|
|
embed: Optional[Embed] = MISSING,
|
|
file: File = MISSING,
|
|
files: List[File] = MISSING,
|
|
view: Optional[View] = MISSING,
|
|
allowed_mentions: Optional[AllowedMentions] = None,
|
|
) -> InteractionMessage:
|
|
"""|coro|
|
|
|
|
Edits the original interaction response message.
|
|
|
|
This is a lower level interface to :meth:`InteractionMessage.edit` in case
|
|
you do not want to fetch the message and save an HTTP request.
|
|
|
|
This method is also the only way to edit the original message if
|
|
the message sent was ephemeral.
|
|
|
|
Parameters
|
|
------------
|
|
content: Optional[:class:`str`]
|
|
The content to edit the message with or ``None`` to clear it.
|
|
embeds: List[:class:`Embed`]
|
|
A list of embeds to edit the message with.
|
|
embed: Optional[:class:`Embed`]
|
|
The embed to edit the message with. ``None`` suppresses the embeds.
|
|
This should not be mixed with the ``embeds`` parameter.
|
|
file: :class:`File`
|
|
The file to upload. This cannot be mixed with ``files`` parameter.
|
|
files: List[:class:`File`]
|
|
A list of files to send with the content. This cannot be mixed with the
|
|
``file`` parameter.
|
|
allowed_mentions: :class:`AllowedMentions`
|
|
Controls the mentions being processed in this message.
|
|
See :meth:`.abc.Messageable.send` for more information.
|
|
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.
|
|
Forbidden
|
|
Edited a message that is not yours.
|
|
TypeError
|
|
You specified both ``embed`` and ``embeds`` or ``file`` and ``files``
|
|
ValueError
|
|
The length of ``embeds`` was invalid.
|
|
|
|
Returns
|
|
--------
|
|
:class:`InteractionMessage`
|
|
The newly edited message.
|
|
"""
|
|
|
|
previous_mentions: Optional[AllowedMentions] = self._state.allowed_mentions
|
|
params = handle_message_parameters(
|
|
content=content,
|
|
file=file,
|
|
files=files,
|
|
embed=embed,
|
|
embeds=embeds,
|
|
view=view,
|
|
allowed_mentions=allowed_mentions,
|
|
previous_allowed_mentions=previous_mentions,
|
|
)
|
|
adapter = async_context.get()
|
|
data = await adapter.edit_original_interaction_response(
|
|
self.application_id,
|
|
self.token,
|
|
session=self._session,
|
|
payload=params.payload,
|
|
multipart=params.multipart,
|
|
files=params.files,
|
|
)
|
|
|
|
# The message channel types should always match
|
|
message = InteractionMessage(state=self._state, channel=self.channel, data=data) # type: ignore
|
|
if view and not view.is_finished():
|
|
self._state.store_view(view, message.id)
|
|
return message
|
|
|
|
async def delete_original_message(self) -> None:
|
|
"""|coro|
|
|
|
|
Deletes the original interaction response message.
|
|
|
|
This is a lower level interface to :meth:`InteractionMessage.delete` in case
|
|
you do not want to fetch the message and save an HTTP request.
|
|
|
|
Raises
|
|
-------
|
|
HTTPException
|
|
Deleting the message failed.
|
|
Forbidden
|
|
Deleted a message that is not yours.
|
|
"""
|
|
adapter = async_context.get()
|
|
await adapter.delete_original_interaction_response(
|
|
self.application_id,
|
|
self.token,
|
|
session=self._session,
|
|
)
|
|
|
|
|
|
class InteractionResponse:
|
|
"""Represents a Discord interaction response.
|
|
|
|
This type can be accessed through :attr:`Interaction.response`.
|
|
|
|
.. versionadded:: 2.0
|
|
"""
|
|
|
|
__slots__: Tuple[str, ...] = (
|
|
"responded_at",
|
|
"_parent",
|
|
)
|
|
|
|
def __init__(self, parent: Interaction):
|
|
self.responded_at: Optional[datetime] = None
|
|
self._parent: Interaction = parent
|
|
|
|
def is_done(self) -> bool:
|
|
""":class:`bool`: Indicates whether an interaction response has been done before.
|
|
|
|
An interaction can only be responded to once.
|
|
"""
|
|
return self.responded_at is not None
|
|
|
|
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.
|
|
InteractionResponded
|
|
This interaction has already been responded to before.
|
|
"""
|
|
if self.is_done():
|
|
raise InteractionResponded(self._parent)
|
|
|
|
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_at = utils.utcnow()
|
|
|
|
async def pong(self) -> None:
|
|
"""|coro|
|
|
|
|
Pongs the ping interaction.
|
|
|
|
This should rarely be used.
|
|
|
|
Raises
|
|
-------
|
|
HTTPException
|
|
Ponging the interaction failed.
|
|
InteractionResponded
|
|
This interaction has already been responded to before.
|
|
"""
|
|
if self.is_done():
|
|
raise InteractionResponded(self._parent)
|
|
|
|
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_at = utils.utcnow()
|
|
|
|
async def send_message(
|
|
self,
|
|
content: Optional[Any] = None,
|
|
*,
|
|
embed: Embed = MISSING,
|
|
embeds: List[Embed] = MISSING,
|
|
view: View = 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.
|
|
view: :class:`discord.ui.View`
|
|
The view to send with the message.
|
|
ephemeral: :class:`bool`
|
|
Indicates if the message should only be visible to the user who started the interaction.
|
|
If a view is sent with an ephemeral message and it has no timeout set then the timeout
|
|
is set to 15 minutes.
|
|
|
|
Raises
|
|
-------
|
|
HTTPException
|
|
Sending the message failed.
|
|
TypeError
|
|
You specified both ``embed`` and ``embeds``.
|
|
ValueError
|
|
The length of ``embeds`` was invalid.
|
|
InteractionResponded
|
|
This interaction has already been responded to before.
|
|
"""
|
|
if self.is_done():
|
|
raise InteractionResponded(self._parent)
|
|
|
|
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
|
|
|
|
if view is not MISSING:
|
|
payload["components"] = view.to_components()
|
|
|
|
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,
|
|
)
|
|
|
|
if view is not MISSING:
|
|
if ephemeral and view.timeout is None:
|
|
view.timeout = 15 * 60.0
|
|
|
|
self._parent._state.store_view(view)
|
|
|
|
self.responded_at = utils.utcnow()
|
|
|
|
async def edit_message(
|
|
self,
|
|
*,
|
|
content: Optional[Any] = MISSING,
|
|
embed: Optional[Embed] = MISSING,
|
|
embeds: List[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.
|
|
embeds: List[:class:`Embed`]
|
|
A list of embeds to edit the message with.
|
|
embed: Optional[:class:`Embed`]
|
|
The embed to edit the message with. ``None`` suppresses the embeds.
|
|
This should not be mixed with the ``embeds`` parameter.
|
|
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.
|
|
TypeError
|
|
You specified both ``embed`` and ``embeds``.
|
|
InteractionResponded
|
|
This interaction has already been responded to before.
|
|
"""
|
|
if self.is_done():
|
|
raise InteractionResponded(self._parent)
|
|
|
|
parent = self._parent
|
|
msg = parent.message
|
|
state = parent._state
|
|
message_id = msg.id if msg else None
|
|
if parent.type is not InteractionType.component:
|
|
return
|
|
|
|
payload = {}
|
|
if content is not MISSING:
|
|
if content is None:
|
|
payload["content"] = None
|
|
else:
|
|
payload["content"] = str(content)
|
|
|
|
if embed is not MISSING and embeds is not MISSING:
|
|
raise TypeError("cannot mix both embed and embeds keyword arguments")
|
|
|
|
if embed is not MISSING:
|
|
if embed is None:
|
|
embeds = []
|
|
else:
|
|
embeds = [embed]
|
|
|
|
if embeds is not MISSING:
|
|
payload["embeds"] = [e.to_dict() for e in embeds]
|
|
|
|
if attachments is not MISSING:
|
|
payload["attachments"] = [a.to_dict() for a in attachments]
|
|
|
|
if view is not MISSING:
|
|
state.prevent_view_updates_for(message_id)
|
|
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,
|
|
)
|
|
|
|
if view and not view.is_finished():
|
|
state.store_view(view, message_id)
|
|
|
|
self.responded_at = utils.utcnow()
|
|
|
|
async def autocomplete_result(self, choices: List[ApplicationCommandOptionChoice]):
|
|
"""|coro|
|
|
|
|
Responds to this autocomplete interaction with the resulting choices.
|
|
This should rarely be used.
|
|
|
|
Parameters
|
|
-----------
|
|
choices: List[Dict[:class:`str`, :class:`str`]]
|
|
The choices to be shown in the autocomplete UI of the user.
|
|
Must be a list of dictionaries with the ``name`` and ``value`` keys.
|
|
|
|
Raises
|
|
-------
|
|
HTTPException
|
|
Responding to the interaction failed.
|
|
InteractionResponded
|
|
This interaction has already been responded to before.
|
|
"""
|
|
if self.is_done():
|
|
raise InteractionResponded(self._parent)
|
|
|
|
parent = self._parent
|
|
if parent.type is not InteractionType.application_command_autocomplete:
|
|
return
|
|
|
|
adapter = async_context.get()
|
|
await adapter.create_interaction_response(
|
|
parent.id,
|
|
parent.token,
|
|
session=parent._session,
|
|
type=InteractionResponseType.application_command_autocomplete_result.value,
|
|
data={"choices": choices},
|
|
)
|
|
|
|
self.responded_at = utils.utcnow()
|
|
|
|
|
|
class _InteractionMessageState:
|
|
__slots__ = ("_parent", "_interaction")
|
|
|
|
def __init__(self, interaction: Interaction, parent: ConnectionState):
|
|
self._interaction: Interaction = interaction
|
|
self._parent: ConnectionState = parent
|
|
|
|
def _get_guild(self, guild_id):
|
|
return self._parent._get_guild(guild_id)
|
|
|
|
def store_user(self, data):
|
|
return self._parent.store_user(data)
|
|
|
|
def create_user(self, data):
|
|
return self._parent.create_user(data)
|
|
|
|
@property
|
|
def http(self):
|
|
return self._parent.http
|
|
|
|
def __getattr__(self, attr):
|
|
return getattr(self._parent, attr)
|
|
|
|
|
|
class InteractionMessage(Message):
|
|
"""Represents the original interaction response message.
|
|
|
|
This allows you to edit or delete the message associated with
|
|
the interaction response. To retrieve this object see :meth:`Interaction.original_message`.
|
|
|
|
This inherits from :class:`discord.Message` with changes to
|
|
:meth:`edit` and :meth:`delete` to work.
|
|
|
|
.. versionadded:: 2.0
|
|
"""
|
|
|
|
__slots__ = ()
|
|
_state: _InteractionMessageState
|
|
|
|
async def edit(
|
|
self,
|
|
content: Optional[str] = MISSING,
|
|
embeds: List[Embed] = MISSING,
|
|
embed: Optional[Embed] = MISSING,
|
|
file: File = MISSING,
|
|
files: List[File] = MISSING,
|
|
view: Optional[View] = MISSING,
|
|
allowed_mentions: Optional[AllowedMentions] = None,
|
|
) -> InteractionMessage:
|
|
"""|coro|
|
|
|
|
Edits the message.
|
|
|
|
Parameters
|
|
------------
|
|
content: Optional[:class:`str`]
|
|
The content to edit the message with or ``None`` to clear it.
|
|
embeds: List[:class:`Embed`]
|
|
A list of embeds to edit the message with.
|
|
embed: Optional[:class:`Embed`]
|
|
The embed to edit the message with. ``None`` suppresses the embeds.
|
|
This should not be mixed with the ``embeds`` parameter.
|
|
file: :class:`File`
|
|
The file to upload. This cannot be mixed with ``files`` parameter.
|
|
files: List[:class:`File`]
|
|
A list of files to send with the content. This cannot be mixed with the
|
|
``file`` parameter.
|
|
allowed_mentions: :class:`AllowedMentions`
|
|
Controls the mentions being processed in this message.
|
|
See :meth:`.abc.Messageable.send` for more information.
|
|
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.
|
|
Forbidden
|
|
Edited a message that is not yours.
|
|
TypeError
|
|
You specified both ``embed`` and ``embeds`` or ``file`` and ``files``
|
|
ValueError
|
|
The length of ``embeds`` was invalid.
|
|
|
|
Returns
|
|
---------
|
|
:class:`InteractionMessage`
|
|
The newly edited message.
|
|
"""
|
|
return await self._state._interaction.edit_original_message(
|
|
content=content,
|
|
embeds=embeds,
|
|
embed=embed,
|
|
file=file,
|
|
files=files,
|
|
view=view,
|
|
allowed_mentions=allowed_mentions,
|
|
)
|
|
|
|
async def delete(self, *, delay: Optional[float] = None) -> None:
|
|
"""|coro|
|
|
|
|
Deletes the message.
|
|
|
|
Parameters
|
|
-----------
|
|
delay: Optional[:class:`float`]
|
|
If provided, the number of seconds to wait before deleting the message.
|
|
The waiting is done in the background and deletion failures are ignored.
|
|
|
|
Raises
|
|
------
|
|
Forbidden
|
|
You do not have proper permissions to delete the message.
|
|
NotFound
|
|
The message was deleted already.
|
|
HTTPException
|
|
Deleting the message failed.
|
|
"""
|
|
|
|
if delay is not None:
|
|
|
|
async def inner_call(delay: float = delay):
|
|
await asyncio.sleep(delay)
|
|
try:
|
|
await self._state._interaction.delete_original_message()
|
|
except HTTPException:
|
|
pass
|
|
|
|
asyncio.create_task(inner_call())
|
|
else:
|
|
await self._state._interaction.delete_original_message()
|