Implement a ctx.send helper for slash commands
This commit is contained in:
parent
a19e43675f
commit
614c6bca67
@ -74,7 +74,7 @@ CXT = TypeVar('CXT', bound='Context')
|
||||
|
||||
class _FakeSlashMessage(discord.PartialMessage):
|
||||
activity = application = edited_at = reference = webhook_id = None
|
||||
attachments = components = reactions = stickers = []
|
||||
attachments = components = reactions = stickers = mentions = []
|
||||
author: Union[discord.User, discord.Member]
|
||||
tts = False
|
||||
|
||||
@ -1066,7 +1066,6 @@ class BotBase(GroupMixin):
|
||||
return
|
||||
|
||||
assert interaction.user is not None
|
||||
|
||||
interaction.data = cast(ApplicationCommandInteractionData, interaction.data)
|
||||
|
||||
# Ensure the interaction channel is usable
|
||||
@ -1105,7 +1104,6 @@ class BotBase(GroupMixin):
|
||||
message.content = f'{prefix}{command_name} '
|
||||
for name, param in command.clean_params.items():
|
||||
option = next((o for o in command_options if o['name'] == name), None) # type: ignore
|
||||
print(name, param, option)
|
||||
|
||||
if option is None:
|
||||
if not command._is_typing_optional(param.annotation):
|
||||
@ -1224,7 +1222,7 @@ class Bot(BotBase, discord.Client):
|
||||
return
|
||||
|
||||
application = self.application_id or (await self.application_info()).id
|
||||
commands = [scmd for cmd in self.commands if (scmd := cmd.to_application_command()) is not None]
|
||||
commands = [scmd for cmd in self.commands if not cmd.hidden and (scmd := cmd.to_application_command()) is not None]
|
||||
|
||||
await self.http.bulk_upsert_guild_commands(application, self.slash_command_guild, payload=commands)
|
||||
|
||||
|
@ -25,8 +25,8 @@ from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
import re
|
||||
|
||||
from typing import Any, Dict, Generic, List, Optional, TYPE_CHECKING, TypeVar, Union
|
||||
from datetime import timedelta
|
||||
from typing import Any, Dict, Generic, List, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload
|
||||
|
||||
import discord.abc
|
||||
import discord.utils
|
||||
@ -41,6 +41,7 @@ if TYPE_CHECKING:
|
||||
from discord.member import Member
|
||||
from discord.state import ConnectionState
|
||||
from discord.user import ClientUser, User
|
||||
from discord.webhook import WebhookMessage
|
||||
from discord.interactions import Interaction
|
||||
from discord.voice_client import VoiceProtocol
|
||||
|
||||
@ -397,6 +398,81 @@ class Context(discord.abc.Messageable, Generic[BotT]):
|
||||
except CommandError as e:
|
||||
await cmd.on_help_command_error(self, e)
|
||||
|
||||
|
||||
@overload
|
||||
async def send(self,
|
||||
content: Optional[str] = None,
|
||||
return_message: Literal[False] = False,
|
||||
ephemeral: bool = False, **kwargs: Any
|
||||
) -> Optional[Union[Message, WebhookMessage]]: ...
|
||||
@overload
|
||||
async def send(self,
|
||||
content: Optional[str] = None,
|
||||
return_message: Literal[True] = True,
|
||||
ephemeral: bool = False, **kwargs: Any
|
||||
) -> Union[Message, WebhookMessage]: ...
|
||||
|
||||
async def send(self,
|
||||
content: Optional[str] = None,
|
||||
return_message: bool = True,
|
||||
ephemeral: bool = False, **kwargs: Any
|
||||
) -> Optional[Union[Message, WebhookMessage]]:
|
||||
"""
|
||||
|coro|
|
||||
|
||||
A shortcut method to :meth:`.abc.Messageable.send` with interaction helpers.
|
||||
|
||||
This function takes all the parameters of :meth:`.abc.Messageable.send` plus the following:
|
||||
|
||||
Parameters
|
||||
------------
|
||||
return_message: :class:`bool`
|
||||
Ignored if not in a slash command context.
|
||||
If this is set to False more native interaction methods will be used.
|
||||
ephemeral: :class:`bool`
|
||||
Ignored if not in a slash command context.
|
||||
Indicates if the message should only be visible to the user who started the interaction.
|
||||
If a view is sent with an ephemeral message and it has no timeout set then the timeout
|
||||
is set to 15 minutes.
|
||||
|
||||
Returns
|
||||
--------
|
||||
Optional[Union[:class:`.Message`, :class:`.WebhookMessage`]]
|
||||
In a slash command context, the message that was sent if return_message is True.
|
||||
|
||||
In a normal context, it always returns a :class:`.Message`
|
||||
"""
|
||||
|
||||
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)
|
||||
)):
|
||||
return await super().send(content, **kwargs)
|
||||
|
||||
# Remove unsupported arguments from kwargs
|
||||
kwargs.pop("nonce", None)
|
||||
kwargs.pop("stickers", None)
|
||||
kwargs.pop("reference", None)
|
||||
kwargs.pop("delete_after", None)
|
||||
kwargs.pop("mention_author", None)
|
||||
|
||||
if not (
|
||||
return_message
|
||||
or self.interaction.response.is_done()
|
||||
or any(arg in kwargs for arg in ("file", "files", "allowed_mentions"))
|
||||
):
|
||||
send = self.interaction.response.send_message
|
||||
else:
|
||||
# We have to defer in order to use the followup webhook
|
||||
if not self.interaction.response.is_done():
|
||||
await self.interaction.response.defer(ephemeral=ephemeral)
|
||||
|
||||
send = self.interaction.followup.send
|
||||
|
||||
return await send(content, ephemeral=ephemeral, **kwargs) # type: ignore
|
||||
|
||||
@discord.utils.copy_doc(Message.reply)
|
||||
async def reply(self, content: Optional[str] = None, **kwargs: Any) -> Message:
|
||||
return await self.message.reply(content, **kwargs)
|
||||
|
@ -47,6 +47,8 @@ __all__ = (
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from datetime import datetime
|
||||
|
||||
from .types.interactions import (
|
||||
Interaction as InteractionPayload,
|
||||
InteractionData,
|
||||
@ -369,20 +371,20 @@ class InteractionResponse:
|
||||
"""
|
||||
|
||||
__slots__: Tuple[str, ...] = (
|
||||
'_responded',
|
||||
'responded_at',
|
||||
'_parent',
|
||||
)
|
||||
|
||||
def __init__(self, parent: Interaction):
|
||||
self.responded_at: Optional[datetime] = None
|
||||
self._parent: Interaction = parent
|
||||
self._responded: bool = False
|
||||
|
||||
def is_done(self) -> bool:
|
||||
""":class:`bool`: Indicates whether an interaction response has been done before.
|
||||
|
||||
An interaction can only be responded to once.
|
||||
"""
|
||||
return self._responded
|
||||
return self.responded_at is not None
|
||||
|
||||
async def defer(self, *, ephemeral: bool = False) -> None:
|
||||
"""|coro|
|
||||
@ -405,7 +407,7 @@ class InteractionResponse:
|
||||
InteractionResponded
|
||||
This interaction has already been responded to before.
|
||||
"""
|
||||
if self._responded:
|
||||
if self.is_done():
|
||||
raise InteractionResponded(self._parent)
|
||||
|
||||
defer_type: int = 0
|
||||
@ -423,7 +425,8 @@ class InteractionResponse:
|
||||
await adapter.create_interaction_response(
|
||||
parent.id, parent.token, session=parent._session, type=defer_type, data=data
|
||||
)
|
||||
self._responded = True
|
||||
|
||||
self.responded_at = utils.utcnow()
|
||||
|
||||
async def pong(self) -> None:
|
||||
"""|coro|
|
||||
@ -439,7 +442,7 @@ class InteractionResponse:
|
||||
InteractionResponded
|
||||
This interaction has already been responded to before.
|
||||
"""
|
||||
if self._responded:
|
||||
if self.is_done():
|
||||
raise InteractionResponded(self._parent)
|
||||
|
||||
parent = self._parent
|
||||
@ -448,7 +451,7 @@ class InteractionResponse:
|
||||
await adapter.create_interaction_response(
|
||||
parent.id, parent.token, session=parent._session, type=InteractionResponseType.pong.value
|
||||
)
|
||||
self._responded = True
|
||||
self.responded_at = utils.utcnow()
|
||||
|
||||
async def send_message(
|
||||
self,
|
||||
@ -494,7 +497,7 @@ class InteractionResponse:
|
||||
InteractionResponded
|
||||
This interaction has already been responded to before.
|
||||
"""
|
||||
if self._responded:
|
||||
if self.is_done():
|
||||
raise InteractionResponded(self._parent)
|
||||
|
||||
payload: Dict[str, Any] = {
|
||||
@ -537,7 +540,7 @@ class InteractionResponse:
|
||||
|
||||
self._parent._state.store_view(view)
|
||||
|
||||
self._responded = True
|
||||
self.responded_at = utils.utcnow()
|
||||
|
||||
async def edit_message(
|
||||
self,
|
||||
@ -578,7 +581,7 @@ class InteractionResponse:
|
||||
InteractionResponded
|
||||
This interaction has already been responded to before.
|
||||
"""
|
||||
if self._responded:
|
||||
if self.is_done():
|
||||
raise InteractionResponded(self._parent)
|
||||
|
||||
parent = self._parent
|
||||
@ -629,7 +632,7 @@ class InteractionResponse:
|
||||
if view and not view.is_finished():
|
||||
state.store_view(view, message_id)
|
||||
|
||||
self._responded = True
|
||||
self.responded_at = utils.utcnow()
|
||||
|
||||
|
||||
class _InteractionMessageState:
|
||||
|
@ -357,7 +357,7 @@ class View:
|
||||
return
|
||||
|
||||
await item.callback(interaction)
|
||||
if not interaction.response._responded:
|
||||
if not interaction.response.is_done():
|
||||
await interaction.response.defer()
|
||||
except Exception as e:
|
||||
return await self.on_error(e, item, interaction)
|
||||
|
Loading…
x
Reference in New Issue
Block a user