Browse Source

Implement attachment option type

pull/10109/head
dolfies 3 years ago
parent
commit
f1834e03d6
  1. 41
      discord/commands.py
  2. 21
      discord/http.py

41
discord/commands.py

@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations from __future__ import annotations
from typing import Any, Dict, List, Optional, Protocol, Tuple, Type, runtime_checkable, TYPE_CHECKING, Union from typing import Any, Dict, List, Optional, Protocol, Tuple, Type, runtime_checkable, Tuple, TYPE_CHECKING, Union
from .enums import AppCommandOptionType, AppCommandType, ChannelType, InteractionType, try_enum from .enums import AppCommandOptionType, AppCommandType, ChannelType, InteractionType, try_enum
from .errors import InvalidData from .errors import InvalidData
@ -35,8 +35,9 @@ from .utils import _generate_nonce, _get_as_snowflake
if TYPE_CHECKING: if TYPE_CHECKING:
from .abc import Messageable, Snowflake from .abc import Messageable, Snowflake
from .appinfo import InteractionApplication from .appinfo import InteractionApplication
from .file import File
from .interactions import Interaction from .interactions import Interaction
from .message import Message from .message import Attachment, Message
from .state import ConnectionState from .state import ConnectionState
__all__ = ( __all__ = (
@ -99,7 +100,7 @@ class ApplicationCommand(Protocol):
def __str__(self) -> str: def __str__(self) -> str:
return self.name return self.name
async def __call__(self, data, channel: Optional[Messageable] = None) -> Interaction: async def __call__(self, data: dict, files: Optional[List[File]] = None, channel: Optional[Messageable] = None) -> Interaction:
channel = channel or self.target_channel channel = channel or self.target_channel
if channel is None: if channel is None:
raise TypeError('__call__() missing 1 required argument: \'channel\'') raise TypeError('__call__() missing 1 required argument: \'channel\'')
@ -111,7 +112,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, files=files, nonce=nonce, application_id=self.application_id
) )
i = await state.client.wait_for( i = await state.client.wait_for(
'interaction_finish', 'interaction_finish',
@ -248,25 +249,26 @@ class SlashMixin(ApplicationCommand, Protocol):
options: List[Option] options: List[Option]
children: List[SubCommand] children: List[SubCommand]
async def __call__(self, options, channel=None): async def __call__(self, options: List[dict], files: Optional[List[File]], attachments: List[Attachment], channel: Optional[Messageable] = None) -> Interaction:
obj = self._parent obj = self._parent
command = obj._data command = obj._data
command['name_localized'] = command['name'] command['name_localized'] = command['name']
data = { data = {
'application_command': command, 'application_command': command,
'attachments': [], 'attachments': attachments,
'id': str(obj.id), 'id': str(obj.id),
'name': obj.name, 'name': obj.name,
'options': options, 'options': options,
'type': obj.type.value, 'type': obj.type.value,
'version': str(obj.version), 'version': str(obj.version),
} }
return await super().__call__(data, channel) return await super().__call__(data, files, channel)
def _parse_kwargs(self, kwargs: Dict[str, Any]) -> List[Dict[str, Any]]: def _parse_kwargs(self, kwargs: Dict[str, Any]) -> Tuple[List[Dict[str, Any]], List[File], List[Attachment]]:
possible_options = {o.name: o for o in self.options} possible_options = {o.name: o for o in self.options}
kwargs = {k: v for k, v in kwargs.items() if k in possible_options} kwargs = {k: v for k, v in kwargs.items() if k in possible_options}
options = [] options = []
files = []
for k, v in kwargs.items(): for k, v in kwargs.items():
option = possible_options[k] option = possible_options[k]
@ -281,6 +283,9 @@ class SlashMixin(ApplicationCommand, Protocol):
v = str(v.id) v = str(v.id)
elif type is AppCommandOptionType.boolean: elif type is AppCommandOptionType.boolean:
v = bool(v) v = bool(v)
elif type is AppCommandOptionType.attachment:
files.append(v)
v = len(files) - 1
else: else:
v = option._convert(v) v = option._convert(v)
@ -293,7 +298,11 @@ class SlashMixin(ApplicationCommand, Protocol):
options.append({'name': k, 'value': v, 'type': type.value}) options.append({'name': k, 'value': v, 'type': type.value})
return options attachments = []
for index, file in enumerate(files):
attachments.append(file.to_dict(index))
return options, files, attachments
def _unwrap_options(self, data: List[Dict[str, Any]]) -> None: def _unwrap_options(self, data: List[Dict[str, Any]]) -> None:
options = [] options = []
@ -352,7 +361,7 @@ class UserCommand(BaseCommand):
'type': self.type.value, 'type': self.type.value,
'version': str(self.version), 'version': str(self.version),
} }
return await super().__call__(data, channel) return await super().__call__(data, None, channel)
@property @property
def target_user(self) -> Optional[Snowflake]: def target_user(self) -> Optional[Snowflake]:
@ -408,7 +417,7 @@ class MessageCommand(BaseCommand):
'type': self.type.value, 'type': self.type.value,
'version': str(self.version), 'version': str(self.version),
} }
return await super().__call__(data, channel) return await super().__call__(data, None, channel)
@property @property
def target_message(self) -> Optional[Message]: def target_message(self) -> Optional[Message]:
@ -466,7 +475,7 @@ class SlashCommand(BaseCommand, SlashMixin):
if self.is_group(): if self.is_group():
raise TypeError('Cannot use a group') raise TypeError('Cannot use a group')
return await super().__call__(self._parse_kwargs(kwargs), channel) return await super().__call__(*self._parse_kwargs(kwargs), channel)
def __repr__(self) -> str: def __repr__(self) -> str:
BASE = f'<SlashCommand id={self.id} name={self.name!r}' BASE = f'<SlashCommand id={self.id} name={self.name!r}'
@ -567,11 +576,13 @@ class SubCommand(SlashMixin):
if self.is_group(): if self.is_group():
raise TypeError('Cannot use a group') raise TypeError('Cannot use a group')
options, files, attachments = self._parse_kwargs(kwargs)
options = [ options = [
{ {
'type': self._type.value, 'type': self._type.value,
'name': self.name, 'name': self.name,
'options': self._parse_kwargs(kwargs), 'options': options,
} }
] ]
for parent in self._walk_parents(): for parent in self._walk_parents():
@ -583,7 +594,7 @@ class SubCommand(SlashMixin):
} }
] ]
return await super().__call__(options, channel) return await super().__call__(options, files, attachments, channel)
def __repr__(self) -> str: def __repr__(self) -> str:
BASE = f'<SubCommand name={self.name!r}' BASE = f'<SubCommand name={self.name!r}'
@ -734,7 +745,7 @@ class OptionChoice:
---------- ----------
name: :class:`str` name: :class:`str`
The choice's displayed name. The choice's displayed name.
value: Any value: Union[:class:`str`, :class:`int`, :class:`float`]
The choice's value. The type of this depends on the option's type. The choice's value. The type of this depends on the option's type.
""" """

