Skip to content

Commit 8cb3de6

Browse files
authored
add support for sending file(s) in interaction response (#263)
* add support for sending file(s) in interaction response * update docstring * fix * add ephemeral attribute to Attachment
1 parent 897c93b commit 8cb3de6

File tree

3 files changed

+66
-14
lines changed

3 files changed

+66
-14
lines changed

discord/interactions.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131

3232
from . import utils
3333
from .enums import try_enum, InteractionType, InteractionResponseType
34-
from .errors import InteractionResponded, HTTPException, ClientException
34+
from .errors import InteractionResponded, HTTPException, ClientException, InvalidArgument
3535
from .channel import PartialMessageable, ChannelType
36-
36+
from .file import File
3737
from .user import User
3838
from .member import Member
3939
from .message import Message, Attachment
@@ -55,7 +55,6 @@
5555
)
5656
from .guild import Guild
5757
from .state import ConnectionState
58-
from .file import File
5958
from .mentions import AllowedMentions
6059
from aiohttp import ClientSession
6160
from .embeds import Embed
@@ -463,6 +462,8 @@ async def send_message(
463462
tts: bool = False,
464463
ephemeral: bool = False,
465464
allowed_mentions: AllowedMentions = None,
465+
file: File = None,
466+
files: List[File] = None,
466467
delete_after: float = None
467468
) -> None:
468469
"""|coro|
@@ -493,6 +494,10 @@ async def send_message(
493494
delete_after: :class:`float`
494495
If provided, the number of seconds to wait in the background
495496
before deleting the message we just sent.
497+
file: :class:`File`
498+
The file to upload.
499+
files: :class:`List[File]`
500+
A list of files to upload. Must be a maximum of 10.
496501
497502
Raises
498503
-------
@@ -535,15 +540,36 @@ async def send_message(
535540
if allowed_mentions:
536541
payload['allowed_mentions'] = allowed_mentions.to_dict()
537542

543+
if file is not None and files is not None:
544+
raise InvalidArgument('cannot pass both file and files parameter to send()')
545+
546+
if file is not None:
547+
if not isinstance(file, File):
548+
raise InvalidArgument('file parameter must be File')
549+
else:
550+
files = [file]
551+
552+
if files is not None:
553+
if len(files) > 10:
554+
raise InvalidArgument('files parameter must be a list of up to 10 elements')
555+
elif not all(isinstance(file, File) for file in files):
556+
raise InvalidArgument('files parameter must be a list of File')
557+
538558
parent = self._parent
539559
adapter = async_context.get()
540-
await adapter.create_interaction_response(
541-
parent.id,
542-
parent.token,
543-
session=parent._session,
544-
type=InteractionResponseType.channel_message.value,
545-
data=payload,
546-
)
560+
try:
561+
await adapter.create_interaction_response(
562+
parent.id,
563+
parent.token,
564+
session=parent._session,
565+
type=InteractionResponseType.channel_message.value,
566+
data=payload,
567+
files=files
568+
)
569+
finally:
570+
if files:
571+
for file in files:
572+
file.close()
547573

548574
if view is not MISSING:
549575
if ephemeral and view.timeout is None:
@@ -784,4 +810,4 @@ async def inner_call(delay: float = delay):
784810

785811
asyncio.create_task(inner_call())
786812
else:
787-
await self._state._interaction.delete_original_message()
813+
await self._state._interaction.delete_original_message()

discord/message.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,13 @@ class Attachment(Hashable):
150150
minutes or not valid at all.
151151
content_type: Optional[:class:`str`]
152152
The attachment's `media type <https://en.wikipedia.org/wiki/Media_type>`_
153+
ephemeral: :class:`bool`
154+
Whether the attachment is ephemeral or not.
153155
154156
.. versionadded:: 1.7
155157
"""
156158

157-
__slots__ = ('id', 'size', 'height', 'width', 'filename', 'url', 'proxy_url', '_http', 'content_type')
159+
__slots__ = ('id', 'size', 'height', 'width', 'filename', 'url', 'proxy_url', '_http', 'content_type', 'ephemeral')
158160

159161
def __init__(self, *, data: AttachmentPayload, state: ConnectionState):
160162
self.id: int = int(data['id'])
@@ -166,6 +168,7 @@ def __init__(self, *, data: AttachmentPayload, state: ConnectionState):
166168
self.proxy_url: str = data.get('proxy_url')
167169
self._http = state.http
168170
self.content_type: Optional[str] = data.get('content_type')
171+
self.ephemeral: bool = data['ephemeral']
169172

170173
def is_spoiler(self) -> bool:
171174
""":class:`bool`: Whether this attachment contains a spoiler."""

discord/webhook/async_.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ async def request(
146146
for p in multipart:
147147
form_data.add_field(**p)
148148
to_send = form_data
149-
150149
try:
151150
async with session.request(method, url, data=to_send, headers=headers, params=params) as response:
152151
_log.debug(
@@ -350,13 +349,37 @@ def create_interaction_response(
350349
session: aiohttp.ClientSession,
351350
type: int,
352351
data: Optional[Dict[str, Any]] = None,
352+
files: List[File]=None
353+
353354
) -> Response[None]:
354355
payload: Dict[str, Any] = {
355356
'type': type,
356357
}
357358

358359
if data is not None:
359360
payload['data'] = data
361+
form = [{'name': 'payload_json', 'value': utils._to_json(payload)}]
362+
files = files or []
363+
if len(files) == 1:
364+
file = files[0]
365+
form.append(
366+
{
367+
'name': 'file',
368+
'value': file.fp,
369+
'filename': file.filename,
370+
'content_type': 'application/octet-stream',
371+
}
372+
)
373+
else:
374+
for index, file in enumerate(files):
375+
form.append(
376+
{
377+
'name': f'file{index}',
378+
'value': file.fp,
379+
'filename': file.filename,
380+
'content_type': 'application/octet-stream',
381+
}
382+
)
360383

361384
route = Route(
362385
'POST',
@@ -365,7 +388,7 @@ def create_interaction_response(
365388
webhook_token=token,
366389
)
367390

368-
return self.request(route, session=session, payload=payload)
391+
return self.request(route, session=session, files=files, multipart=form)
369392

370393
def get_original_interaction_response(
371394
self,

0 commit comments

Comments
 (0)