From e99ee71233e5bfce1abc543ac59f5d75efd85cd2 Mon Sep 17 00:00:00 2001 From: Gnome Date: Sat, 23 Oct 2021 21:19:51 +0100 Subject: [PATCH] Add `ctx.defer` to help with 3 second slash command response rule. Acts as `ctx.interaction.response.defer` or loops `ctx.trigger_typing` depending on context. --- discord/ext/commands/context.py | 35 ++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/discord/ext/commands/context.py b/discord/ext/commands/context.py index 6bcce31b..950a94f6 100644 --- a/discord/ext/commands/context.py +++ b/discord/ext/commands/context.py @@ -22,11 +22,12 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import annotations +import asyncio import inspect import re from datetime import timedelta -from typing import Any, Dict, Generic, List, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload +from typing import Any, Dict, Generic, List, Literal, NoReturn, Optional, TYPE_CHECKING, TypeVar, Union, overload import discord.abc import discord.utils @@ -155,6 +156,7 @@ class Context(discord.abc.Messageable, Generic[BotT]): self.command_failed: bool = command_failed self.current_parameter: Optional[inspect.Parameter] = current_parameter self._ignored_params: List[inspect.Parameter] = [] + self._typing_task: Optional[asyncio.Task[NoReturn]] = None self._state: ConnectionState = self.message._state async def invoke(self, command: Command[CogT, P, T], /, *args: P.args, **kwargs: P.kwargs) -> T: @@ -455,6 +457,10 @@ class Context(discord.abc.Messageable, Generic[BotT]): In a normal context, it always returns a :class:`.Message` """ + if self._typing_task is not None: + self._typing_task.cancel() + self._typing_task = None + if self.interaction is None or ( self.interaction.response.responded_at is not None and discord.utils.utcnow() - self.interaction.response.responded_at >= timedelta(minutes=15) @@ -500,3 +506,30 @@ class Context(discord.abc.Messageable, Generic[BotT]): self, content: Optional[str] = None, return_message: bool = True, **kwargs: Any ) -> Optional[Union[Message, WebhookMessage]]: return await self.send(content, return_message=return_message, reference=self.message, **kwargs) # type: ignore + + async def defer(self, *, ephemeral: bool = False, trigger_typing: bool = True) -> None: + """|coro| + + Defers the Slash Command interaction if ran in a slash command **or** + + Loops triggering ``Bot is typing`` in the channel if run in a message command. + + Parameters + ------------ + trigger_typing: :class:`bool` + Indicates whether to trigger typing in a message command. + ephemeral: :class:`bool` + Indicates whether the deferred message will eventually be ephemeral in a slash command. + """ + + if self.interaction is None: + if self._typing_task is None and trigger_typing: + + async def typing_task(): + while True: + await self.trigger_typing() + await asyncio.sleep(10) + + self._typing_task = self.bot.loop.create_task(typing_task()) + else: + await self.interaction.response.defer(ephemeral=ephemeral)