Browse Source

Implement BaseCommand.application properly; app command documentation fixes

pull/10109/head
dolfies 3 years ago
parent
commit
ec2834f050
  1. 12
      discord/appinfo.py
  2. 56
      discord/commands.py
  3. 13
      discord/iterators.py

12
discord/appinfo.py

@ -31,6 +31,7 @@ from .asset import Asset
from .enums import ApplicationType, ApplicationVerificationState, RPCApplicationState, StoreApplicationState, try_enum from .enums import ApplicationType, ApplicationVerificationState, RPCApplicationState, StoreApplicationState, try_enum
from .flags import ApplicationFlags from .flags import ApplicationFlags
from .mixins import Hashable from .mixins import Hashable
from .object import Object
from .permissions import Permissions from .permissions import Permissions
from .user import User from .user import User
@ -554,13 +555,8 @@ class InteractionApplication(Hashable):
The bot attached to the application. The bot attached to the application.
description: Optional[:class:`str`] description: Optional[:class:`str`]
The application description. The application description.
Only available from :attr:`~Modal.application`.
type: Optional[:class:`ApplicationType`] type: Optional[:class:`ApplicationType`]
The type of application. The type of application.
Only available from :attr:`~Modal.application`.
command_count: Optional[:class:`int`]
The number of commands the application has.
Only available from :attr:`~BaseCommand.application`.
""" """
__slots__ = ( __slots__ = (
@ -583,14 +579,16 @@ class InteractionApplication(Hashable):
def _update(self, data: dict) -> None: def _update(self, data: dict) -> None:
self.id: int = int(data['id']) self.id: int = int(data['id'])
self.name: str = data['name'] self.name: str = data['name']
self.description: Optional[str] = data.get('description') self.description: str = data.get('description') or ''
self._icon: Optional[str] = data.get('icon') self._icon: Optional[str] = data.get('icon')
self.type: Optional[ApplicationType] = try_enum(ApplicationType, data['type']) if 'type' in data else None self.type: Optional[ApplicationType] = try_enum(ApplicationType, data['type']) if 'type' in data else None
self.bot: User = None # type: ignore # This should never be None but it's volatile self.bot: User # User data should always be available, but these payloads are volatile
user = data.get('bot') user = data.get('bot')
if user is not None: if user is not None:
self.bot = User(state=self._state, data=user) self.bot = User(state=self._state, data=user)
else:
self.bot = Object(id=self.id) # type: ignore
def __repr__(self) -> str: def __repr__(self) -> str:
return f'<{self.__class__.__name__} id={self.id} name={self.name!r}>' return f'<{self.__class__.__name__} id={self.id} name={self.name!r}>'

56
discord/commands.py

@ -33,6 +33,7 @@ from .utils import _generate_nonce
if TYPE_CHECKING: if TYPE_CHECKING:
from .abc import Messageable, Snowflake from .abc import Messageable, Snowflake
from .appinfo import InteractionApplication
from .interactions import Interaction from .interactions import Interaction
from .message import Message from .message import Message
from .state import ConnectionState from .state import ConnectionState
@ -68,19 +69,27 @@ class ApplicationCommand(Protocol):
The command's description, if any. The command's description, if any.
type: :class:`.AppCommandType` type: :class:`.AppCommandType`
The type of application command. The type of application command.
default_permission: :class:`bool`
Whether the command is enabled in guilds by default.
application: Optional[:class:`InteractionApplication`]
The application this command belongs to.
Only available if requested.
application_id: :class:`int`
The ID of the application this command belongs to.
""" """
__slots__ = () __slots__ = ()
if TYPE_CHECKING: if TYPE_CHECKING:
_state: ConnectionState _state: ConnectionState
_application_id: int application_id: int
name: str name: str
description: str description: str
version: int version: int
type: AppCommandType type: AppCommandType
target_channel: Optional[Messageable] target_channel: Optional[Messageable]
default_permission: bool default_permission: bool
application: Optional[InteractionApplication]
def __str__(self) -> str: def __str__(self) -> str:
return self.name return self.name
@ -97,7 +106,7 @@ class ApplicationCommand(Protocol):
state._interaction_cache[nonce] = (type.value, data['name'], acc_channel) state._interaction_cache[nonce] = (type.value, data['name'], acc_channel)
try: try:
await state.http.interact( await state.http.interact(
type, data, acc_channel, form_data=True, nonce=nonce, application_id=self._application_id type, data, acc_channel, form_data=True, nonce=nonce, application_id=self.application_id
) )
i = await state.client.wait_for( i = await state.client.wait_for(
'interaction_finish', 'interaction_finish',
@ -138,14 +147,19 @@ class BaseCommand(ApplicationCommand, Hashable):
The command's ID. The command's ID.
version: :class:`int` version: :class:`int`
The command's version. The command's version.
default_permission: :class:`bool`
Whether the command is enabled in guilds by default.
name: :class:`str` name: :class:`str`
The command's name. The command's name.
description: :class:`str` description: :class:`str`
The command's description, if any. The command's description, if any.
type: :class:`AppCommandType` type: :class:`AppCommandType`
The type of application command. The type of application command.
default_permission: :class:`bool`
Whether the command is enabled in guilds by default.
application: Optional[:class:`InteractionApplication`]
The application this command belongs to.
Only available if requested.
application_id: :class:`int`
The ID of the application this command belongs to.
""" """
__slots__ = ( __slots__ = (
@ -155,27 +169,29 @@ class BaseCommand(ApplicationCommand, Hashable):
'version', 'version',
'type', 'type',
'default_permission', 'default_permission',
'application',
'application_id',
'_data', '_data',
'_state', '_state',
'_channel', '_channel',
'_application_id',
'_dm_permission', '_dm_permission',
'_default_member_permissions', '_default_member_permissions',
) )
def __init__(self, *, state: ConnectionState, data: Dict[str, Any], channel: Optional[Messageable] = None) -> None: def __init__(self, *, state: ConnectionState, data: Dict[str, Any], channel: Optional[Messageable] = None, application: Optional[InteractionApplication] = None) -> None:
self._state = state self._state = state
self._data = data self._data = data
self.name = data['name'] self.name = data['name']
self.description = data['description'] self.description = data['description']
self._channel = channel self._channel = channel
self._application_id: int = int(data['application_id']) self.application_id: int = int(data['application_id'])
self.id: int = int(data['id']) self.id: int = int(data['id'])
self.version = int(data['version']) self.version = int(data['version'])
self.type = try_enum(AppCommandType, data['type']) self.type = try_enum(AppCommandType, data['type'])
self.default_permission: bool = data['default_permission'] self.default_permission: bool = data.get('default_permission', True)
self._dm_permission = data.get('dm_permission') self._dm_permission = data.get('dm_permission')
self._default_member_permissions = data['default_member_permissions'] self._default_member_permissions = data['default_member_permissions']
self.application = application
def __repr__(self) -> str: def __repr__(self) -> str:
return f'<{self.__class__.__name__} id={self.id} name={self.name!r}>' return f'<{self.__class__.__name__} id={self.id} name={self.name!r}>'
@ -192,12 +208,6 @@ class BaseCommand(ApplicationCommand, Hashable):
""" """
return False return False
@property
def application(self):
"""The application this command belongs to."""
...
# return self._state.get_application(self._application_id)
@property @property
def target_channel(self) -> Optional[Messageable]: def target_channel(self) -> Optional[Messageable]:
"""Optional[:class:`.abc.Messageable`]: The channel this application command will be used on. """Optional[:class:`.abc.Messageable`]: The channel this application command will be used on.
@ -567,8 +577,9 @@ class SubCommand(SlashMixin):
return BASE + '>' return BASE + '>'
@property @property
def _application_id(self) -> int: def application_id(self) -> int:
return self._parent._application_id """:class:`int`: The ID of the application this command belongs to."""
return self._parent.application_id
@property @property
def version(self) -> int: def version(self) -> int:
@ -592,7 +603,9 @@ class SubCommand(SlashMixin):
@property @property
def application(self): def application(self):
"""The application this command belongs to.""" """Optional[:class:`InteractionApplication`]: The application this command belongs to.
Only available if requested.
"""
return self._parent.application return self._parent.application
@property @property
@ -608,7 +621,7 @@ class SubCommand(SlashMixin):
self._parent.target_channel = value self._parent.target_channel = value
class Option: # TODO: Add validation class Option:
"""Represents a command option. """Represents a command option.
.. container:: operations .. container:: operations
@ -633,12 +646,17 @@ class Option: # TODO: Add validation
Maximum value of the option. Only applicable to :attr:`AppCommandOptionType.integer` and :attr:`AppCommandOptionType.number`. Maximum value of the option. Only applicable to :attr:`AppCommandOptionType.integer` and :attr:`AppCommandOptionType.number`.
choices: List[:class:`OptionChoice`] choices: List[:class:`OptionChoice`]
A list of possible choices to choose from. If these are present, you must choose one from them. A list of possible choices to choose from. If these are present, you must choose one from them.
Only applicable to :attr:`AppCommandOptionType.string`, :attr:`AppCommandOptionType.integer`, and :attr:`AppCommandOptionType.number`. Only applicable to :attr:`AppCommandOptionType.string`, :attr:`AppCommandOptionType.integer`, and :attr:`AppCommandOptionType.number`.
channel_types: List[:class:`ChannelType`] channel_types: List[:class:`ChannelType`]
A list of channel types that you can choose from. If these are present, you must choose a channel that is one of these types. A list of channel types that you can choose from. If these are present, you must choose a channel that is one of these types.
Only applicable to :attr:`AppCommandOptionType.channel`. Only applicable to :attr:`AppCommandOptionType.channel`.
autocomplete: :class:`bool` autocomplete: :class:`bool`
Whether the option autocompletes. Always ``False`` if :attr:`choices` are present. Whether the option autocompletes.
Only applicable to :attr:`AppCommandOptionType.string`, :attr:`AppCommandOptionType.integer`, and :attr:`AppCommandOptionType.number`.
Always ``False`` if :attr:`choices` are present.
""" """
__slots__ = ( __slots__ = (

13
discord/iterators.py

@ -27,8 +27,9 @@ from __future__ import annotations
import asyncio import asyncio
from typing import Awaitable, TYPE_CHECKING, TypeVar, Optional, Any, Callable, Union, List, Tuple, AsyncIterator, Dict from typing import Awaitable, TYPE_CHECKING, TypeVar, Optional, Any, Callable, Union, List, Tuple, AsyncIterator, Dict
from .appinfo import InteractionApplication
from .errors import InvalidData from .errors import InvalidData
from .utils import _generate_nonce from .utils import _generate_nonce, _get_as_snowflake
from .object import Object from .object import Object
from .commands import _command_factory from .commands import _command_factory
from .enums import AppCommandType from .enums import AppCommandType
@ -97,6 +98,7 @@ class CommandIterator:
self.applications: bool = kwargs.get('applications', True) self.applications: bool = kwargs.get('applications', True)
self.application: Snowflake = kwargs.get('application', None) self.application: Snowflake = kwargs.get('application', None)
self.commands = asyncio.Queue() self.commands = asyncio.Queue()
self._application_cache: Dict[int, InteractionApplication] = {}
async def _process_args(self) -> Tuple[DMChannel, Optional[str], Optional[Union[User, Message]]]: async def _process_args(self) -> Tuple[DMChannel, Optional[str], Optional[Union[User, Message]]]:
item = self.item item = self.item
@ -189,19 +191,20 @@ class CommandIterator:
kwargs['offset'] += retrieve kwargs['offset'] += retrieve
for app in data.get('applications', []):
self._application_cache[int(app['id'])] = InteractionApplication(state=state, data=app)
for cmd in cmds: for cmd in cmds:
self.commands.put_nowait(self.create_command(cmd)) self.commands.put_nowait(self.create_command(cmd))
for app in data.get('applications', []):
...
def create_command(self, data) -> ApplicationCommand: def create_command(self, data) -> ApplicationCommand:
channel, item, value = self._tuple # type: ignore channel, item, value = self._tuple # type: ignore
if item is not None: if item is not None:
kwargs = {item: value} kwargs = {item: value}
else: else:
kwargs = {} kwargs = {}
return self.cls(state=channel._state, data=data, channel=channel, **kwargs) app_id = _get_as_snowflake(data, 'application_id')
return self.cls(state=channel._state, data=data, channel=channel, application=self._application_cache.get(app_id), **kwargs) # type: ignore
class FakeCommandIterator: class FakeCommandIterator:

Loading…
Cancel
Save