diff --git a/discord/file.py b/discord/file.py index 701418ab4..6f176eef8 100644 --- a/discord/file.py +++ b/discord/file.py @@ -23,7 +23,7 @@ DEALINGS IN THE SOFTWARE. """ from __future__ import annotations -from typing import Optional, Union +from typing import Any, Dict, Optional, Union import os import io @@ -126,3 +126,14 @@ class File: self.fp.close = self._closer if self._owner: self._closer() + + def to_dict(self, index: int) -> Dict[str, Any]: + payload = { + 'id': index, + 'filename': self.filename, + } + + if self.description is not None: + payload['description'] = self.description + + return payload diff --git a/discord/http.py b/discord/http.py index a3f83ce81..42aa2199a 100644 --- a/discord/http.py +++ b/discord/http.py @@ -52,13 +52,13 @@ import aiohttp from .errors import HTTPException, Forbidden, NotFound, LoginFailure, DiscordServerError, GatewayNotFound, InvalidArgument from .gateway import DiscordClientWebSocketResponse +from .file import File from . import __version__, utils from .utils import MISSING _log = logging.getLogger(__name__) if TYPE_CHECKING: - from .file import File from .ui.view import View from .embeds import Embed from .mentions import AllowedMentions @@ -147,7 +147,7 @@ def handle_message_parameters( files: List[File] = MISSING, embed: Optional[Embed] = MISSING, embeds: List[Embed] = MISSING, - attachments: List[Attachment] = MISSING, + attachments: List[Union[Attachment, File]] = MISSING, view: Optional[View] = MISSING, allowed_mentions: Optional[AllowedMentions] = MISSING, message_reference: Optional[message.MessageReference] = MISSING, @@ -160,6 +160,12 @@ def handle_message_parameters( if embeds is not MISSING and embed is not MISSING: raise TypeError('Cannot mix embed and embeds keyword arguments.') + if file is not MISSING: + files = [file] + + if attachments is not MISSING and files is not MISSING: + raise TypeError('Cannot mix attachments and files keyword arguments.') + payload = {} if embeds is not MISSING: if len(embeds) > 10: @@ -190,11 +196,6 @@ def handle_message_parameters( if message_reference is not MISSING: payload['message_reference'] = message_reference - if attachments is not MISSING: - # Note: This will be overwritten if file or files is provided - # However, right now this is only passed via Message.edit not Messageable.send - payload['attachments'] = [a.to_dict() for a in attachments] - if stickers is not MISSING: if stickers is not None: payload['sticker_ids'] = stickers @@ -224,26 +225,25 @@ def handle_message_parameters( except KeyError: pass - multipart = [] - if file is not MISSING: - files = [file] + if attachments is MISSING: + attachments = files # type: ignore + else: + files = [a for a in attachments if isinstance(a, File)] - if files: - for index, file in enumerate(files): - attachments_payload = [] - for index, file in enumerate(files): - attachment = { - 'id': index, - 'filename': file.filename, - } - - if file.description is not None: - attachment['description'] = file.description - - attachments_payload.append(attachment) + if attachments is not MISSING: + file_index = 0 + attachments_payload = [] + for attachment in attachments: + if isinstance(attachment, File): + attachments_payload.append(attachment.to_dict(file_index)) + file_index += 1 + else: + attachments_payload.append(attachment.to_dict()) - payload['attachments'] = attachments_payload + payload['attachments'] = attachments_payload + multipart = [] + if files: multipart.append({'name': 'payload_json', 'value': utils._to_json(payload)}) payload = None for index, file in enumerate(files): @@ -596,7 +596,10 @@ class HTTPClient: def edit_message(self, channel_id: Snowflake, message_id: Snowflake, *, params: MultipartParameters) -> Response[message.Message]: r = Route('PATCH', '/channels/{channel_id}/messages/{message_id}', channel_id=channel_id, message_id=message_id) - return self.request(r, json=params.payload) + if params.files: + return self.request(r, files=params.files, form=params.multipart) + else: + return self.request(r, json=params.payload) def add_reaction(self, channel_id: Snowflake, message_id: Snowflake, emoji: str) -> Response[None]: r = Route( diff --git a/discord/interactions.py b/discord/interactions.py index 57925f2b3..d54756f93 100644 --- a/discord/interactions.py +++ b/discord/interactions.py @@ -259,8 +259,7 @@ class Interaction: content: Optional[str] = MISSING, embeds: List[Embed] = MISSING, embed: Optional[Embed] = MISSING, - file: File = MISSING, - files: List[File] = MISSING, + attachments: List[Union[Attachment, File]] = MISSING, view: Optional[View] = MISSING, allowed_mentions: Optional[AllowedMentions] = None, ) -> InteractionMessage: @@ -283,11 +282,14 @@ class Interaction: embed: Optional[:class:`Embed`] The embed to edit the message with. ``None`` suppresses the embeds. This should not be mixed with the ``embeds`` parameter. - file: :class:`File` - 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. + attachments: List[Union[:class:`Attachment`, :class:`File`]] + A list of attachments to keep in the message as well as new files to upload. If ``[]`` is passed + then all attachments are removed. + + .. note:: + + New files will always appear after current attachments. + allowed_mentions: :class:`AllowedMentions` Controls the mentions being processed in this message. See :meth:`.abc.Messageable.send` for more information. @@ -302,7 +304,7 @@ class Interaction: Forbidden Edited a message that is not yours. TypeError - You specified both ``embed`` and ``embeds`` or ``file`` and ``files`` + You specified both ``embed`` and ``embeds`` ValueError The length of ``embeds`` was invalid. @@ -315,8 +317,7 @@ class Interaction: previous_mentions: Optional[AllowedMentions] = self._state.allowed_mentions params = handle_message_parameters( content=content, - file=file, - files=files, + attachments=attachments, embed=embed, embeds=embeds, view=view, @@ -549,7 +550,7 @@ class InteractionResponse: content: Optional[Any] = MISSING, embed: Optional[Embed] = MISSING, embeds: List[Embed] = MISSING, - attachments: List[Attachment] = MISSING, + attachments: List[Union[Attachment, File]] = MISSING, view: Optional[View] = MISSING, allowed_mentions: Optional[AllowedMentions] = MISSING, ) -> None: @@ -567,9 +568,14 @@ class InteractionResponse: embed: Optional[:class:`Embed`] The embed to edit the message with. ``None`` suppresses the embeds. This should not be mixed with the ``embeds`` parameter. - attachments: List[:class:`Attachment`] - A list of attachments to keep in the message. If ``[]`` is passed + attachments: List[Union[:class:`Attachment`, :class:`File`]] + A list of attachments to keep in the message as well as new files to upload. If ``[]`` is passed then all attachments are removed. + + .. note:: + + New files will always appear after current attachments. + view: Optional[:class:`~discord.ui.View`] The updated view to update this message with. If ``None`` is passed then the view is removed. @@ -667,8 +673,7 @@ class InteractionMessage(Message): content: Optional[str] = MISSING, embeds: List[Embed] = MISSING, embed: Optional[Embed] = MISSING, - file: File = MISSING, - files: List[File] = MISSING, + attachments: List[Union[Attachment, File]] = MISSING, view: Optional[View] = MISSING, allowed_mentions: Optional[AllowedMentions] = None, ) -> InteractionMessage: @@ -685,11 +690,14 @@ class InteractionMessage(Message): embed: Optional[:class:`Embed`] The embed to edit the message with. ``None`` suppresses the embeds. This should not be mixed with the ``embeds`` parameter. - file: :class:`File` - 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. + attachments: List[Union[:class:`Attachment`, :class:`File`]] + A list of attachments to keep in the message as well as new files to upload. If ``[]`` is passed + then all attachments are removed. + + .. note:: + + New files will always appear after current attachments. + allowed_mentions: :class:`AllowedMentions` Controls the mentions being processed in this message. See :meth:`.abc.Messageable.send` for more information. @@ -704,7 +712,7 @@ class InteractionMessage(Message): Forbidden Edited a message that is not yours. TypeError - You specified both ``embed`` and ``embeds`` or ``file`` and ``files`` + You specified both ``embed`` and ``embeds`` ValueError The length of ``embeds`` was invalid. @@ -717,11 +725,62 @@ class InteractionMessage(Message): content=content, embeds=embeds, embed=embed, - file=file, - files=files, + attachments=attachments, view=view, allowed_mentions=allowed_mentions, ) + + async def add_files(self, *files: File) -> InteractionMessage: + r"""|coro| + + Adds new files to the end of the message attachments. + + .. versionadded:: 2.0 + + Parameters + ----------- + \*files: :class:`File` + New files to add to the message. + + Raises + ------- + HTTPException + Editing the message failed. + Forbidden + Tried to edit a message that isn't yours. + + Returns + --------- + :class:`InteractionMessage` + The newly edited message. + """ + return await self.edit(attachments=[*self.attachments, *files]) + + async def remove_attachments(self, *attachments: Attachment) -> InteractionMessage: + r"""|coro| + + Removes attachments from the message. + + .. versionadded:: 2.0 + + Parameters + ----------- + \*attachments: :class:`Attachment` + Attachments to remove from the message. + + Raises + ------- + HTTPException + Editing the message failed. + Forbidden + Tried to edit a message that isn't yours. + + Returns + --------- + :class:`InteractionMessage` + The newly edited message. + """ + return await self.edit(attachments=[a for a in self.attachments if a not in attachments]) async def delete(self, *, delay: Optional[float] = None) -> None: """|coro| diff --git a/discord/message.py b/discord/message.py index 58abb2a03..7aaa0aff5 100644 --- a/discord/message.py +++ b/discord/message.py @@ -1176,7 +1176,7 @@ class Message(Hashable): *, content: Optional[str] = ..., embed: Optional[Embed] = ..., - attachments: List[Attachment] = ..., + attachments: List[Union[Attachment, File]] = ..., suppress: bool = ..., delete_after: Optional[float] = ..., allowed_mentions: Optional[AllowedMentions] = ..., @@ -1190,7 +1190,7 @@ class Message(Hashable): *, content: Optional[str] = ..., embeds: List[Embed] = ..., - attachments: List[Attachment] = ..., + attachments: List[Union[Attachment, File]] = ..., suppress: bool = ..., delete_after: Optional[float] = ..., allowed_mentions: Optional[AllowedMentions] = ..., @@ -1203,7 +1203,7 @@ class Message(Hashable): content: Optional[str] = MISSING, embed: Optional[Embed] = MISSING, embeds: List[Embed] = MISSING, - attachments: List[Attachment] = MISSING, + attachments: List[Union[Attachment, File]] = MISSING, suppress: bool = MISSING, delete_after: Optional[float] = None, allowed_mentions: Optional[AllowedMentions] = MISSING, @@ -1231,10 +1231,14 @@ class Message(Hashable): To remove all embeds ``[]`` should be passed. .. versionadded:: 2.0 - attachments: List[:class:`Attachment`] - A list of attachments to keep in the message. If ``[]`` is passed + attachments: List[Union[:class:`Attachment`, :class:`File`]] + A list of attachments to keep in the message as well as new files to upload. If ``[]`` is passed then all attachments are removed. + .. note:: + + New files will always appear after current attachments. + .. versionadded:: 2.0 suppress: :class:`bool` Whether to suppress embeds for the message. This removes @@ -1299,6 +1303,58 @@ class Message(Hashable): return message + async def add_files(self, *files: File) -> Message: + r"""|coro| + + Adds new files to the end of the message attachments. + + .. versionadded:: 2.0 + + Parameters + ----------- + \*files: :class:`File` + New files to add to the message. + + Raises + ------- + HTTPException + Editing the message failed. + Forbidden + Tried to edit a message that isn't yours. + + Returns + -------- + :class:`Message` + The newly edited message. + """ + return await self.edit(attachments=[*self.attachments, *files]) + + async def remove_attachments(self, *attachments: Attachment) -> Message: + r"""|coro| + + Removes attachments from the message. + + .. versionadded:: 2.0 + + Parameters + ----------- + \*attachments: :class:`Attachment` + Attachments to remove from the message. + + Raises + ------- + HTTPException + Editing the message failed. + Forbidden + Tried to edit a message that isn't yours. + + Returns + -------- + :class:`Message` + The newly edited message. + """ + return await self.edit(attachments=[a for a in self.attachments if a not in attachments]) + async def publish(self) -> None: """|coro| @@ -1643,6 +1699,8 @@ class PartialMessage(Hashable): jump_url: str = Message.jump_url # type: ignore edit = Message.edit + add_files = Message.add_files + remove_attachments = Message.remove_attachments delete = Message.delete publish = Message.publish pin = Message.pin diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index d63d16cc4..a2f128564 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -30,7 +30,7 @@ import json import re from urllib.parse import quote as urlquote -from typing import Any, Dict, List, Literal, NamedTuple, Optional, TYPE_CHECKING, Tuple, Union, overload +from typing import Any, Dict, List, Literal, Optional, TYPE_CHECKING, Tuple, Union, overload from contextvars import ContextVar import weakref @@ -434,7 +434,7 @@ def interaction_message_response_params( files: List[File] = MISSING, embed: Optional[Embed] = MISSING, embeds: List[Embed] = MISSING, - attachments: List[Attachment] = MISSING, + attachments: List[Union[Attachment, File]] = MISSING, view: Optional[View] = MISSING, allowed_mentions: Optional[AllowedMentions] = MISSING, previous_allowed_mentions: Optional[AllowedMentions] = None, @@ -444,6 +444,12 @@ def interaction_message_response_params( if embeds is not MISSING and embed is not MISSING: raise TypeError('Cannot mix embed and embeds keyword arguments.') + if file is not MISSING: + files = [file] + + if attachments is not MISSING and files is not MISSING: + raise TypeError('Cannot mix attachments and files keyword arguments.') + data: Optional[Dict[str, Any]] = { 'tts': tts, } @@ -471,11 +477,6 @@ def interaction_message_response_params( else: data['components'] = [] - if attachments is not MISSING: - # Note: This will be overwritten if file or files is provided - # However, right now this is only passed via edit not send - data['attachments'] = [a.to_dict() for a in attachments] - if flags is not MISSING: data['flags'] = flags.value @@ -487,26 +488,25 @@ def interaction_message_response_params( elif previous_allowed_mentions is not None: data['allowed_mentions'] = previous_allowed_mentions.to_dict() - multipart = [] - if file is not MISSING: - files = [file] - - if files: - for index, file in enumerate(files): - attachments_payload = [] - for index, file in enumerate(files): - attachment = { - 'id': index, - 'filename': file.filename, - } - - if file.description is not None: - attachment['description'] = file.description + if attachments is MISSING: + attachments = files # type: ignore + else: + files = [a for a in attachments if isinstance(a, File)] - attachments_payload.append(attachment) + if attachments is not MISSING: + file_index = 0 + attachments_payload = [] + for attachment in attachments: + if isinstance(attachment, File): + attachments_payload.append(attachment.to_dict(file_index)) + file_index += 1 + else: + attachments_payload.append(attachment.to_dict()) - data['attachments'] = attachments_payload + data['attachments'] = attachments_payload + multipart = [] + if files: data = {'type': type, 'data': data} multipart.append({'name': 'payload_json', 'value': utils._to_json(data)}) data = None @@ -656,8 +656,7 @@ class WebhookMessage(Message): content: Optional[str] = MISSING, embeds: List[Embed] = MISSING, embed: Optional[Embed] = MISSING, - file: File = MISSING, - files: List[File] = MISSING, + attachments: List[Union[Attachment, File]] = MISSING, view: Optional[View] = MISSING, allowed_mentions: Optional[AllowedMentions] = None, ) -> WebhookMessage: @@ -679,13 +678,13 @@ class WebhookMessage(Message): embed: Optional[:class:`Embed`] The embed to edit the message with. ``None`` suppresses the embeds. This should not be mixed with the ``embeds`` parameter. - file: :class:`File` - The file to upload. This cannot be mixed with ``files`` parameter. + attachments: List[Union[:class:`Attachment`, :class:`File`]] + A list of attachments to keep in the message as well as new files to upload. If ``[]`` is passed + then all attachments are removed. + + .. note:: - .. versionadded:: 2.0 - files: List[:class:`File`] - A list of files to send with the content. This cannot be mixed with the - ``file`` parameter. + New files will always appear after current attachments. .. versionadded:: 2.0 allowed_mentions: :class:`AllowedMentions` @@ -704,7 +703,7 @@ class WebhookMessage(Message): Forbidden Edited a message that is not yours. TypeError - You specified both ``embed`` and ``embeds`` or ``file`` and ``files`` + You specified both ``embed`` and ``embeds`` ValueError The length of ``embeds`` was invalid InvalidArgument @@ -720,12 +719,63 @@ class WebhookMessage(Message): content=content, embeds=embeds, embed=embed, - file=file, - files=files, + attachments=attachments, view=view, allowed_mentions=allowed_mentions, ) + async def add_files(self, *files: File) -> WebhookMessage: + r"""|coro| + + Adds new files to the end of the message attachments. + + .. versionadded:: 2.0 + + Parameters + ----------- + \*files: :class:`File` + New files to add to the message. + + Raises + ------- + HTTPException + Editing the message failed. + Forbidden + Tried to edit a message that isn't yours. + + Returns + -------- + :class:`WebhookMessage` + The newly edited message. + """ + return await self.edit(attachments=[*self.attachments, *files]) + + async def remove_attachments(self, *attachments: Attachment) -> WebhookMessage: + r"""|coro| + + Removes attachments from the message. + + .. versionadded:: 2.0 + + Parameters + ----------- + \*attachments: :class:`Attachment` + Attachments to remove from the message. + + Raises + ------- + HTTPException + Editing the message failed. + Forbidden + Tried to edit a message that isn't yours. + + Returns + -------- + :class:`WebhookMessage` + The newly edited message. + """ + return await self.edit(attachments=[a for a in self.attachments if a not in attachments]) + async def delete(self, *, delay: Optional[float] = None) -> None: """|coro| @@ -1470,8 +1520,7 @@ class Webhook(BaseWebhook): content: Optional[str] = MISSING, embeds: List[Embed] = MISSING, embed: Optional[Embed] = MISSING, - file: File = MISSING, - files: List[File] = MISSING, + attachments: List[Union[Attachment, File]] = MISSING, view: Optional[View] = MISSING, allowed_mentions: Optional[AllowedMentions] = None, ) -> WebhookMessage: @@ -1498,13 +1547,9 @@ class Webhook(BaseWebhook): embed: Optional[:class:`Embed`] The embed to edit the message with. ``None`` suppresses the embeds. This should not be mixed with the ``embeds`` parameter. - file: :class:`File` - The file to upload. This cannot be mixed with ``files`` parameter. - - .. versionadded:: 2.0 - files: List[:class:`File`] - A list of files to send with the content. This cannot be mixed with the - ``file`` parameter. + attachments: List[Union[:class:`Attachment`, :class:`File`]] + A list of attachments to keep in the message as well as new files to upload. If ``[]`` is passed + then all attachments are removed. .. versionadded:: 2.0 allowed_mentions: :class:`AllowedMentions` @@ -1524,7 +1569,7 @@ class Webhook(BaseWebhook): Forbidden Edited a message that is not yours. TypeError - You specified both ``embed`` and ``embeds`` or ``file`` and ``files`` + You specified both ``embed`` and ``embeds`` ValueError The length of ``embeds`` was invalid InvalidArgument @@ -1549,8 +1594,7 @@ class Webhook(BaseWebhook): previous_mentions: Optional[AllowedMentions] = getattr(self._state, 'allowed_mentions', None) params = handle_message_parameters( content=content, - file=file, - files=files, + attachments=attachments, embed=embed, embeds=embeds, view=view, diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index bf79c2c1b..c2adf3f6c 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -59,6 +59,7 @@ if TYPE_CHECKING: from ..file import File from ..embeds import Embed from ..mentions import AllowedMentions + from ..message import Attachment from ..types.webhook import ( Webhook as WebhookPayload, ) @@ -381,8 +382,7 @@ class SyncWebhookMessage(Message): content: Optional[str] = MISSING, embeds: List[Embed] = MISSING, embed: Optional[Embed] = MISSING, - file: File = MISSING, - files: List[File] = MISSING, + attachments: List[Union[Attachment, File]] = MISSING, allowed_mentions: Optional[AllowedMentions] = None, ) -> SyncWebhookMessage: """Edits the message. @@ -396,11 +396,15 @@ class SyncWebhookMessage(Message): embed: Optional[:class:`Embed`] The embed to edit the message with. ``None`` suppresses the embeds. This should not be mixed with the ``embeds`` parameter. - file: :class:`File` - 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. + attachments: List[Union[:class:`Attachment`, :class:`File`]] + A list of attachments to keep in the message as well as new files to upload. If ``[]`` is passed + then all attachments are removed. + + .. note:: + + New files will always appear after current attachments. + + .. versionadded:: 2.0 allowed_mentions: :class:`AllowedMentions` Controls the mentions being processed in this message. See :meth:`.abc.Messageable.send` for more information. @@ -412,7 +416,7 @@ class SyncWebhookMessage(Message): Forbidden Edited a message that is not yours. TypeError - You specified both ``embed`` and ``embeds`` or ``file`` and ``files`` + You specified both ``embed`` and ``embeds`` ValueError The length of ``embeds`` was invalid InvalidArgument @@ -428,11 +432,58 @@ class SyncWebhookMessage(Message): content=content, embeds=embeds, embed=embed, - file=file, - files=files, + attachments=attachments, allowed_mentions=allowed_mentions, ) + def add_files(self, *files: File) -> SyncWebhookMessage: + r"""Adds new files to the end of the message attachments. + + .. versionadded:: 2.0 + + Parameters + ----------- + \*files: :class:`File` + New files to add to the message. + + Raises + ------- + HTTPException + Editing the message failed. + Forbidden + Tried to edit a message that isn't yours. + + Returns + -------- + :class:`SyncWebhookMessage` + The newly edited message. + """ + return self.edit(attachments=[*self.attachments, *files]) + + def remove_attachments(self, *attachments: Attachment) -> SyncWebhookMessage: + r"""Removes attachments from the message. + + .. versionadded:: 2.0 + + Parameters + ----------- + \*attachments: :class:`Attachment` + Attachments to remove from the message. + + Raises + ------- + HTTPException + Editing the message failed. + Forbidden + Tried to edit a message that isn't yours. + + Returns + -------- + :class:`SyncWebhookMessage` + The newly edited message. + """ + return self.edit(attachments=[a for a in self.attachments if a not in attachments]) + def delete(self, *, delay: Optional[float] = None) -> None: """Deletes the message. @@ -966,8 +1017,7 @@ class SyncWebhook(BaseWebhook): content: Optional[str] = MISSING, embeds: List[Embed] = MISSING, embed: Optional[Embed] = MISSING, - file: File = MISSING, - files: List[File] = MISSING, + attachments: List[Union[Attachment, File]] = MISSING, allowed_mentions: Optional[AllowedMentions] = None, ) -> SyncWebhookMessage: """Edits a message owned by this webhook. @@ -988,11 +1038,11 @@ class SyncWebhook(BaseWebhook): embed: Optional[:class:`Embed`] The embed to edit the message with. ``None`` suppresses the embeds. This should not be mixed with the ``embeds`` parameter. - file: :class:`File` - 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. + attachments: List[Union[:class:`Attachment`, :class:`File`]] + A list of attachments to keep in the message as well as new files to upload. If ``[]`` is passed + then all attachments are removed. + + .. versionadded:: 2.0 allowed_mentions: :class:`AllowedMentions` Controls the mentions being processed in this message. See :meth:`.abc.Messageable.send` for more information. @@ -1004,7 +1054,7 @@ class SyncWebhook(BaseWebhook): Forbidden Edited a message that is not yours. TypeError - You specified both ``embed`` and ``embeds`` or ``file`` and ``files`` + You specified both ``embed`` and ``embeds`` ValueError The length of ``embeds`` was invalid InvalidArgument @@ -1017,8 +1067,7 @@ class SyncWebhook(BaseWebhook): previous_mentions: Optional[AllowedMentions] = getattr(self._state, 'allowed_mentions', None) params = handle_message_parameters( content=content, - file=file, - files=files, + attachments=attachments, embed=embed, embeds=embeds, allowed_mentions=allowed_mentions,