Skip to content

Commit 6ac921b

Browse files
authored
Merge pull request #138 from Pycord-Development/restructure
Merge restructure into slash
2 parents 14607fe + 6f809db commit 6ac921b

File tree

13 files changed

+131
-77
lines changed

13 files changed

+131
-77
lines changed

discord/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
from .components import *
6161
from .threads import *
6262
from .bot import *
63-
from .app import *
63+
from .commands import *
6464
from .cog import Cog
6565
from .welcome_screen import *
6666

discord/bot.py

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,22 @@
2323
DEALINGS IN THE SOFTWARE.
2424
"""
2525

26-
from __future__ import annotations # will probably need in future for type hinting
26+
from __future__ import annotations
27+
2728
import asyncio
29+
import collections
30+
import inspect
2831
import traceback
29-
from .app.errors import ApplicationCommandError, CheckFailure
32+
from .commands.errors import CheckFailure
3033

31-
from typing import Callable, Optional
34+
from typing import List, Optional, Union
3235

3336
import sys
3437

3538
from .client import Client
3639
from .shard import AutoShardedClient
3740
from .utils import get, async_all
38-
from .app import (
41+
from .commands import (
3942
SlashCommand,
4043
SlashCommandGroup,
4144
MessageCommand,
@@ -72,6 +75,13 @@ def __init__(self, *args, **kwargs) -> None:
7275
def pending_application_commands(self):
7376
return self._pending_application_commands
7477

78+
@property
79+
def commands(self) -> List[Union[ApplicationCommand, ...]]:
80+
commands = list(self.application_commands.values())
81+
if self._supports_prefixed_commands:
82+
commands += self.prefixed_commands
83+
return commands
84+
7585
def add_application_command(self, command: ApplicationCommand) -> None:
7686
"""Adds a :class:`.ApplicationCommand` into the internal list of commands.
7787
@@ -354,13 +364,31 @@ class be provided, it must be similar enough to
354364

355365

356366
class BotBase(ApplicationCommandMixin, CogMixin):
367+
_supports_prefixed_commands = False
357368
# TODO I think
358-
def __init__(self, *args, **kwargs):
369+
def __init__(self, description=None, *args, **options):
359370
# super(Client, self).__init__(*args, **kwargs)
360371
# I replaced ^ with v and it worked
361-
super().__init__(*args, **kwargs)
362-
self.debug_guild = kwargs.pop("debug_guild", None)
363-
self.debug_guilds = kwargs.pop("debug_guilds", None)
372+
super().__init__(*args, **options)
373+
self.extra_events = {} # TYPE: Dict[str, List[CoroFunc]]
374+
self.__cogs = {} # TYPE: Dict[str, Cog]
375+
self.__extensions = {} # TYPE: Dict[str, types.ModuleType]
376+
self._checks = [] # TYPE: List[Check]
377+
self._check_once = []
378+
self._before_invoke = None
379+
self._after_invoke = None
380+
self.description = inspect.cleandoc(description) if description else ''
381+
self.owner_id = options.get('owner_id')
382+
self.owner_ids = options.get('owner_ids', set())
383+
384+
self.debug_guild = options.pop("debug_guild", None) # TODO: remove or reimplement
385+
self.debug_guilds = options.pop("debug_guilds", None)
386+
387+
if self.owner_id and self.owner_ids:
388+
raise TypeError('Both owner_id and owner_ids are set.')
389+
390+
if self.owner_ids and not isinstance(self.owner_ids, collections.abc.Collection):
391+
raise TypeError(f'owner_ids must be a collection not {self.owner_ids.__class__!r}')
364392

