Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
13020e6
restructure BotBase.__init__
BobDotCom Sep 8, 2021
530a141
Edit discord.Bot docstring
BobDotCom Sep 8, 2021
8c1ff0c
Merge pull request #145 from Pycord-Development/slash
BobDotCom Sep 9, 2021
1dcdaac
Fix documentation for 13020e6630d1f105f14bdd57418e8fb536b0a015
BobDotCom Sep 10, 2021
d872253
rename discord.app to discord.commands
BobDotCom Sep 14, 2021
e39f02e
Fix imports after rename
Dorukyum Sep 14, 2021
61d3dad
Create `ApplicationCommandMixin.commands`
Dorukyum Sep 14, 2021
34a4dea
Fix for [#134](https://github.com/Pycord-Development/pycord/issues/134)
Middledot Sep 18, 2021
eae2de8
Refactor + Docs change
Middledot Sep 18, 2021
56edf3a
Implement Command.cooldown
Dorukyum Sep 23, 2021
b078ef5
Merge pull request #225 from Dorukyum/command.cooldown
Dorukyum Sep 24, 2021
eca3cd8
Fix rename
Middledot Sep 27, 2021
53527c6
Re-refactor
Middledot Sep 27, 2021
aa13b93
Fix rename
Middledot Sep 27, 2021
533e952
Remove unused imports and ext.commands import
Middledot Sep 29, 2021
e8f72e1
Remove unused imports
Middledot Sep 29, 2021
1ca80ba
Remove unused imports
Middledot Sep 29, 2021
c61f348
Fix more imports after rename
Dorukyum Sep 30, 2021
6c15981
Implement custom converter types
Dorukyum Oct 7, 2021
e632c6e
Remove import and compare enums directly
Dorukyum Oct 8, 2021
b9307fa
Fix SyntaxError
Dorukyum Oct 8, 2021
11eff31
Fix relative import
Dorukyum Oct 8, 2021
387b523
Support instances
Dorukyum Oct 9, 2021
4ad7677
Better instance check
Dorukyum Oct 9, 2021
d3f2990
Fix instance check
Dorukyum Oct 9, 2021
9d148fd
Fix instance check again
Dorukyum Oct 9, 2021
fc43b56
Use `is not None` to check converter
Dorukyum Oct 13, 2021
c168781
Merge pull request #256 from Dorukyum/restructure
Dorukyum Oct 18, 2021
6f809db
Merge branch 'feature/slash' into restructure
BobDotCom Oct 19, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion discord/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
from .components import *
from .threads import *
from .bot import *
from .app import *
from .commands import *
from .cog import Cog
from .welcome_screen import *

Expand Down
60 changes: 51 additions & 9 deletions discord/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,22 @@
DEALINGS IN THE SOFTWARE.
"""

from __future__ import annotations # will probably need in future for type hinting
from __future__ import annotations

import asyncio
import collections
import inspect
import traceback
from .app.errors import ApplicationCommandError, CheckFailure
from .commands.errors import CheckFailure

from typing import Callable, Optional
from typing import List, Optional, Union

import sys

from .client import Client
from .shard import AutoShardedClient
from .utils import get, async_all
from .app import (
from .commands import (
SlashCommand,
SlashCommandGroup,
MessageCommand,
Expand Down Expand Up @@ -72,6 +75,13 @@ def __init__(self, *args, **kwargs) -> None:
def pending_application_commands(self):
return self._pending_application_commands

@property
def commands(self) -> List[Union[ApplicationCommand, ...]]:
commands = list(self.application_commands.values())
if self._supports_prefixed_commands:
commands += self.prefixed_commands
return commands

def add_application_command(self, command: ApplicationCommand) -> None:
"""Adds a :class:`.ApplicationCommand` into the internal list of commands.

Expand Down Expand Up @@ -354,13 +364,31 @@ class be provided, it must be similar enough to


class BotBase(ApplicationCommandMixin, CogMixin):
_supports_prefixed_commands = False
# TODO I think
def __init__(self, *args, **kwargs):
def __init__(self, description=None, *args, **options):
# super(Client, self).__init__(*args, **kwargs)
# I replaced ^ with v and it worked
super().__init__(*args, **kwargs)
self.debug_guild = kwargs.pop("debug_guild", None)
self.debug_guilds = kwargs.pop("debug_guilds", None)
super().__init__(*args, **options)
self.extra_events = {} # TYPE: Dict[str, List[CoroFunc]]
self.__cogs = {} # TYPE: Dict[str, Cog]
self.__extensions = {} # TYPE: Dict[str, types.ModuleType]
self._checks = [] # TYPE: List[Check]
self._check_once = []
self._before_invoke = None
self._after_invoke = None
self.description = inspect.cleandoc(description) if description else ''
self.owner_id = options.get('owner_id')
self.owner_ids = options.get('owner_ids', set())

self.debug_guild = options.pop("debug_guild", None) # TODO: remove or reimplement
self.debug_guilds = options.pop("debug_guilds", None)

if self.owner_id and self.owner_ids:
raise TypeError('Both owner_id and owner_ids are set.')

if self.owner_ids and not isinstance(self.owner_ids, collections.abc.Collection):
raise TypeError(f'owner_ids must be a collection not {self.owner_ids.__class__!r}')

if self.debug_guild:
if self.debug_guilds is None:
Expand Down Expand Up @@ -570,6 +598,20 @@ class Bot(BotBase, Client):

Attributes
-----------
description: :class:`str`
The content prefixed into the default help message.
owner_id: Optional[:class:`int`]
The user ID that owns the bot. If this is not set and is then queried via
:meth:`.is_owner` then it is fetched automatically using
:meth:`~.Bot.application_info`.
owner_ids: Optional[Collection[:class:`int`]]
The user IDs that owns the bot. This is similar to :attr:`owner_id`.
If this is not set and the application is team based, then it is
fetched automatically using :meth:`~.Bot.application_info`.
For performance reasons it is recommended to use a :class:`set`
for the collection. You cannot set both ``owner_id`` and ``owner_ids``.

.. versionadded:: 1.3
debug_guild: Optional[:class:`int`]
Guild ID of a guild to use for testing commands. Prevents setting global commands
in favor of guild commands, which update instantly.
Expand All @@ -591,4 +633,4 @@ class AutoShardedBot(BotBase, AutoShardedClient):
.. versionadded:: 2.0
"""

pass
pass
22 changes: 15 additions & 7 deletions discord/cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
import discord.utils
import types
from . import errors
from .app import SlashCommand, UserCommand, MessageCommand, ApplicationCommand#, _BaseCommand
from .commands import SlashCommand, UserCommand, MessageCommand, ApplicationCommand

from typing import Any, Callable, Mapping, ClassVar, Dict, Generator, List, Optional, TYPE_CHECKING, Tuple, TypeVar, Type, Union
from typing import Any, Callable, Mapping, ClassVar, Dict, Generator, List, Optional, TYPE_CHECKING, Tuple, TypeVar, Type

from .app.commands import _BaseCommand
from .commands.commands import _BaseCommand

if TYPE_CHECKING:
from .app import InteractionContext, ApplicationCommand
from .commands import InteractionContext, ApplicationCommand

__all__ = (
'CogMeta',
Expand Down Expand Up @@ -239,15 +239,23 @@ def get_commands(self) -> List[ApplicationCommand]:
r"""
Returns
--------
List[:class:`.Command`]
A :class:`list` of :class:`.Command`\s that are
List[:class:`.ApplicationCommand`]
A :class:`list` of :class:`.ApplicationCommand`\s that are
defined inside this cog.

.. note::

This does not include subcommands.
"""
return [c for c in self.__cog_commands__ if c.parent is None]
return [
c for c in (
c for c in self.__cog_commands__
if not isinstance(c, (SlashCommand, MessageCommand, UserCommand))
) if c.parent is None
] + [
c for c in self.__cog_commands__
if isinstance(c, (SlashCommand, MessageCommand, UserCommand))
]

@property
def qualified_name(self) -> str:
Expand Down
File renamed without changes.
48 changes: 27 additions & 21 deletions discord/app/commands.py → discord/commands/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from collections import OrderedDict
from typing import Any, Callable, Dict, List, Optional, Union

from ..enums import OptionType, ChannelType
from ..enums import SlashCommandOptionType, SlashCommandChannelType
from ..member import Member
from ..user import User
from ..message import Message
Expand Down Expand Up @@ -338,6 +338,7 @@ def __init__(self, func: Callable, *args, **kwargs) -> None:
def parse_options(self, params) -> List[Option]:
final_options = []

params = self._get_signature_parameters()
if list(params.items())[0][0] == "self":
temp = list(params.items())
temp.pop(0)
Expand Down Expand Up @@ -368,7 +369,7 @@ def parse_options(self, params) -> List[Option]:
else:
option = Option(
option.__args__, "No description provided"
)
)

if not isinstance(option, Option):
option = Option(option, "No description provided")
Expand Down Expand Up @@ -403,7 +404,7 @@ def to_dict(self) -> Dict:
"options": [o.to_dict() for o in self.options],
}
if self.is_subcommand:
as_dict["type"] = OptionType.sub_command.value
as_dict["type"] = SlashCommandOptionType.sub_command.value

return as_dict

Expand All @@ -415,27 +416,30 @@ def __eq__(self, other) -> bool:
)

async def _invoke(self, ctx: ApplicationContext) -> None:
# TODO: Parse the args better, apply custom converters etc.
# TODO: Parse the args better
kwargs = {}
for arg in ctx.interaction.data.get("options", []):
op = find(lambda x: x.name == arg["name"], self.options)
arg = arg["value"]

# Checks if input_type is user, role or channel
if (
OptionType.user.value
SlashCommandOptionType.user.value
<= op.input_type.value
<= OptionType.role.value
<= SlashCommandOptionType.role.value
):
name = "member" if op.input_type.name == "user" else op.input_type.name
arg = await get_or_fetch(ctx.guild, name, int(arg), default=int(arg))

elif op.input_type == OptionType.mentionable:
elif op.input_type == SlashCommandOptionType.mentionable:
arg_id = int(arg)
arg = await get_or_fetch(ctx.guild, "member", arg_id)
if arg is None:
arg = ctx.guild.get_role(arg_id) or arg_id

elif op.input_type == SlashCommandOptionType.string and op._converter is not None:
arg = await op._converter.convert(ctx, arg)

kwargs[op.name] = arg

for o in self.options:
Expand Down Expand Up @@ -488,10 +492,10 @@ def _update_copy(self, kwargs: Dict[str, Any]):
return self.copy()

channel_type_map = {
'TextChannel': ChannelType.text,
'VoiceChannel': ChannelType.voice,
'StageChannel': ChannelType.stage_voice,
'CategoryChannel': ChannelType.category
'TextChannel': SlashCommandOptionType.text,
'VoiceChannel': SlashCommandOptionType.voice,
'StageChannel': SlashCommandOptionType.stage_voice,
'CategoryChannel': SlashCommandOptionType.category
}

class Option:
Expand All @@ -500,11 +504,15 @@ def __init__(
) -> None:
self.name: Optional[str] = kwargs.pop("name", None)
self.description = description or "No description provided"

self.channel_types: List[ChannelType] = kwargs.pop("channel_types", [])
if not isinstance(input_type, OptionType):
self.input_type = OptionType.from_datatype(input_type)
if self.input_type == OptionType.channel:
self._converter = None
self.channel_types: List[SlashCommandOptionType] = kwargs.pop("channel_types", [])
if not isinstance(input_type, SlashCommandOptionType):
to_assign = input_type() if isinstance(input_type, type) else input_type
_type = SlashCommandOptionType.from_datatype(to_assign.__class__)
if _type == SlashCommandOptionType.custom:
self._converter = to_assign
input_type = SlashCommandOptionType.string
elif _type == SlashCommandOptionType.channel:
if not isinstance(input_type, tuple):
input_type = (input_type,)
for i in input_type:
Expand All @@ -513,9 +521,7 @@ def __init__(

channel_type = channel_type_map[i.__name__]
self.channel_types.append(channel_type)
else:
self.input_type = input_type

self.input_type = input_type
self.required: bool = kwargs.pop("required", True)
self.choices: List[OptionChoice] = [
o if isinstance(o, OptionChoice) else OptionChoice(o)
Expand All @@ -533,7 +539,7 @@ def to_dict(self) -> Dict:
}
if self.channel_types:
as_dict["channel_types"] = [t.value for t in self.channel_types]

return as_dict


Expand Down Expand Up @@ -577,7 +583,7 @@ def __init__(
validate_chat_input_name(name)
validate_chat_input_description(description)
super().__init__(
OptionType.sub_command_group,
SlashCommandOptionType.sub_command_group,
name=name,
description=description,
)
Expand Down
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions discord/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,9 @@ def from_datatype(cls, datatype):
if issubclass(datatype, float):
return cls.number

if hasattr(datatype, "convert"):
return cls.custom

if datatype.__name__ == "Member":
return cls.user
if datatype.__name__ in [
Expand Down
50 changes: 19 additions & 31 deletions discord/ext/commands/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,28 +121,13 @@ def __repr__(self):
_default = _DefaultRepr()

class BotBase(GroupMixin):
def __init__(self, command_prefix=when_mentioned, help_command=_default, description=None, **options):
_supports_prefixed_commands = True
def __init__(self, command_prefix=when_mentioned, help_command=_default, **options):
super().__init__(**options)
self.command_prefix = command_prefix
self.extra_events: Dict[str, List[CoroFunc]] = {}
self.__cogs: Dict[str, Cog] = {}
self.__extensions: Dict[str, types.ModuleType] = {}
self._checks: List[Check] = []
self._check_once = []
self._before_invoke = None
self._after_invoke = None
self._help_command = None
self.description = inspect.cleandoc(description) if description else ''
self.owner_id = options.get('owner_id')
self.owner_ids = options.get('owner_ids', set())
self.strip_after_prefix = options.get('strip_after_prefix', False)

if self.owner_id and self.owner_ids:
raise TypeError('Both owner_id and owner_ids are set.')

if self.owner_ids and not isinstance(self.owner_ids, collections.abc.Collection):
raise TypeError(f'owner_ids must be a collection not {self.owner_ids.__class__!r}')

if help_command is _default:
self.help_command = DefaultHelpCommand()
else:
Expand Down Expand Up @@ -1034,6 +1019,23 @@ async def process_commands(self, message: Message) -> None:
async def on_message(self, message):
await self.process_commands(message)


















class Bot(BotBase, discord.Bot):
"""Represents a discord bot.

Expand Down Expand Up @@ -1079,24 +1081,10 @@ class Bot(BotBase, discord.Bot):
Whether the commands should be case insensitive. Defaults to ``False``. This
attribute does not carry over to groups. You must set it to every group if
you require group commands to be case insensitive as well.
description: :class:`str`
The content prefixed into the default help message.
help_command: Optional[:class:`.HelpCommand`]
The help command implementation to use. This can be dynamically
set at runtime. To remove the help command pass ``None``. For more
information on implementing a help command, see :ref:`ext_commands_help_command`.
owner_id: Optional[:class:`int`]
The user ID that owns the bot. If this is not set and is then queried via
:meth:`.is_owner` then it is fetched automatically using
:meth:`~.Bot.application_info`.
owner_ids: Optional[Collection[:class:`int`]]
The user IDs that owns the bot. This is similar to :attr:`owner_id`.
If this is not set and the application is team based, then it is
fetched automatically using :meth:`~.Bot.application_info`.
For performance reasons it is recommended to use a :class:`set`
for the collection. You cannot set both ``owner_id`` and ``owner_ids``.

.. versionadded:: 1.3
strip_after_prefix: :class:`bool`
Whether to strip whitespace characters after encountering the command
prefix. This allows for ``! hello`` and ``!hello`` to both work if
Expand Down
Loading