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):
|
class _FakeSlashMessage(discord.PartialMessage):
|
||||||
activity = application = edited_at = reference = webhook_id = None
|
activity = application = edited_at = reference = webhook_id = None
|
||||||
attachments = components = reactions = stickers = []
|
attachments = components = reactions = stickers = mentions = []
|
||||||
author: Union[discord.User, discord.Member]
|
author: Union[discord.User, discord.Member]
|
||||||
tts = False
|
tts = False
|
||||||
|
|
||||||
@ -1066,7 +1066,6 @@ class BotBase(GroupMixin):
|
|||||||
return
|
return
|
||||||
|
|
||||||
assert interaction.user is not None
|
assert interaction.user is not None
|
||||||
|
|
||||||
interaction.data = cast(ApplicationCommandInteractionData, interaction.data)
|
interaction.data = cast(ApplicationCommandInteractionData, interaction.data)
|
||||||
|
|
||||||
# Ensure the interaction channel is usable
|
# Ensure the interaction channel is usable
|
||||||
@ -1105,7 +1104,6 @@ class BotBase(GroupMixin):
|
|||||||
message.content = f'{prefix}{command_name} '
|
message.content = f'{prefix}{command_name} '
|
||||||
for name, param in command.clean_params.items():
|
for name, param in command.clean_params.items():
|
||||||
option = next((o for o in command_options if o['name'] == name), None) # type: ignore
|
option = next((o for o in command_options if o['name'] == name), None) # type: ignore
|
||||||
print(name, param, option)
|
|
||||||
|
|
||||||
if option is None:
|
if option is None:
|
||||||
if not command._is_typing_optional(param.annotation):
|
if not command._is_typing_optional(param.annotation):
|
||||||
@ -1224,7 +1222,7 @@ class Bot(BotBase, discord.Client):
|
|||||||
return
|
return
|
||||||
|
|
||||||
application = self.application_id or (await self.application_info()).id
|
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)
|
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 inspect
|
||||||
import re
|
import re
|
||||||
|
from datetime import timedelta
|
||||||
from typing import Any, Dict, Generic, List, Optional, TYPE_CHECKING, TypeVar, Union
|
from typing import Any, Dict, Generic, List, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload
|
||||||
|
|
||||||
import discord.abc
|
import discord.abc
|
||||||
import discord.utils
|
import discord.utils
|
||||||
@ -41,6 +41,7 @@ if TYPE_CHECKING:
|
|||||||
from discord.member import Member
|
from discord.member import Member
|
||||||
from discord.state import ConnectionState
|
from discord.state import ConnectionState
|
||||||
from discord.user import ClientUser, User
|
from discord.user import ClientUser, User
|
||||||
|
from discord.webhook import WebhookMessage
|
||||||
from discord.interactions import Interaction
|
from discord.interactions import Interaction
|
||||||
from discord.voice_client import VoiceProtocol
|
from discord.voice_client import VoiceProtocol
|
||||||
|
|
||||||
@ -397,6 +398,81 @@ class Context(discord.abc.Messageable, Generic[BotT]):
|
|||||||
except CommandError as e:
|
except CommandError as e:
|
||||||
await cmd.on_help_command_error(self, 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)
|
@discord.utils.copy_doc(Message.reply)
|
||||||
async def reply(self, content: Optional[str] = None, **kwargs: Any) -> Message:
|
async def reply(self, content: Optional[str] = None, **kwargs: Any) -> Message:
|
||||||
return await self.message.reply(content, **kwargs)
|
return await self.message.reply(content, **kwargs)
|
||||||
|
@ -47,6 +47,8 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from .types.interactions import (
|
from .types.interactions import (
|
||||||
Interaction as InteractionPayload,
|
Interaction as InteractionPayload,
|
||||||
InteractionData,
|
InteractionData,
|
||||||
@ -369,20 +371,20 @@ class InteractionResponse:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__: Tuple[str, ...] = (
|
__slots__: Tuple[str, ...] = (
|
||||||
'_responded',
|
'responded_at',
|
||||||
'_parent',
|
'_parent',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, parent: Interaction):
|
def __init__(self, parent: Interaction):
|
||||||
|
self.responded_at: Optional[datetime] = None
|
||||||
self._parent: Interaction = parent
|
self._parent: Interaction = parent
|
||||||
self._responded: bool = False
|
|
||||||
|
|
||||||
def is_done(self) -> bool:
|
def is_done(self) -> bool:
|
||||||
""":class:`bool`: Indicates whether an interaction response has been done before.
|
""":class:`bool`: Indicates whether an interaction response has been done before.
|
||||||
|
|
||||||
An interaction can only be responded to once.
|
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:
|
async def defer(self, *, ephemeral: bool = False) -> None:
|
||||||
"""|coro|
|
"""|coro|
|
||||||
@ -405,7 +407,7 @@ class InteractionResponse:
|
|||||||
InteractionResponded
|
InteractionResponded
|
||||||
This interaction has already been responded to before.
|
This interaction has already been responded to before.
|
||||||
"""
|
"""
|
||||||
if self._responded:
|
if self.is_done():
|
||||||
raise InteractionResponded(self._parent)
|
raise InteractionResponded(self._parent)
|
||||||
|
|
||||||
defer_type: int = 0
|
defer_type: int = 0
|
||||||
@ -423,7 +425,8 @@ class InteractionResponse:
|
|||||||
await adapter.create_interaction_response(
|
await adapter.create_interaction_response(
|
||||||
parent.id, parent.token, session=parent._session, type=defer_type, data=data
|
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:
|
async def pong(self) -> None:
|
||||||
"""|coro|
|
"""|coro|
|
||||||
@ -439,7 +442,7 @@ class InteractionResponse:
|
|||||||
InteractionResponded
|
InteractionResponded
|
||||||
This interaction has already been responded to before.
|
This interaction has already been responded to before.
|
||||||
"""
|
"""
|
||||||
if self._responded:
|
if self.is_done():
|
||||||
raise InteractionResponded(self._parent)
|
raise InteractionResponded(self._parent)
|
||||||
|
|
||||||
parent = self._parent
|
parent = self._parent
|
||||||
@ -448,7 +451,7 @@ class InteractionResponse:
|
|||||||
await adapter.create_interaction_response(
|
await adapter.create_interaction_response(
|
||||||
parent.id, parent.token, session=parent._session, type=InteractionResponseType.pong.value
|
parent.id, parent.token, session=parent._session, type=InteractionResponseType.pong.value
|
||||||
)
|
)
|
||||||
self._responded = True
|
self.responded_at = utils.utcnow()
|
||||||
|
|
||||||
async def send_message(
|
async def send_message(
|
||||||
self,
|
self,
|
||||||
@ -494,7 +497,7 @@ class InteractionResponse:
|
|||||||
InteractionResponded
|
InteractionResponded
|
||||||
This interaction has already been responded to before.
|
This interaction has already been responded to before.
|
||||||
"""
|
"""
|
||||||
if self._responded:
|
if self.is_done():
|
||||||
raise InteractionResponded(self._parent)
|
raise InteractionResponded(self._parent)
|
||||||
|
|
||||||
payload: Dict[str, Any] = {
|
payload: Dict[str, Any] = {
|
||||||
@ -537,7 +540,7 @@ class InteractionResponse:
|
|||||||
|
|
||||||
self._parent._state.store_view(view)
|
self._parent._state.store_view(view)
|
||||||
|
|
||||||
self._responded = True
|
self.responded_at = utils.utcnow()
|
||||||
|
|
||||||
async def edit_message(
|
async def edit_message(
|
||||||
self,
|
self,
|
||||||
@ -578,7 +581,7 @@ class InteractionResponse:
|
|||||||
InteractionResponded
|
InteractionResponded
|
||||||
This interaction has already been responded to before.
|
This interaction has already been responded to before.
|
||||||
"""
|
"""
|
||||||
if self._responded:
|
if self.is_done():
|
||||||
raise InteractionResponded(self._parent)
|
raise InteractionResponded(self._parent)
|
||||||
|
|
||||||
parent = self._parent
|
parent = self._parent
|
||||||
@ -629,7 +632,7 @@ class InteractionResponse:
|
|||||||
if view and not view.is_finished():
|
if view and not view.is_finished():
|
||||||
state.store_view(view, message_id)
|
state.store_view(view, message_id)
|
||||||
|
|
||||||
self._responded = True
|
self.responded_at = utils.utcnow()
|
||||||
|
|
||||||
|
|
||||||
class _InteractionMessageState:
|
class _InteractionMessageState:
|
||||||
|
@ -357,7 +357,7 @@ class View:
|
|||||||
return
|
return
|
||||||
|
|
||||||
await item.callback(interaction)
|
await item.callback(interaction)
|
||||||
if not interaction.response._responded:
|
if not interaction.response.is_done():
|
||||||
await interaction.response.defer()
|
await interaction.response.defer()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return await self.on_error(e, item, interaction)
|
return await self.on_error(e, item, interaction)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user