Skip to content

Commit 51e40af

Browse files
committed
feat: Implement with_response For Interaction Callbacks
1 parent 04b7ca0 commit 51e40af

File tree

3 files changed

+71
-5
lines changed

3 files changed

+71
-5
lines changed

discord/interactions.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
from .poll import Poll
8282
from .state import ConnectionState
8383
from .threads import Thread
84-
from .types.interactions import Interaction as InteractionPayload
84+
from .types.interactions import Interaction as InteractionPayload, InteractionCallbackResponse
8585
from .types.interactions import InteractionData
8686
from .types.interactions import InteractionMetadata as InteractionMetadataPayload
8787
from .types.interactions import MessageInteraction as MessageInteractionPayload
@@ -129,6 +129,7 @@ class Interaction:
129129
The user or member that sent the interaction. Will be `None` in PING interactions.
130130
message: Optional[:class:`Message`]
131131
The message that sent this interaction.
132+
This is updated to the message object of the response after responding by sending or editing a message.
132133
token: :class:`str`
133134
The token to continue the interaction. These are valid
134135
for 15 minutes.
@@ -185,12 +186,16 @@ class Interaction:
185186
"_cs_response",
186187
"_cs_followup",
187188
"_cs_channel",
189+
"_response_message_loading",
190+
"_response_message_ephemeral",
188191
)
189192

190193
def __init__(self, *, data: InteractionPayload, state: ConnectionState):
191194
self._state: ConnectionState = state
192195
self._session: ClientSession = state.http._HTTPClient__session
193196
self._original_response: InteractionMessage | None = None
197+
self._response_message_loading: bool = False
198+
self._response_message_ephemeral: bool = False
194199
self._from_data(data)
195200

196201
def _from_data(self, data: InteractionPayload):
@@ -308,6 +313,22 @@ def is_component(self) -> bool:
308313
"""Indicates whether the interaction is a message component."""
309314
return self.type == InteractionType.component
310315

316+
def is_loading(self) -> bool:
317+
"""Indicates whether the response message is in a loading state.
318+
319+
.. versionadded:: 2.7
320+
"""
321+
return self._response_message_loading
322+
323+
def is_ephemeral(self) -> bool:
324+
"""Indicates whether the response message is ephemeral.
325+
326+
This might be useful for determining if the message was forced to be ephemeral.
327+
328+
.. versionadded:: 2.7
329+
"""
330+
return self._response_message_ephemeral
331+
311332
@utils.cached_slot_property("_cs_channel")
312333
@utils.deprecated("Interaction.channel", "2.7", stacklevel=4)
313334
def cached_channel(self) -> InteractionChannel | None:
@@ -871,6 +892,19 @@ async def pong(self) -> None:
871892
)
872893
self._responded = True
873894

895+
async def _process_callback_response(self, callback_response: InteractionCallbackResponse):
896+
if callback_response.get("resource") and callback_response["resource"].get("message"):
897+
# TODO: fix later to not raise?
898+
channel = self._parent.channel
899+
if channel is None:
900+
raise ClientException("Channel for message could not be resolved")
901+
state = _InteractionMessageState(self._parent, self._parent._state)
902+
message = InteractionMessage(state=state, channel=channel, data=callback_response["resource"]["message"]) # type: ignore
903+
self._parent._original_response = message
904+
905+
self._parent._response_message_ephemeral = callback_response["interaction"].get("response_message_ephemeral", False)
906+
self._parent._response_message_loading = callback_response["interaction"].get("response_message_loading", False)
907+
874908
async def send_message(
875909
self,
876910
content: Any | None = None,
@@ -1007,7 +1041,7 @@ async def send_message(
10071041
adapter = async_context.get()
10081042
http = parent._state.http
10091043
try:
1010-
await self._locked_response(
1044+
callback_response: InteractionCallbackResponse = await self._locked_response(
10111045
adapter.create_interaction_response(
10121046
parent.id,
10131047
parent.token,
@@ -1031,6 +1065,8 @@ async def send_message(
10311065
view.parent = self._parent
10321066
self._parent._state.store_view(view)
10331067

1068+
await self._process_callback_response(callback_response)
1069+
10341070
self._responded = True
10351071
if delete_after is not None:
10361072
await self._parent.delete_original_response(delay=delete_after)
@@ -1171,7 +1207,7 @@ async def edit_message(
11711207
adapter = async_context.get()
11721208
http = parent._state.http
11731209
try:
1174-
await self._locked_response(
1210+
callback_response: InteractionCallbackResponse = await self._locked_response(
11751211
adapter.create_interaction_response(
11761212
parent.id,
11771213
parent.token,
@@ -1192,6 +1228,8 @@ async def edit_message(
11921228
view.message = msg
11931229
state.store_view(view, message_id)
11941230

1231+
await self._process_callback_response(callback_response)
1232+
11951233
self._responded = True
11961234
if delete_after is not None:
11971235
await self._parent.delete_original_response(delay=delete_after)
@@ -1319,7 +1357,7 @@ async def premium_required(self) -> Interaction:
13191357
self._responded = True
13201358
return self._parent
13211359

1322-
async def _locked_response(self, coro: Coroutine[Any, Any, Any]) -> None:
1360+
async def _locked_response(self, coro: Coroutine[Any, Any, Any]) -> Any:
13231361
"""|coro|
13241362
13251363
Wraps a response and makes sure that it's locked while executing.
@@ -1338,7 +1376,7 @@ async def _locked_response(self, coro: Coroutine[Any, Any, Any]) -> None:
13381376
if self.is_done():
13391377
coro.close() # cleanup un-awaited coroutine
13401378
raise InteractionResponded(self._parent)
1341-
await coro
1379+
return await coro
13421380

13431381

13441382
class _InteractionMessageState:

discord/types/interactions.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,26 @@ class EditApplicationCommand(TypedDict):
272272
_StringApplicationIntegrationType = Literal["0", "1"]
273273

274274
AuthorizingIntegrationOwners = Dict[_StringApplicationIntegrationType, Snowflake]
275+
276+
277+
class InteractionCallbackResponse(TypedDict):
278+
interaction: InteractionCallback
279+
resource: NotRequired[InteractionCallbackResource]
280+
281+
282+
class InteractionCallback(TypedDict):
283+
id: Snowflake
284+
type: InteractionType
285+
activity_instance_id: NotRequired[str]
286+
response_message_id: NotRequired[Snowflake]
287+
response_message_loading: NotRequired[bool]
288+
response_message_ephemeral: NotRequired[bool]
289+
290+
291+
class InteractionCallbackResource(TypedDict):
292+
type: InteractionResponseType
293+
# This is not fully typed as activities are out of scope
294+
activity_instance: NotRequired[dict]
295+
message: NotRequired[Message]
296+
297+

discord/webhook/async_.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,13 +537,18 @@ def create_interaction_response(
537537
webhook_token=token,
538538
)
539539

540+
params: dict[str, Any] = {
541+
"with_response": True,
542+
}
543+
540544
return self.request(
541545
route,
542546
session=session,
543547
proxy=proxy,
544548
proxy_auth=proxy_auth,
545549
files=files,
546550
multipart=form,
551+
params=params,
547552
)
548553

549554
def get_original_interaction_response(

0 commit comments

Comments
 (0)