Skip to content

Commit 131ceb0

Browse files
B1ue-Devpre-commit-ci[bot]Damego
authored
feat: Adding files support for file sending in context (#1171)
* feat: Adding `files` support for file sending * ci: correct from checks. * docs: Add `files` to docstring * Update interactions/api/http/interaction.py Co-authored-by: Damego <[email protected]> * Update interactions/api/http/interaction.py Co-authored-by: Damego <[email protected]> * Update interactions/api/http/interaction.py Co-authored-by: Damego <[email protected]> * Update interactions/api/http/interaction.py Co-authored-by: Damego <[email protected]> * Update interactions/api/http/interaction.py Co-authored-by: Damego <[email protected]> * Update interactions/api/http/interaction.py Co-authored-by: Damego <[email protected]> * Update interactions/api/http/interaction.py Co-authored-by: Damego <[email protected]> * Update interactions/api/http/interaction.py Co-authored-by: Damego <[email protected]> * Update interaction.py * Update interaction.py * Update interactions/api/http/interaction.py Co-authored-by: Damego <[email protected]> * Update interactions/api/http/interaction.py Co-authored-by: Damego <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Damego <[email protected]>
1 parent 4a1bcac commit 131ceb0

File tree

2 files changed

+134
-22
lines changed

2 files changed

+134
-22
lines changed

interactions/api/http/interaction.py

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from typing import TYPE_CHECKING, List, Optional, Union
22

3+
from aiohttp import MultipartWriter
4+
35
from ..models import Snowflake
6+
from ..models.message import File
47
from .request import _Request
58
from .route import Route
69

@@ -216,17 +219,41 @@ async def get_all_application_command_permissions(
216219
)
217220

218221
async def create_interaction_response(
219-
self, token: str, application_id: int, data: dict
222+
self,
223+
token: str,
224+
application_id: int,
225+
data: dict,
226+
files: Optional[List[File]] = None,
220227
) -> None:
221228
"""
222229
Posts initial response to an interaction, but you need to add the token.
223230
224231
:param token: Token.
225232
:param application_id: Application ID snowflake
226233
:param data: The data to send.
234+
:param files: The files to send.
227235
"""
236+
237+
file_data = None
238+
if files:
239+
240+
file_data = MultipartWriter("form-data")
241+
part = file_data.append_json(data)
242+
part.set_content_disposition("form-data", name="payload_json")
243+
data = None
244+
245+
for id, file in enumerate(files):
246+
part = file_data.append(
247+
file._fp,
248+
)
249+
part.set_content_disposition(
250+
"form-data", name=f"files[{str(id)}]", filename=file._filename
251+
)
252+
228253
return await self._req.request(
229-
Route("POST", f"/interactions/{application_id}/{token}/callback"), json=data
254+
Route("POST", f"/interactions/{application_id}/{token}/callback"),
255+
json=data,
256+
data=file_data,
230257
)
231258

232259
# This is still Interactions, but this also applies to webhooks
@@ -248,21 +275,44 @@ async def get_original_interaction_response(
248275
)
249276

250277
async def edit_interaction_response(
251-
self, data: dict, token: str, application_id: str, message_id: str = "@original"
278+
self,
279+
data: dict,
280+
token: str,
281+
application_id: str,
282+
files: Optional[List[File]] = None,
283+
message_id: str = "@original",
252284
) -> dict:
253285
"""
254286
Edits an existing interaction message, but token needs to be manually called.
255287
256288
:param data: A dictionary containing the new response.
257289
:param token: the token of the interaction
258290
:param application_id: Application ID snowflake.
291+
:param files: The files to send.
259292
:param message_id: Message ID snowflake. Defaults to `@original` which represents the initial response msg.
260293
:return: Updated message data.
261294
"""
262295
# ^ again, I don't know if python will let me
296+
file_data = None
297+
if files:
298+
299+
file_data = MultipartWriter("form-data")
300+
part = file_data.append_json(data)
301+
part.set_content_disposition("form-data", name="payload_json")
302+
data = None
303+
304+
for id, file in enumerate(files):
305+
part = file_data.append(
306+
file._fp,
307+
)
308+
part.set_content_disposition(
309+
"form-data", name=f"files[{id}]", filename=file._filename
310+
)
311+
263312
return await self._req.request(
264313
Route("PATCH", f"/webhooks/{application_id}/{token}/messages/{message_id}"),
265314
json=data,
315+
data=file_data,
266316
)
267317

268318
async def delete_interaction_response(
@@ -283,15 +333,39 @@ async def delete_interaction_response(
283333
Route("DELETE", f"/webhooks/{int(application_id)}/{token}/messages/{message_id}")
284334
)
285335

286-
async def _post_followup(self, data: dict, token: str, application_id: str) -> dict:
336+
async def _post_followup(
337+
self,
338+
data: dict,
339+
token: str,
340+
application_id: str,
341+
files: Optional[List[File]] = None,
342+
) -> dict:
287343
"""
288344
Send a followup to an interaction.
289345
290346
:param data: the payload to send
291347
:param application_id: the id of the application
292348
:param token: the token of the interaction
349+
:param files: the files to send
293350
"""
294351

352+
file_data = None
353+
if files:
354+
file_data = MultipartWriter("form-data")
355+
part = file_data.append_json(data)
356+
part.set_content_disposition("form-data", name="payload_json")
357+
data = None
358+
359+
for id, file in enumerate(files):
360+
part = file_data.append(
361+
file._fp,
362+
)
363+
part.set_content_disposition(
364+
"form-data", name=f"files[{id}]", filename=file._filename
365+
)
366+
295367
return await self._req.request(
296-
Route("POST", f"/webhooks/{application_id}/{token}"), json=data
368+
Route("POST", f"/webhooks/{application_id}/{token}"),
369+
json=data,
370+
data=file_data,
297371
)

interactions/client/context.py

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from ..api.models.flags import Permissions
77
from ..api.models.guild import Guild
88
from ..api.models.member import Member
9-
from ..api.models.message import Attachment, Embed, Message, MessageReference
9+
from ..api.models.message import Attachment, Embed, File, Message, MessageReference
1010
from ..api.models.misc import AllowedMentions, Snowflake
1111
from ..api.models.user import User
1212
from ..base import get_logger
@@ -112,6 +112,7 @@ async def send(
112112
*,
113113
tts: Optional[bool] = MISSING,
114114
attachments: Optional[List[Attachment]] = MISSING,
115+
files: Optional[Union[File, List[File]]] = MISSING,
115116
embeds: Optional[Union[Embed, List[Embed]]] = MISSING,
116117
allowed_mentions: Optional[Union[AllowedMentions, dict]] = MISSING,
117118
components: Optional[
@@ -127,6 +128,7 @@ async def send(
127128
:param Optional[str] content: The contents of the message as a string or string-converted value.
128129
:param Optional[bool] tts: Whether the message utilizes the text-to-speech Discord programme or not.
129130
:param Optional[List[Attachment]] attachments: The attachments to attach to the message. Needs to be uploaded to the CDN first
131+
:param Optional[Union[File, List[File]]] files: The files to attach to the message.
130132
:param Optional[Union[Embed, List[Embed]]] embeds: An embed, or list of embeds for the message.
131133
:param Optional[Union[AllowedMentions, dict]] allowed_mentions: The allowed mentions for the message.
132134
:param Optional[Union[ActionRow, Button, SelectMenu, List[Union[ActionRow, Button, SelectMenu]]]] components: A component, or list of components for the message.
@@ -188,14 +190,27 @@ async def send(
188190

189191
_attachments = [] if attachments is MISSING else [a._json for a in attachments]
190192

191-
return dict(
192-
content=_content,
193-
tts=_tts,
194-
embeds=_embeds,
195-
allowed_mentions=_allowed_mentions,
196-
components=_components,
197-
attachments=_attachments,
198-
flags=_flags,
193+
if not files or files is MISSING:
194+
_files = []
195+
elif isinstance(files, list):
196+
_files = [file._json_payload(id) for id, file in enumerate(files)]
197+
else:
198+
_files = [files._json_payload(0)]
199+
files = [files]
200+
201+
_files.extend(_attachments)
202+
203+
return (
204+
dict(
205+
content=_content,
206+
tts=_tts,
207+
embeds=_embeds,
208+
allowed_mentions=_allowed_mentions,
209+
components=_components,
210+
attachments=_files,
211+
flags=_flags,
212+
),
213+
files,
199214
)
200215

201216
async def edit(
@@ -204,6 +219,7 @@ async def edit(
204219
*,
205220
tts: Optional[bool] = MISSING,
206221
attachments: Optional[List[Attachment]] = MISSING,
222+
files: Optional[Union[File, List[File]]] = MISSING,
207223
embeds: Optional[Union[Embed, List[Embed]]] = MISSING,
208224
allowed_mentions: Optional[Union[AllowedMentions, dict]] = MISSING,
209225
message_reference: Optional[MessageReference] = MISSING,
@@ -225,6 +241,7 @@ async def edit(
225241
if self.message.content is not None or content is not MISSING:
226242
_content: str = self.message.content if content is MISSING else content
227243
payload["content"] = _content
244+
228245
_tts: bool = False if tts is MISSING else tts
229246
payload["tts"] = _tts
230247

@@ -252,7 +269,17 @@ async def edit(
252269
else []
253270
)
254271

255-
payload["attachments"] = _attachments
272+
if not files or files is MISSING:
273+
_files = []
274+
elif isinstance(files, list):
275+
_files = [file._json_payload(id) for id, file in enumerate(files)]
276+
else:
277+
_files = [files._json_payload(0)]
278+
files = [files]
279+
280+
_files.extend(_attachments)
281+
282+
payload["attachments"] = _files
256283

257284
_allowed_mentions: dict = (
258285
{}
@@ -276,7 +303,7 @@ async def edit(
276303

277304
payload["components"] = _components
278305

279-
return payload
306+
return payload, files
280307

281308
async def popup(self, modal: Modal) -> dict:
282309
"""
@@ -374,7 +401,7 @@ async def edit(
374401
self, content: Optional[str] = MISSING, **kwargs
375402
) -> Message: # sourcery skip: low-code-quality
376403

377-
payload = await super().edit(content, **kwargs)
404+
payload, files = await super().edit(content, **kwargs)
378405
msg = None
379406

380407
if self.deferred:
@@ -385,7 +412,7 @@ async def edit(
385412
):
386413
try:
387414
res = await self._client.edit_message(
388-
int(self.channel_id), int(self.message.id), payload=payload
415+
int(self.channel_id), int(self.message.id), payload=payload, files=files
389416
)
390417
except LibraryException as e:
391418
if e.code in {10015, 10018}:
@@ -401,6 +428,7 @@ async def edit(
401428
token=self.token,
402429
application_id=str(self.id),
403430
data=payload,
431+
files=files,
404432
message_id=self.message.id
405433
if self.message and self.message.flags != 64
406434
else "@original",
@@ -416,7 +444,10 @@ async def edit(
416444
else:
417445
try:
418446
res = await self._client.edit_interaction_response(
419-
token=self.token, application_id=str(self.application_id), data=payload
447+
token=self.token,
448+
application_id=str(self.application_id),
449+
data=payload,
450+
files=files,
420451
)
421452
except LibraryException as e:
422453
if e.code in {10015, 10018}:
@@ -450,7 +481,7 @@ async def defer(self, ephemeral: Optional[bool] = False) -> None:
450481
self.responded = True
451482

452483
async def send(self, content: Optional[str] = MISSING, **kwargs) -> Message:
453-
payload = await super().send(content, **kwargs)
484+
payload, files = await super().send(content, **kwargs)
454485

455486
if not self.deferred:
456487
self.callback = InteractionCallbackType.CHANNEL_MESSAGE_WITH_SOURCE
@@ -461,6 +492,7 @@ async def send(self, content: Optional[str] = MISSING, **kwargs) -> Message:
461492
if self.responded:
462493
res = await self._client._post_followup(
463494
data=payload,
495+
files=files,
464496
token=self.token,
465497
application_id=str(self.application_id),
466498
)
@@ -470,6 +502,7 @@ async def send(self, content: Optional[str] = MISSING, **kwargs) -> Message:
470502
token=self.token,
471503
application_id=int(self.id),
472504
data=_payload,
505+
files=files,
473506
)
474507

475508
try:
@@ -571,13 +604,14 @@ class ComponentContext(_Context):
571604

572605
async def edit(self, content: Optional[str] = MISSING, **kwargs) -> Message:
573606

574-
payload = await super().edit(content, **kwargs)
607+
payload, files = await super().edit(content, **kwargs)
575608
msg = None
576609

577610
if not self.deferred:
578611
self.callback = InteractionCallbackType.UPDATE_MESSAGE
579612
await self._client.create_interaction_response(
580613
data={"type": self.callback.value, "data": payload},
614+
files=files,
581615
token=self.token,
582616
application_id=int(self.id),
583617
)
@@ -595,12 +629,14 @@ async def edit(self, content: Optional[str] = MISSING, **kwargs) -> Message:
595629
elif self.callback != InteractionCallbackType.DEFERRED_UPDATE_MESSAGE:
596630
await self._client._post_followup(
597631
data=payload,
632+
files=files,
598633
token=self.token,
599634
application_id=str(self.application_id),
600635
)
601636
else:
602637
res = await self._client.edit_interaction_response(
603638
data=payload,
639+
files=files,
604640
token=self.token,
605641
application_id=str(self.application_id),
606642
)
@@ -610,7 +646,7 @@ async def edit(self, content: Optional[str] = MISSING, **kwargs) -> Message:
610646
return msg if msg is not None else Message(**payload, _client=self._client)
611647

612648
async def send(self, content: Optional[str] = MISSING, **kwargs) -> Message:
613-
payload = await super().send(content, **kwargs)
649+
payload, files = await super().send(content, **kwargs)
614650

615651
if not self.deferred:
616652
self.callback = InteractionCallbackType.CHANNEL_MESSAGE_WITH_SOURCE
@@ -621,6 +657,7 @@ async def send(self, content: Optional[str] = MISSING, **kwargs) -> Message:
621657
if self.responded:
622658
res = await self._client._post_followup(
623659
data=payload,
660+
files=files,
624661
token=self.token,
625662
application_id=str(self.application_id),
626663
)
@@ -630,6 +667,7 @@ async def send(self, content: Optional[str] = MISSING, **kwargs) -> Message:
630667
token=self.token,
631668
application_id=int(self.id),
632669
data=_payload,
670+
files=files,
633671
)
634672

635673
try:

0 commit comments

Comments
 (0)