Skip to content

Commit 3ffe90d

Browse files
authored
feat: option to defer without raising errors (#1661)
* feat: add ctx.defer() kwarg suppress_error * fix: docstring oops * docs: remove note on specific use case * docs: refactor defer usage note for clarity * docs: use orig "followup" than "new one" on modal * feat: suppress hybrid cmd trigger typing errors
1 parent 5a2e320 commit 3ffe90d

File tree

2 files changed

+75
-6
lines changed

2 files changed

+75
-6
lines changed

interactions/ext/hybrid_commands/context.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
process_message_payload,
2525
)
2626
from interactions.client.mixins.send import SendMixin
27+
from interactions.client.errors import HTTPException
2728
from interactions.ext import prefixed_commands as prefixed
29+
import contextlib
2830

2931
if TYPE_CHECKING:
3032
from .hybrid_slash import HybridSlashCommand
@@ -175,16 +177,26 @@ def typing(self) -> Typing | DeferTyping:
175177
return DeferTyping(self._slash_ctx, self.ephemeral)
176178
return self.channel.typing
177179

178-
async def defer(self, ephemeral: bool = False) -> None:
180+
async def defer(self, ephemeral: bool = False, suppress_error: bool = False) -> None:
179181
"""
180182
Either defers the response (if used in an interaction) or triggers a typing indicator for 10 seconds (if used for messages).
181183
184+
???+ note "Interaction Note"
185+
This method's ephemeral settings override the ephemeral settings of `send()`.
186+
187+
For example, deferring with `ephemeral=True` will make the interaction response ephemeral even with
188+
`send(ephemeral=False)`.
189+
182190
Args:
183191
ephemeral: Should the response be ephemeral? Only applies to responses for interactions.
192+
suppress_error: Should errors on deferring be suppressed than raised.
184193
185194
"""
186195
if self._slash_ctx:
187-
await self._slash_ctx.defer(ephemeral=ephemeral)
196+
await self._slash_ctx.defer(ephemeral=ephemeral, suppress_error=suppress_error)
197+
elif suppress_error:
198+
with contextlib.suppress(HTTPException):
199+
await self.channel.trigger_typing()
188200
else:
189201
await self.channel.trigger_typing()
190202

interactions/models/internal/context.py

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import abc
22
import datetime
33
import re
4+
import contextlib
45
import typing
56
from typing_extensions import Self
67

