From a105f8b8cc55eed787994245c4bcc689a59a4df3 Mon Sep 17 00:00:00 2001 From: Terrance Date: Mon, 29 Oct 2018 07:58:21 +0000 Subject: [PATCH] Webhooks: add support for multi-file upload `Webhook.send()` now accepts a `files` kwarg holding a list of `File` objects, which are included in the HTTP request as `file1`, `file2` and so on. This is an undocumented feature of the Discord API, but is analogous with the client's sending of messages with multiple files. --- discord/webhook.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/discord/webhook.py b/discord/webhook.py index 4c631af68..0dfe6ba62 100644 --- a/discord/webhook.py +++ b/discord/webhook.py @@ -104,13 +104,20 @@ class WebhookAdapter: # mocks a ConnectionState for appropriate use for Message return BaseUser(state=self, data=data) - def execute_webhook(self, *, payload, wait=False, file=None): + def execute_webhook(self, *, payload, wait=False, file=None, files=None): if file is not None: multipart = { 'file': file, 'payload_json': utils.to_json(payload) } data = None + elif files is not None: + multipart = { + 'payload_json': utils.to_json(payload) + } + for i, file in enumerate(files, start=1): + multipart['file%i' % i] = file + data = None else: data = payload multipart = None @@ -144,12 +151,12 @@ class AsyncWebhookAdapter(WebhookAdapter): data = utils.to_json(payload) if multipart: - file = multipart.pop('file', None) data = aiohttp.FormData() - if file: - data.add_field('file', file[1], filename=file[0], content_type=file[2]) for key, value in multipart.items(): - data.add_field(key, value) + if key.startswith('file'): + data.add_field(key, value[1], filename=value[0], content_type=value[2]) + else: + data.add_field(key, value) for tries in range(5): async with self.session.request(verb, url, headers=headers, data=data) as r: @@ -561,8 +568,8 @@ class Webhook: return self._adapter.edit_webhook(**payload) - def send(self, content=None, *, wait=False, username=None, avatar_url=None, - tts=False, file=None, embed=None, embeds=None): + def send(self, content=None, *, wait=False, username=None, avatar_url=None, tts=False, + file=None, files=None, embed=None, embeds=None): """|maybecoro| Sends a message using the webhook. @@ -596,7 +603,10 @@ class Webhook: tts: bool Indicates if the message should be sent using text-to-speech. file: :class:`File` - The file to upload. + The file to upload. This cannot be mixed with ``files`` parameter. + files: List[:class:`File`] + A list of files to send with the content. This cannot be mixed with the + ``file`` parameter. embed: :class:`Embed` The rich embed for the content to send. This cannot be mixed with ``embeds`` parameter. @@ -624,6 +634,8 @@ class Webhook: payload = {} + if files is not None and file is not None: + raise InvalidArgument('Cannot mix file and files keyword arguments.') if embeds is not None and embed is not None: raise InvalidArgument('Cannot mix embed and embeds keyword arguments.') @@ -650,6 +662,14 @@ class Webhook: return self._adapter.execute_webhook(wait=wait, file=to_pass, payload=payload) finally: file.close() + elif files is not None: + try: + to_pass = [(file.filename, file.open_file(), 'application/octet-stream') + for file in files] + return self._adapter.execute_webhook(wait=wait, files=to_pass, payload=payload) + finally: + for file in files: + file.close() else: return self._adapter.execute_webhook(wait=wait, payload=payload)