365393
if self.debug_guild:
366394
if self.debug_guilds is None:
@@ -570,6 +598,20 @@ class Bot(BotBase, Client):
570598
571599
Attributes
572600
-----------
601+
description: :class:`str`
602+
The content prefixed into the default help message.
603+
owner_id: Optional[:class:`int`]
604+
The user ID that owns the bot. If this is not set and is then queried via
605+
:meth:`.is_owner` then it is fetched automatically using
606+
:meth:`~.Bot.application_info`.
607+
owner_ids: Optional[Collection[:class:`int`]]
608+
The user IDs that owns the bot. This is similar to :attr:`owner_id`.
609+
If this is not set and the application is team based, then it is
610+
fetched automatically using :meth:`~.Bot.application_info`.
611+
For performance reasons it is recommended to use a :class:`set`
612+
for the collection. You cannot set both ``owner_id`` and ``owner_ids``.
613+
614+
.. versionadded:: 1.3
573615
debug_guild: Optional[:class:`int`]
574616
Guild ID of a guild to use for testing commands. Prevents setting global commands
575617
in favor of guild commands, which update instantly.
@@ -591,4 +633,4 @@ class AutoShardedBot(BotBase, AutoShardedClient):
591633
.. versionadded:: 2.0
592634
"""
593635

594-
pass
636+
pass

discord/cog.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@
2929
import discord.utils
3030
import types
3131
from . import errors
32-
from .app import SlashCommand, UserCommand, MessageCommand, ApplicationCommand#, _BaseCommand
32+
from .commands import SlashCommand, UserCommand, MessageCommand, ApplicationCommand
3333

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

36-
from .app.commands import _BaseCommand
36+
from .commands.commands import _BaseCommand
3737

3838
if TYPE_CHECKING:
39-
from .app import InteractionContext, ApplicationCommand
39+
from .commands import InteractionContext, ApplicationCommand
4040

4141
__all__ = (
4242
'CogMeta',
@@ -239,15 +239,23 @@ def get_commands(self) -> List[ApplicationCommand]:
239239
r"""
240240
Returns
241241
--------
242-
List[:class:`.Command`]
243-
A :class:`list` of :class:`.Command`\s that are
242+
List[:class:`.ApplicationCommand`]
243+
A :class:`list` of :class:`.ApplicationCommand`\s that are
244244
defined inside this cog.
245245
246246
.. note::
247247
248248
This does not include subcommands.
249249
"""
250-
return [c for c in self.__cog_commands__ if c.parent is None]
250+
return [
251+
c for c in (
252+
c for c in self.__cog_commands__
253+
if not isinstance(c, (SlashCommand, MessageCommand, UserCommand))
254+
) if c.parent is None
255+
] + [
256+
c for c in self.__cog_commands__
257+
if isinstance(c, (SlashCommand, MessageCommand, UserCommand))
258+
]
251259

252260
@property
253261
def qualified_name(self) -> str:
File renamed without changes.

discord/app/commands.py renamed to discord/commands/commands.py

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from collections import OrderedDict
3434
from typing import Any, Callable, Dict, List, Optional, Union
3535

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

341+
params = self._get_signature_parameters()
341342
if list(params.items())[0][0] == "self":
342343
temp = list(params.items())
343344
temp.pop(0)
@@ -368,7 +369,7 @@ def parse_options(self, params) -> List[Option]:
368369
else:
369370
option = Option(
370371
option.__args__, "No description provided"
371-
)
372+
)
372373

373374
if not isinstance(option, Option):
374375
option = Option(option, "No description provided")
@@ -403,7 +404,7 @@ def to_dict(self) -> Dict:
403404
"options": [o.to_dict() for o in self.options],
404405
}
405406
if self.is_subcommand:
406-
as_dict["type"] = OptionType.sub_command.value
407+
as_dict["type"] = SlashCommandOptionType.sub_command.value
407408

408409
return as_dict
409410

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

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

424425
# Checks if input_type is user, role or channel
425426
if (
426-
OptionType.user.value
427+
SlashCommandOptionType.user.value
427428
<= op.input_type.value
428-
<= OptionType.role.value
429+
<= SlashCommandOptionType.role.value
429430
):
430431
name = "member" if op.input_type.name == "user" else op.input_type.name
431432
arg = await get_or_fetch(ctx.guild, name, int(arg), default=int(arg))
432433

433-
elif op.input_type == OptionType.mentionable:
434+
elif op.input_type == SlashCommandOptionType.mentionable:
434435
arg_id = int(arg)
435436
arg = await get_or_fetch(ctx.guild, "member", arg_id)
436437
if arg is None:
437438
arg = ctx.guild.get_role(arg_id) or arg_id
438439

440+
elif op.input_type == SlashCommandOptionType.string and op._converter is not None:
441+
arg = await op._converter.convert(ctx, arg)
442+
439443
kwargs[op.name] = arg
440444

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

490494
channel_type_map = {
491-
'TextChannel': ChannelType.text,
492-
'VoiceChannel': ChannelType.voice,
493-
'StageChannel': ChannelType.stage_voice,
494-
'CategoryChannel': ChannelType.category
495+
'TextChannel': SlashCommandOptionType.text,
496+
'VoiceChannel': SlashCommandOptionType.voice,
497+
'StageChannel': SlashCommandOptionType.stage_voice,
498+
'CategoryChannel': SlashCommandOptionType.category
495499
}
496500

497501
class Option:
@@ -500,11 +504,15 @@ def __init__(
500504
) -> None:
501505
self.name: Optional[str] = kwargs.pop("name", None)
502506
self.description = description or "No description provided"
503-
504-
self.channel_types: List[ChannelType] = kwargs.pop("channel_types", [])
505-
if not isinstance(input_type, OptionType):
506-
self.input_type = OptionType.from_datatype(input_type)
507-
if self.input_type == OptionType.channel:
507+
self._converter = None
508+
self.channel_types: List[SlashCommandOptionType] = kwargs.pop("channel_types", [])
509+
if not isinstance(input_type, SlashCommandOptionType):
510+
to_assign = input_type() if isinstance(input_type, type) else input_type
511+
_type = SlashCommandOptionType.from_datatype(to_assign.__class__)
512+
if _type == SlashCommandOptionType.custom:
513+
self._converter = to_assign
514+
input_type = SlashCommandOptionType.string
515+
elif _type == SlashCommandOptionType.channel:
508516
if not isinstance(input_type, tuple):
509517
input_type = (input_type,)
510518
for i in input_type:
@@ -513,9 +521,7 @@ def __init__(
513521

514522
channel_type = channel_type_map[i.__name__]
515523
self.channel_types.append(channel_type)
516-
else:
517-
self.input_type = input_type
518-
524+
self.input_type = input_type
519525
self.required: bool = kwargs.pop("required", True)
520526
self.choices: List[OptionChoice] = [
521527
o if isinstance(o, OptionChoice) else OptionChoice(o)
@@ -533,7 +539,7 @@ def to_dict(self) -> Dict:
533539
}
534540
if self.channel_types:
535541
as_dict["channel_types"] = [t.value for t in self.channel_types]
536-
542+
537543
return as_dict
538544

539545

@@ -577,7 +583,7 @@ def __init__(
577583
validate_chat_input_name(name)
578584
validate_chat_input_description(description)
579585
super().__init__(
580-
OptionType.sub_command_group,
586+
SlashCommandOptionType.sub_command_group,
581587
name=name,
582588
description=description,
583589
)
File renamed without changes.
File renamed without changes.

discord/enums.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,9 @@ def from_datatype(cls, datatype):
642642
if issubclass(datatype, float):
643643
return cls.number
644644

645+
if hasattr(datatype, "convert"):
646+
return cls.custom
647+
645648
if datatype.__name__ == "Member":
646649
return cls.user
647650
if datatype.__name__ in [

discord/ext/commands/bot.py

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -121,28 +121,13 @@ def __repr__(self):
121121
_default = _DefaultRepr()
122122

123123
class BotBase(GroupMixin):
124-
def __init__(self, command_prefix=when_mentioned, help_command=_default, description=None, **options):
124+
_supports_prefixed_commands = True
125+
def __init__(self, command_prefix=when_mentioned, help_command=_default, **options):
125126
super().__init__(**options)
126127
self.command_prefix = command_prefix
127-
self.extra_events: Dict[str, List[CoroFunc]] = {}
128-
self.__cogs: Dict[str, Cog] = {}
129-
self.__extensions: Dict[str, types.ModuleType] = {}
130-
self._checks: List[Check] = []
131-
self._check_once = []
132-
self._before_invoke = None
133-
self._after_invoke = None
134128
self._help_command = None
135-
self.description = inspect.cleandoc(description) if description else ''
136-
self.owner_id = options.get('owner_id')
137-
self.owner_ids = options.get('owner_ids', set())
138129
self.strip_after_prefix = options.get('strip_after_prefix', False)
139130

140-
if self.owner_id and self.owner_ids:
141-
raise TypeError('Both owner_id and owner_ids are set.')
142-
143-
if self.owner_ids and not isinstance(self.owner_ids, collections.abc.Collection):
144-
raise TypeError(f'owner_ids must be a collection not {self.owner_ids.__class__!r}')
145-
146131
if help_command is _default:
147132
self.help_command = DefaultHelpCommand()
148133
else:
@@ -1034,6 +1019,23 @@ async def process_commands(self, message: Message) -> None:
10341019
async def on_message(self, message):
10351020
await self.process_commands(message)
10361021

1022+
1023+
1024+
1025+
1026+
1027+
1028+
1029+
1030+
1031+
1032+
1033+
1034+
1035+
1036+
1037+
1038+
10371039
class Bot(BotBase, discord.Bot):
10381040
"""Represents a discord bot.
10391041
@@ -1079,24 +1081,10 @@ class Bot(BotBase, discord.Bot):
10791081
Whether the commands should be case insensitive. Defaults to ``False``. This
10801082
attribute does not carry over to groups. You must set it to every group if
10811083
you require group commands to be case insensitive as well.
1082-
description: :class:`str`
1083-
The content prefixed into the default help message.
10841084
help_command: Optional[:class:`.HelpCommand`]
10851085
The help command implementation to use. This can be dynamically
10861086
set at runtime. To remove the help command pass ``None``. For more
10871087
information on implementing a help command, see :ref:`ext_commands_help_command`.
1088-
owner_id: Optional[:class:`int`]
1089-
The user ID that owns the bot. If this is not set and is then queried via
1090-
:meth:`.is_owner` then it is fetched automatically using
1091-
:meth:`~.Bot.application_info`.
1092-
owner_ids: Optional[Collection[:class:`int`]]
1093-
The user IDs that owns the bot. This is similar to :attr:`owner_id`.
1094-
If this is not set and the application is team based, then it is
1095-
fetched automatically using :meth:`~.Bot.application_info`.
1096-
For performance reasons it is recommended to use a :class:`set`
1097-
for the collection. You cannot set both ``owner_id`` and ``owner_ids``.
1098-
1099-
.. versionadded:: 1.3
11001088
strip_after_prefix: :class:`bool`
11011089
Whether to strip whitespace characters after encountering the command
11021090
prefix. This allows for ``! hello`` and ``!hello`` to both work if

0 commit comments

Comments
 (0)