Skip to content

Commit 3220cec

Browse files
authored
Add description to Attachments and Files (#509)
1 parent 6f642c4 commit 3220cec

File tree

5 files changed

+47
-48
lines changed

5 files changed

+47
-48
lines changed

discord/file.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,26 @@ class File:
6161
The filename to display when uploading to Discord.
6262
If this is not given then it defaults to ``fp.name`` or if ``fp`` is
6363
a string then the ``filename`` will default to the string given.
64+
description: Optional[:class`str`]
65+
The description of a file, used by Discord to display alternative text on images.
6466
spoiler: :class:`bool`
6567
Whether the attachment is a spoiler.
6668
"""
6769

68-
__slots__ = ('fp', 'filename', 'spoiler', '_original_pos', '_owner', '_closer')
70+
__slots__ = ('fp', 'filename', 'spoiler', '_original_pos', '_owner', '_closer', 'description')
6971

7072
if TYPE_CHECKING:
7173
fp: io.BufferedIOBase
7274
filename: Optional[str]
75+
description: Optional[str]
7376
spoiler: bool
7477

7578
def __init__(
7679
self,
7780
fp: Union[str, bytes, os.PathLike, io.BufferedIOBase],
7881
filename: Optional[str] = None,
7982
*,
83+
description: Optional[str] = None,
8084
spoiler: bool = False,
8185
):
8286
if isinstance(fp, io.IOBase):
@@ -109,6 +113,7 @@ def __init__(
109113
self.filename = 'SPOILER_' + self.filename
110114

111115
self.spoiler = spoiler or (self.filename is not None and self.filename.startswith('SPOILER_'))
116+
self.description = description
112117

113118
def reset(self, *, seek: Union[int, bool] = True) -> None:
114119
# The `seek` parameter is needed because

discord/http.py

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ async def request(
267267
f.reset(seek=tries)
268268

269269
if form:
270-
form_data = aiohttp.FormData()
270+
form_data = aiohttp.FormData(quote_fields=False)
271271
for params in form:
272272
form_data.add_field(**params)
273273
kwargs['data'] = form_data
@@ -497,28 +497,20 @@ def send_multipart_helper(
497497
if stickers:
498498
payload['sticker_ids'] = stickers
499499

500-
form.append({'name': 'payload_json', 'value': utils._to_json(payload)})
501-
if len(files) == 1:
502-
file = files[0]
500+
attachments = []
501+
form.append({'name': 'payload_json'})
502+
for index, file in enumerate(files):
503+
attachments.append({'id': index, 'filename': file.filename, 'description': file.description})
503504
form.append(
504505
{
505-
'name': 'file',
506+
'name': f'files[{index}]',
506507
'value': file.fp,
507508
'filename': file.filename,
508509
'content_type': 'application/octet-stream',
509510
}
510511
)
511-
else:
512-
for index, file in enumerate(files):
513-
form.append(
514-
{
515-
'name': f'file{index}',
516-
'value': file.fp,
517-
'filename': file.filename,
518-
'content_type': 'application/octet-stream',
519-
}
520-
)
521-
512+
payload['attachments'] = attachments
513+
form[0]['value'] = utils._to_json(payload)
522514
return self.request(route, form=form, files=files)
523515

524516
def send_files(
@@ -559,27 +551,23 @@ def edit_multipart_helper(
559551
) -> Response[message.Message]:
560552
form = []
561553

562-
form.append({'name': 'payload_json', 'value': utils._to_json(payload)})
563-
if len(files) == 1:
564-
file = files[0]
554+
attachments = []
555+
form.append({'name': 'payload_json'})
556+
for index, file in enumerate(files):
557+
attachments.append({'id': index, 'filename': file.filename, 'description': file.description})
565558
form.append(
566559
{
567-
'name': 'file',
560+
'name': f'files[{index}]',
568561
'value': file.fp,
569562
'filename': file.filename,
570563
'content_type': 'application/octet-stream',
571564
}
572565
)
566+
if 'attachments' not in payload:
567+
payload['attachments'] = attachments
573568
else:
574-
for index, file in enumerate(files):
575-
form.append(
576-
{
577-
'name': f'file{index}',
578-
'value': file.fp,
579-
'filename': file.filename,
580-
'content_type': 'application/octet-stream',
581-
}
582-
)
569+
payload['attachments'].extend(attachments)
570+
form[0]['value'] = utils._to_json(payload)
583571

584572
return self.request(route, form=form, files=files)
585573

discord/interactions.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,6 @@ async def edit_message(
713713
payload['components'] = []
714714
else:
715715
payload['components'] = view.to_components()
716-
717716
adapter = async_context.get()
718717
await adapter.create_interaction_response(
719718
parent.id,

discord/message.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,14 @@ class Attachment(Hashable):
154154
Whether the attachment is ephemeral or not.
155155
156156
.. versionadded:: 1.7
157+
158+
description: Optional[:class:`str`]
159+
The attachment's description.
160+
161+
.. versionadded:: 2.0
157162
"""
158163

159-
__slots__ = ('id', 'size', 'height', 'width', 'filename', 'url', 'proxy_url', '_http', 'content_type', 'ephemeral')
164+
__slots__ = ('id', 'size', 'height', 'width', 'filename', 'url', 'proxy_url', '_http', 'content_type', 'ephemeral', 'description')
160165

161166
def __init__(self, *, data: AttachmentPayload, state: ConnectionState):
162167
self.id: int = int(data['id'])
@@ -169,6 +174,7 @@ def __init__(self, *, data: AttachmentPayload, state: ConnectionState):
169174
self._http = state.http
170175
self.content_type: Optional[str] = data.get('content_type')
171176
self.ephemeral: bool = data.get('ephemeral', False)
177+
self.description: Optional[str] = data.get('description')
172178

173179
def is_spoiler(self) -> bool:
174180
""":class:`bool`: Whether this attachment contains a spoiler."""
@@ -305,7 +311,7 @@ async def to_file(self, *, use_cached: bool = False, spoiler: bool = False) -> F
305311
"""
306312

307313
data = await self.read(use_cached=use_cached)
308-
return File(io.BytesIO(data), filename=self.filename, spoiler=spoiler)
314+
return File(io.BytesIO(data), filename=self.filename, spoiler=spoiler, description=self.description)
309315

310316
def to_dict(self) -> AttachmentPayload:
311317
result: AttachmentPayload = {
@@ -322,6 +328,8 @@ def to_dict(self) -> AttachmentPayload:
322328
result['width'] = self.width
323329
if self.content_type:
324330
result['content_type'] = self.content_type
331+
if self.description:
332+
result['description'] = self.description
325333
return result
326334

327335

@@ -1312,6 +1320,9 @@ async def edit(
13121320
raise InvalidArgument('cannot pass both file and files parameter to edit()')
13131321

13141322
if file is not MISSING:
1323+
if 'attachments' not in payload:
1324+
# don't want it to remove any attachments when we just add a new file
1325+
payload['attachments'] = [a.to_dict() for a in self.attachments]
13151326
if not isinstance(file, File):
13161327
raise InvalidArgument('file parameter must be File')
13171328

@@ -1330,6 +1341,9 @@ async def edit(
13301341
raise InvalidArgument('files parameter must be a list of up to 10 elements')
13311342
elif not all(isinstance(file, File) for file in files):
13321343
raise InvalidArgument('files parameter must be a list of File')
1344+
if 'attachments' not in payload:
1345+
# don't want it to remove any attachments when we just add a new file
1346+
payload['attachments'] = [a.to_dict() for a in self.attachments]
13331347

13341348
try:
13351349
data = await self._state.http.edit_files(

discord/webhook/async_.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ async def request(
145145
file.reset(seek=attempt)
146146

147147
if multipart:
148-
form_data = aiohttp.FormData()
148+
form_data = aiohttp.FormData(quote_fields=False)
149149
for p in multipart:
150150
form_data.add_field(**p)
151151
to_send = form_data
@@ -379,28 +379,21 @@ def create_interaction_response(
379379

380380
if data is not None:
381381
payload['data'] = data
382-
form = [{'name': 'payload_json', 'value': utils._to_json(payload)}]
382+
form = [{'name': 'payload_json'}]
383+
attachments = []
383384
files = files or []
384-
if len(files) == 1:
385-
file = files[0]
385+
for index, file in enumerate(files):
386+
attachments.append({'id': index, 'filename': file.filename, 'description': file.description})
386387
form.append(
387388
{
388-
'name': 'file',
389+
'name': f'files[{index}]',
389390
'value': file.fp,
390391
'filename': file.filename,
391392
'content_type': 'application/octet-stream',
392393
}
393394
)
394-
else:
395-
for index, file in enumerate(files):
396-
form.append(
397-
{
398-
'name': f'file{index}',
399-
'value': file.fp,
400-
'filename': file.filename,
401-
'content_type': 'application/octet-stream',
402-
}
403-
)
395+
payload['attachments'] = attachments
396+
form[0]['value'] = utils._to_json(payload)
404397

405398
route = Route(
406399
'POST',

0 commit comments

Comments
 (0)