21
discord/http.py

@ -2372,9 +2372,9 @@ class HTTPClient:
channel: MessageableChannel, channel: MessageableChannel,
message: Optional[Message] = None, message: Optional[Message] = None,
*, *,
form_data: bool = False,
nonce: Optional[str] = MISSING, nonce: Optional[str] = MISSING,
application_id: Snowflake = MISSING, application_id: Snowflake = MISSING,
files: Optional[List[File]] = None,
) -> Response[None]: ) -> Response[None]:
state = getattr(message, '_state', channel._state) state = getattr(message, '_state', channel._state)
payload = { payload = {
@ -2395,8 +2395,17 @@ class HTTPClient:
if guild is not None: if guild is not None:
payload['guild_id'] = str(guild.id) payload['guild_id'] = str(guild.id)
if form_data: form = []
form = [{'name': 'payload_json', 'value': utils._to_json(payload)}] if files is not None:
return self.request(Route('POST', '/interactions'), form=form) form.append({'name': 'payload_json', 'value': utils._to_json(payload)})
else: for index, file in enumerate(files or []):
return self.request(Route('POST', '/interactions'), json=payload) form.append(
{
'name': f'files[{index}]',
'value': file.fp,
'filename': file.filename,
'content_type': 'application/octet-stream',
}
)
return self.request(Route('POST', '/interactions'), json=payload, form=form, files=files)

Loading…
Cancel
Save