Implement a least breaking approach to slash commands (#39)
* Most slash command support completed, needs some debugging (and reindent) * Implement a ctx.send helper for slash commands * Add group command support * Add Option converter, fix default optional, fix help command * Add client.setup and move readying commands to that * Implement _FakeSlashMessage.from_interaction * Rename normmal_command to message_command * Add docs for added params * Add slash_command_guilds to bot and decos * Fix merge conflict * Remove name from commands.Option, wasn't used * Move slash command processing to BotBase.process_slash_commands * Create slash_only.py Basic example for slash commands * Create slash_and_message.py Basic example for mixed commands * Fix slash_command and normal_command bools * Add some basic error handling for registration * Fixed converter upload errors * Fix some logic and make an actual example * Thanks Safety Jim * docstrings, *args, and error changes * Add proper literal support * Add basic documentation on slash commands * Fix non-slash command interactions * Fix ctx.reply in slash command context * Fix typing on Context.reply * Fix multiple optional argument sorting * Update ctx.message docs to mention error instead of warning * Move slash command creation to BotBase * Fix code style issues with Black * Rearrange some stuff and add flag support * Change some errors and fix interaction.channel fixing * Fix slash command quoting for *args Co-authored-by: iDutchy <42503862+iDutchy@users.noreply.github.com> Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
This commit is contained in:
@@ -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
|
||||
@@ -42,6 +42,8 @@ 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
|
||||
|
||||
from .bot import Bot, AutoShardedBot
|
||||
@@ -120,6 +122,7 @@ class Context(discord.abc.Messageable, Generic[BotT]):
|
||||
A boolean that indicates if the command failed to be parsed, checked,
|
||||
or invoked.
|
||||
"""
|
||||
interaction: Optional[Interaction] = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -151,6 +154,7 @@ class Context(discord.abc.Messageable, Generic[BotT]):
|
||||
self.subcommand_passed: Optional[str] = subcommand_passed
|
||||
self.command_failed: bool = command_failed
|
||||
self.current_parameter: Optional[inspect.Parameter] = current_parameter
|
||||
self._ignored_params: List[inspect.Parameter] = []
|
||||
self._state: ConnectionState = self.message._state
|
||||
|
||||
async def invoke(self, command: Command[CogT, P, T], /, *args: P.args, **kwargs: P.kwargs) -> T:
|
||||
@@ -402,6 +406,97 @@ 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
|
||||
|
||||
@overload
|
||||
async def reply(
|
||||
self, content: Optional[str] = None, return_message: Literal[False] = False, **kwargs: Any
|
||||
) -> Optional[Union[Message, WebhookMessage]]:
|
||||
...
|
||||
|
||||
@overload
|
||||
async def reply(
|
||||
self, content: Optional[str] = None, return_message: Literal[True] = True, **kwargs: Any
|
||||
) -> Union[Message, WebhookMessage]:
|
||||
...
|
||||
|
||||
@discord.utils.copy_doc(Message.reply)
|
||||
async def reply(self, content: Optional[str] = None, **kwargs: Any) -> Message:
|
||||
return await self.message.reply(content, **kwargs)
|
||||
async def reply(
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user