diff --git a/discord/file.py b/discord/file.py index 5303e3251..aecd49502 100644 --- a/discord/file.py +++ b/discord/file.py @@ -60,16 +60,19 @@ class File: The filename to display when uploading to Discord. If this is not given then it defaults to ``fp.name`` or if ``fp`` is a string then the ``filename`` will default to the string given. + description: Optional[:class:`str`] + The description (alt text) for the file. spoiler: :class:`bool` Whether the attachment is a spoiler. """ - __slots__ = ('fp', 'filename', 'spoiler', '_original_pos', '_owner', '_closer') + __slots__ = ('fp', 'filename', 'spoiler', 'description', '_original_pos', '_owner', '_closer') if TYPE_CHECKING: fp: io.BufferedIOBase filename: Optional[str] spoiler: bool + description: Optional[str] def __init__( self, @@ -77,6 +80,7 @@ class File: filename: Optional[str] = None, *, spoiler: bool = False, + description: Optional[str] = None, ): if isinstance(fp, io.IOBase): if not (fp.seekable() and fp.readable()): @@ -89,10 +93,9 @@ class File: self._original_pos = 0 self._owner = True - # aiohttp only uses two methods from IOBase - # read and close, since I want to control when the files - # close, I need to stub it so it doesn't close unless - # I tell it to + # aiohttp only uses two methods from IOBase (read and close) + # Since I want to control when the files close, + # I need to stub it so it doesn't close unless I tell it to self._closer = self.fp.close self.fp.close = lambda: None @@ -108,6 +111,7 @@ class File: self.filename = 'SPOILER_' + self.filename self.spoiler = spoiler or (self.filename is not None and self.filename.startswith('SPOILER_')) + self.description = description def reset(self, *, seek: Union[int, bool] = True) -> None: # The `seek` parameter is needed because diff --git a/discord/http.py b/discord/http.py index e881062d8..4c6eeed1a 100644 --- a/discord/http.py +++ b/discord/http.py @@ -266,6 +266,7 @@ class HTTPClient: bucket = route.bucket method = route.method url = route.url + breakpoint() lock = self._locks.get(bucket) if lock is None: @@ -335,7 +336,7 @@ class HTTPClient: f.reset(seek=tries) if form: - form_data = aiohttp.FormData() + form_data = aiohttp.FormData(quote_fields=False) for params in form: form_data.add_field(**params) kwargs['data'] = form_data @@ -576,27 +577,25 @@ class HTTPClient: if stickers: payload['sticker_ids'] = stickers - form.append({'name': 'payload_json', 'value': utils._to_json(payload)}) - if len(files) == 1: - file = files[0] - form.append( + attachments = [] + for index, file in enumerate(files): + attachment = {'id': str(index), 'filename': file.filename} + if file.description is not None: + attachment['description'] = file.description + attachments.append(attachment) + lol = ( { - 'name': 'file', + 'name': f'files[{index}]', 'value': file.fp, 'filename': file.filename, 'content_type': 'application/octet-stream', } ) - else: - for index, file in enumerate(files): - form.append( - { - 'name': f'file{index}', - 'value': file.fp, - 'filename': file.filename, - 'content_type': 'application/octet-stream', - } - ) + form.append(lol) + breakpoint() + + payload['attachments'] = attachments + form.append({'name': 'payload_json', 'value': utils._to_json(payload)}) return self.request(route, form=form, files=files) @@ -649,7 +648,7 @@ class HTTPClient: return self.request(r, json=payload) - def ack_messages(self, read_states): # TODO: type and implement + def ack_messages(self, read_states) -> Response[None]: # TODO: type and implement payload = { 'read_states': read_states } @@ -1888,5 +1887,12 @@ class HTTPClient: return self.request(Route('POST', '/report'), json=payload) - def interact(self, data) -> Response[None]: - return self.request(Route('POST', '/interactions'), json=data) \ No newline at end of file + def get_application_commands(self, id): + return self.request(Route('GET', '/applications/{user_id}/commands', user_id=id)) + + def interact(self, payload, *, form_data=False) -> Response[None]: + if form_data: + form = [{'name': 'payload_json', 'value': utils._to_json(payload)}] + return self.request(Route('POST', '/interactions'), form=form) + else: + return self.request(Route('POST', '/interactions'), json=payload)