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.
This commit is contained in:
Gnome 2021-10-23 21:19:51 +01:00
parent 63dbecf65d
commit e99ee71233
No known key found for this signature in database
GPG Key ID: 9BF10F8372B254D1

View File

@ -22,11 +22,12 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
""" """
from __future__ import annotations from __future__ import annotations
import asyncio
import inspect import inspect
import re import re
from datetime import timedelta 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.abc
import discord.utils import discord.utils
@ -155,6 +156,7 @@ class Context(discord.abc.Messageable, Generic[BotT]):
self.command_failed: bool = command_failed self.command_failed: bool = command_failed
self.current_parameter: Optional[inspect.Parameter] = current_parameter self.current_parameter: Optional[inspect.Parameter] = current_parameter
self._ignored_params: List[inspect.Parameter] = [] self._ignored_params: List[inspect.Parameter] = []
self._typing_task: Optional[asyncio.Task[NoReturn]] = None
self._state: ConnectionState = self.message._state self._state: ConnectionState = self.message._state
async def invoke(self, command: Command[CogT, P, T], /, *args: P.args, **kwargs: P.kwargs) -> T: 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` 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 ( if self.interaction is None or (
self.interaction.response.responded_at is not None self.interaction.response.responded_at is not None
and discord.utils.utcnow() - self.interaction.response.responded_at >= timedelta(minutes=15) 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 self, content: Optional[str] = None, return_message: bool = True, **kwargs: Any
) -> Optional[Union[Message, WebhookMessage]]: ) -> Optional[Union[Message, WebhookMessage]]:
return await self.send(content, return_message=return_message, reference=self.message, **kwargs) # type: ignore 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)