Skip to content

Commit 3fd5d70

Browse files
Icebluewolfpre-commit-ci[bot]LulalabyPaillat-dev
authored
feat: Implement with_response For Interaction Callbacks (#2711)
Signed-off-by: Ice Wolfy <[email protected]> Signed-off-by: Lala Sabathil <[email protected]> Signed-off-by: Paillat <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Lala Sabathil <[email protected]> Co-authored-by: Paillat <[email protected]>
1 parent cbe9944 commit 3fd5d70

File tree

5 files changed

+161
-44
lines changed

5 files changed

+161
-44
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ These changes are available on the `master` branch, but have not yet been releas
1212

1313
### Added
1414

15+
- Implemented `with_response` for interaction callbacks, adding
16+
`Interaction.callback.is_loading()` and `Interaction.callback.is_ephemeral()`.
17+
([#2711](https://github.com/Pycord-Development/pycord/pull/2711))
1518
- Added `RawMessageUpdateEvent.new_message` - message update events now contain full
1619
message objects ([#2780](https://github.com/Pycord-Development/pycord/pull/2780))
1720

discord/interactions.py

Lines changed: 127 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"MessageInteraction",
6262
"InteractionMetadata",
6363
"AuthorizingIntegrationOwners",
64+
"InteractionCallback",
6465
)
6566

6667
if TYPE_CHECKING:
@@ -83,7 +84,8 @@
8384
from .state import ConnectionState
8485
from .threads import Thread
8586
from .types.interactions import Interaction as InteractionPayload
86-
from .types.interactions import InteractionData
87+
from .types.interactions import InteractionCallback as InteractionCallbackPayload
88+
from .types.interactions import InteractionCallbackResponse, InteractionData
8789
from .types.interactions import InteractionMetadata as InteractionMetadataPayload
8890
from .types.interactions import MessageInteraction as MessageInteractionPayload
8991
from .ui.modal import Modal
@@ -153,6 +155,11 @@ class Interaction:
153155
The context in which this command was executed.
154156
155157
.. versionadded:: 2.6
158+
callback: Optional[:class:`InteractionCallback`]
159+
The callback of the interaction. Contains information about the status of the interaction response.
160+
Will be `None` until the interaction is responded to.
161+
162+
.. versionadded:: 2.7
156163
command: Optional[:class:`ApplicationCommand`]
157164
The command that this interaction belongs to.
158165
@@ -189,6 +196,7 @@ class Interaction:
189196
"entitlements",
190197
"context",
191198
"authorizing_integration_owners",
199+
"callback",
192200
"command",
193201
"view",
194202
"modal",
@@ -213,6 +221,7 @@ def __init__(self, *, data: InteractionPayload, state: ConnectionState):
213221
self._state: ConnectionState = state
214222
self._session: ClientSession = state.http._HTTPClient__session
215223
self._original_response: InteractionMessage | None = None
224+
self.callback: InteractionCallback | None = None
216225
self._from_data(data)
217226

218227
def _from_data(self, data: InteractionPayload):
@@ -468,7 +477,9 @@ async def original_response(self) -> InteractionMessage:
468477
# TODO: fix later to not raise?
469478
channel = self.channel
470479
if channel is None:
471-
raise ClientException("Channel for message could not be resolved")
480+
raise ClientException(
481+
"Channel for message could not be resolved. Please open a issue on GitHub if you encounter this error."
482+
)
472483

473484
adapter = async_context.get()
474485
http = self._state.http
@@ -860,18 +871,21 @@ async def defer(self, *, ephemeral: bool = False, invisible: bool = True) -> Non
860871
if defer_type:
861872
adapter = async_context.get()
862873
http = parent._state.http
863-
await self._locked_response(
864-
adapter.create_interaction_response(
865-
parent.id,
866-
parent.token,
867-
session=parent._session,
868-
type=defer_type,
869-
data=data,
870-
proxy=http.proxy,
871-
proxy_auth=http.proxy_auth,
874+
callback_response: InteractionCallbackResponse = (
875+
await self._locked_response(
876+
adapter.create_interaction_response(
877+
parent.id,
878+
parent.token,
879+
session=parent._session,
880+
type=defer_type,
881+
data=data,
882+
proxy=http.proxy,
883+
proxy_auth=http.proxy_auth,
884+
)
872885
)
873886
)
874887
self._responded = True
888+
await self._process_callback_response(callback_response)
875889

876890
async def pong(self) -> None:
877891
"""|coro|
@@ -894,17 +908,36 @@ async def pong(self) -> None:
894908
if parent.type is InteractionType.ping:
895909
adapter = async_context.get()
896910
http = parent._state.http
897-
await self._locked_response(
898-
adapter.create_interaction_response(
899-
parent.id,
900-
parent.token,
901-
session=parent._session,
902-
proxy=http.proxy,
903-
proxy_auth=http.proxy_auth,
904-
type=InteractionResponseType.pong.value,
911+
callback_response: InteractionCallbackResponse = (
912+
await self._locked_response(
913+
adapter.create_interaction_response(
914+
parent.id,
915+
parent.token,
916+
session=parent._session,
917+
proxy=http.proxy,
918+
proxy_auth=http.proxy_auth,
919+
type=InteractionResponseType.pong.value,
920+
)
905921
)
906922
)
907923
self._responded = True
924+
await self._process_callback_response(callback_response)
925+
926+
async def _process_callback_response(
927+
self, callback_response: InteractionCallbackResponse
928+
):
929+
if callback_response.get("resource", {}).get("message"):
930+
# TODO: fix later to not raise?
931+
channel = self._parent.channel
932+
if channel is None:
933+
raise ClientException(
934+
"Channel for message could not be resolved. Please open a issue on GitHub if you encounter this error."
935+
)
936+
state = _InteractionMessageState(self._parent, self._parent._state)
937+
message = InteractionMessage(state=state, channel=channel, data=callback_response["resource"]["message"]) # type: ignore
938+
self._parent._original_response = message
939+
940+
self._parent.callback = InteractionCallback(callback_response["interaction"])
908941

909942
async def send_message(
910943
self,
@@ -1048,16 +1081,18 @@ async def send_message(
10481081
adapter = async_context.get()
10491082
http = parent._state.http
10501083
try:
1051-
await self._locked_response(
1052-
adapter.create_interaction_response(
1053-
parent.id,
1054-
parent.token,
1055-
session=parent._session,
1056-
type=InteractionResponseType.channel_message.value,
1057-
proxy=http.proxy,
1058-
proxy_auth=http.proxy_auth,
1059-
data=payload,
1060-
files=files,
1084+
callback_response: InteractionCallbackResponse = (
1085+
await self._locked_response(
1086+
adapter.create_interaction_response(
1087+
parent.id,
1088+
parent.token,
1089+
session=parent._session,
1090+
type=InteractionResponseType.channel_message.value,
1091+
proxy=http.proxy,
1092+
proxy_auth=http.proxy_auth,
1093+
data=payload,
1094+
files=files,
1095+
)
10611096
)
10621097
)
10631098
finally:
@@ -1074,6 +1109,7 @@ async def send_message(
10741109
self._parent._state.store_view(view)
10751110

10761111
self._responded = True
1112+
await self._process_callback_response(callback_response)
10771113
if delete_after is not None:
10781114
await self._parent.delete_original_response(delay=delete_after)
10791115
return self._parent
@@ -1213,16 +1249,18 @@ async def edit_message(
12131249
adapter = async_context.get()
12141250
http = parent._state.http
12151251
try:
1216-
await self._locked_response(
1217-
adapter.create_interaction_response(
1218-
parent.id,
1219-
parent.token,
1220-
session=parent._session,
1221-
type=InteractionResponseType.message_update.value,
1222-
proxy=http.proxy,
1223-
proxy_auth=http.proxy_auth,
1224-
data=payload,
1225-
files=files,
1252+
callback_response: InteractionCallbackResponse = (
1253+
await self._locked_response(
1254+
adapter.create_interaction_response(
1255+
parent.id,
1256+
parent.token,
1257+
session=parent._session,
1258+
type=InteractionResponseType.message_update.value,
1259+
proxy=http.proxy,
1260+
proxy_auth=http.proxy_auth,
1261+
data=payload,
1262+
files=files,
1263+
)
12261264
)
12271265
)
12281266
finally:
@@ -1235,6 +1273,7 @@ async def edit_message(
12351273
state.store_view(view, message_id)
12361274

12371275
self._responded = True
1276+
await self._process_callback_response(callback_response)
12381277
if delete_after is not None:
12391278
await self._parent.delete_original_response(delay=delete_after)
12401279

@@ -1270,7 +1309,7 @@ async def send_autocomplete_result(
12701309

12711310
adapter = async_context.get()
12721311
http = parent._state.http
1273-
await self._locked_response(
1312+
callback_response: InteractionCallbackResponse = await self._locked_response(
12741313
adapter.create_interaction_response(
12751314
parent.id,
12761315
parent.token,
@@ -1283,6 +1322,7 @@ async def send_autocomplete_result(
12831322
)
12841323

12851324
self._responded = True
1325+
await self._process_callback_response(callback_response)
12861326

12871327
async def send_modal(self, modal: Modal) -> Interaction:
12881328
"""|coro|
@@ -1309,7 +1349,7 @@ async def send_modal(self, modal: Modal) -> Interaction:
13091349
payload = modal.to_dict()
13101350
adapter = async_context.get()
13111351
http = parent._state.http
1312-
await self._locked_response(
1352+
callback_response: InteractionCallbackResponse = await self._locked_response(
13131353
adapter.create_interaction_response(
13141354
parent.id,
13151355
parent.token,
@@ -1321,6 +1361,7 @@ async def send_modal(self, modal: Modal) -> Interaction:
13211361
)
13221362
)
13231363
self._responded = True
1364+
await self._process_callback_response(callback_response)
13241365
self._parent._state.store_modal(modal, self._parent.user.id)
13251366
return self._parent
13261367

@@ -1348,7 +1389,7 @@ async def premium_required(self) -> Interaction:
13481389

13491390
adapter = async_context.get()
13501391
http = parent._state.http
1351-
await self._locked_response(
1392+
callback_response: InteractionCallbackResponse = await self._locked_response(
13521393
adapter.create_interaction_response(
13531394
parent.id,
13541395
parent.token,
@@ -1359,9 +1400,10 @@ async def premium_required(self) -> Interaction:
13591400
)
13601401
)
13611402
self._responded = True
1403+
await self._process_callback_response(callback_response)
13621404
return self._parent
13631405

1364-
async def _locked_response(self, coro: Coroutine[Any, Any, Any]) -> None:
1406+
async def _locked_response(self, coro: Coroutine[Any, Any, Any]) -> Any:
13651407
"""|coro|
13661408
13671409
Wraps a response and makes sure that it's locked while executing.
@@ -1371,16 +1413,24 @@ async def _locked_response(self, coro: Coroutine[Any, Any, Any]) -> None:
13711413
coro: Coroutine[Any]
13721414
The coroutine to wrap.
13731415
1416+
Returns
1417+
-------
1418+
Any
1419+
The result of the coroutine.
1420+
13741421
Raises
13751422
------
13761423
InteractionResponded
13771424
This interaction has already been responded to before.
1425+
1426+
.. versionchanged:: 2.7
1427+
Return the result of the coroutine
13781428
"""
13791429
async with self._response_lock:
13801430
if self.is_done():
13811431
coro.close() # cleanup un-awaited coroutine
13821432
raise InteractionResponded(self._parent)
1383-
await coro
1433+
return await coro
13841434

13851435

13861436
class _InteractionMessageState:
@@ -1704,3 +1754,36 @@ def guild(self) -> Guild | None:
17041754
if not self.guild_id:
17051755
return None
17061756
return self._state._get_guild(self.guild_id)
1757+
1758+
1759+
class InteractionCallback:
1760+
"""Information about the status of the interaction response.
1761+
1762+
.. versionadded:: 2.7
1763+
"""
1764+
1765+
def __init__(self, data: InteractionCallbackPayload):
1766+
self._response_message_loading: bool = data.get(
1767+
"response_message_loading", False
1768+
)
1769+
self._response_message_ephemeral: bool = data.get(
1770+
"response_message_ephemeral", False
1771+
)
1772+
1773+
def __repr__(self):
1774+
return (
1775+
f"<InteractionCallback "
1776+
f"_response_message_loading={self._response_message_loading} "
1777+
f"_response_message_ephemeral={self._response_message_ephemeral}>"
1778+
)
1779+
1780+
def is_loading(self) -> bool:
1781+
"""Indicates whether the response message is in a loading state."""
1782+
return self._response_message_loading
1783+
1784+
def is_ephemeral(self) -> bool:
1785+
"""Indicates whether the response message is ephemeral.
1786+
1787+
This might be useful for determining if the message was forced to be ephemeral.
1788+
"""
1789+
return self._response_message_ephemeral

discord/types/interactions.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,24 @@ class EditApplicationCommand(TypedDict):
273273
_StringApplicationIntegrationType = Literal["0", "1"]
274274

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

discord/webhook/async_.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,13 +545,18 @@ def create_interaction_response(
545545
webhook_token=token,
546546
)
547547

548+
params: dict[str, Any] = {
549+
"with_response": "true",
550+
}
551+
548552
return self.request(
549553
route,
550554
session=session,
551555
proxy=proxy,
552556
proxy_auth=proxy_auth,
553557
files=files,
554558
multipart=form,
559+
params=params,
555560
)
556561

557562
def get_original_interaction_response(

docs/api/models.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,11 @@ Interactions
373373
.. autoclass:: AuthorizingIntegrationOwners()
374374
:members:
375375

376+
.. attributetable:: InteractionCallback
377+
378+
.. autoclass:: InteractionCallback()
379+
:members:
380+
376381
Message Components
377382
------------------
378383

0 commit comments

Comments
 (0)