@@ -405,14 +406,28 @@ def gather_options(_options: list[dict[str, typing.Any]]) -> dict[str, typing.An
405406

406407

407408
class InteractionContext(BaseInteractionContext, SendMixin):
408-
async def defer(self, *, ephemeral: bool = False) -> None:
409+
async def defer(self, *, ephemeral: bool = False, suppress_error: bool = False) -> None:
409410
"""
410411
Defer the interaction.
411412
413+
Note:
414+
This method's ephemeral settings override the ephemeral settings of `send()`.
415+
416+
For example, deferring with `ephemeral=True` will make the response ephemeral even with
417+
`send(ephemeral=False)`.
418+
412419
Args:
413420
ephemeral: Whether the interaction response should be ephemeral.
421+
suppress_error: Should errors on deferring be suppressed than raised.
414422
415423
"""
424+
if suppress_error:
425+
with contextlib.suppress(AlreadyDeferred, AlreadyResponded, HTTPException):
426+
await self._defer(ephemeral=ephemeral)
427+
else:
428+
await self._defer(ephemeral=ephemeral)
429+
430+
async def _defer(self, *, ephemeral: bool = False) -> None:
416431
if self.deferred:
417432
raise AlreadyDeferred("Interaction has already been responded to.")
418433
if self.responded:
@@ -651,15 +666,29 @@ def from_dict(cls, client: "interactions.Client", payload: dict) -> Self:
651666
instance.target_type = CommandType(payload["data"]["type"])
652667
return instance
653668

654-
async def defer(self, *, ephemeral: bool = False, edit_origin: bool = False) -> None:
669+
async def defer(self, *, ephemeral: bool = False, edit_origin: bool = False, suppress_error: bool = False) -> None:
655670
"""
656671
Defer the interaction.
657672
673+
Note:
674+
This method's ephemeral settings override the ephemeral settings of `send()`.
675+
676+
For example, deferring with `ephemeral=True` will make the response ephemeral even with
677+
`send(ephemeral=False)`.
678+
658679
Args:
659680
ephemeral: Whether the interaction response should be ephemeral.
660681
edit_origin: Whether to edit the original message instead of sending a new one.
682+
suppress_error: Should errors on deferring be suppressed than raised.
661683
662684
"""
685+
if suppress_error:
686+
with contextlib.suppress(AlreadyDeferred, AlreadyResponded, HTTPException):
687+
await self._defer(ephemeral=ephemeral, edit_origin=edit_origin)
688+
else:
689+
await self._defer(ephemeral=ephemeral, edit_origin=edit_origin)
690+
691+
async def _defer(self, *, ephemeral: bool = False, edit_origin: bool = False) -> None:
663692
if self.deferred:
664693
raise AlreadyDeferred("Interaction has already been responded to.")
665694
if self.responded:
@@ -745,15 +774,29 @@ def from_dict(cls, client: "interactions.Client", payload: dict) -> Self:
745774
instance.values[i] = channel
746775
return instance
747776

748-
async def defer(self, *, ephemeral: bool = False, edit_origin: bool = False) -> None:
777+
async def defer(self, *, ephemeral: bool = False, edit_origin: bool = False, suppress_error: bool = False) -> None:
749778
"""
750779
Defer the interaction.
751780
781+
Note:
782+
This method's ephemeral settings override the ephemeral settings of `send()`.
783+
784+
For example, deferring with `ephemeral=True` will make the response ephemeral even with
785+
`send(ephemeral=False)`.
786+
752787
Args:
753788
ephemeral: Whether the interaction response should be ephemeral.
754789
edit_origin: Whether to edit the original message instead of sending a new one.
790+
suppress_error: Should errors on deferring be suppressed than raised.
755791
756792
"""
793+
if suppress_error:
794+
with contextlib.suppress(AlreadyDeferred, AlreadyResponded, HTTPException):
795+
await self._defer(ephemeral=ephemeral, edit_origin=edit_origin)
796+
else:
797+
await self._defer(ephemeral=ephemeral, edit_origin=edit_origin)
798+
799+
async def _defer(self, *, ephemeral: bool = False, edit_origin: bool = False) -> None:
757800
if self.deferred:
758801
raise AlreadyDeferred("Interaction has already been responded to.")
759802
if self.responded:
@@ -883,15 +926,29 @@ async def edit(self, message: "Snowflake_Type", **kwargs) -> "interactions.Messa
883926
await self.defer(edit_origin=True)
884927
return await super().edit(message, **kwargs)
885928

886-
async def defer(self, *, ephemeral: bool = False, edit_origin: bool = False) -> None:
929+
async def defer(self, *, ephemeral: bool = False, edit_origin: bool = False, suppress_error: bool = False) -> None:
887930
"""
888931
Defer the interaction.
889932
933+
Note:
934+
This method's ephemeral settings override the ephemeral settings of `send()`.
935+
936+
For example, deferring with `ephemeral=True` will make the response ephemeral even with
937+
`send(ephemeral=False)`.
938+
890939
Args:
891940
ephemeral: Whether the interaction response should be ephemeral.
892941
edit_origin: Whether to edit the original message instead of sending a followup.
942+
suppress_error: Should errors on deferring be suppressed than raised.
893943
894944
"""
945+
if suppress_error:
946+
with contextlib.suppress(AlreadyDeferred, AlreadyResponded, HTTPException):
947+
await self._defer(ephemeral=ephemeral, edit_origin=edit_origin)
948+
else:
949+
await self._defer(ephemeral=ephemeral, edit_origin=edit_origin)
950+
951+
async def _defer(self, *, ephemeral: bool = False, edit_origin: bool = False) -> None:
895952
if self.deferred:
896953
raise AlreadyDeferred("Interaction has already been responded to.")
897954
if self.responded:

0 commit comments

Comments
 (0)