mirror of
https://github.com/MelisaDev/melisa.git
synced 2024-09-22 19:22:01 +03:00
feat(file): Add support for sending file in message
This commit is contained in:
parent
270792f803
commit
96cbe5ea0e
3 changed files with 120 additions and 8 deletions
|
@ -197,7 +197,7 @@ class HTTPClient:
|
|||
Optional[:class:`Dict`]
|
||||
JSON response from the Discord API.
|
||||
"""
|
||||
return await self.__send("POST", route, json=data, headers=headers)
|
||||
return await self.__send("POST", route, data=data, headers=headers)
|
||||
|
||||
async def delete(self, route: str, *, headers: dict = None) -> Optional[Dict]:
|
||||
"""|coro|
|
||||
|
|
|
@ -17,6 +17,7 @@ from typing import (
|
|||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
from ..message.file import File, create_form
|
||||
from ..message.message import Message
|
||||
from ...exceptions import EmbedFieldError
|
||||
from ...models.message.embed import Embed
|
||||
|
@ -511,7 +512,13 @@ class MessageableChannel(Channel):
|
|||
)
|
||||
|
||||
async def send(
|
||||
self, content: str = None, *, embed: Embed = None, embeds: List[Embed] = None
|
||||
self,
|
||||
content: str = None,
|
||||
*,
|
||||
embed: Embed = None,
|
||||
embeds: List[Embed] = None,
|
||||
file: File = None,
|
||||
files: List[File] = None,
|
||||
) -> Message:
|
||||
"""|coro|
|
||||
|
||||
|
@ -527,6 +534,10 @@ class MessageableChannel(Channel):
|
|||
Embed
|
||||
embeds: Optional[List[:class:`~melisa.models.message.embed.Embed`]]
|
||||
List of embeds
|
||||
file: Optional[:class:`~melisa.models.message.file.File`]
|
||||
File
|
||||
files: Optional[List[:class:`~melisa.models.message.file.File`]]
|
||||
List of files
|
||||
|
||||
Raises
|
||||
-------
|
||||
|
@ -539,14 +550,14 @@ class MessageableChannel(Channel):
|
|||
"""
|
||||
|
||||
# ToDo: Add other parameters
|
||||
# ToDo: add file checks
|
||||
|
||||
if embeds is None:
|
||||
embeds = []
|
||||
embeds = [embed.to_dict()] if embed is not None else []
|
||||
if files is None:
|
||||
files = [file] if file is not None else []
|
||||
|
||||
content = str(content) if content is not None else None
|
||||
|
||||
if embed is not None:
|
||||
embeds.append(embed.to_dict())
|
||||
payload = {"content": str(content) if content is not None else None}
|
||||
|
||||
for _embed in embeds:
|
||||
if embed.total_length() > 6000:
|
||||
|
@ -554,10 +565,17 @@ class MessageableChannel(Channel):
|
|||
"Embed", embed.total_length(), 6000
|
||||
)
|
||||
|
||||
payload["embeds"] = embeds
|
||||
|
||||
print(create_form(payload, files))
|
||||
|
||||
content_type, data = create_form(payload, files)
|
||||
|
||||
return Message.from_dict(
|
||||
await self._http.post(
|
||||
f"/channels/{self.id}/messages",
|
||||
data={"content": content, "embeds": embeds},
|
||||
data=data,
|
||||
headers={"Content-Type": content_type}
|
||||
)
|
||||
)
|
||||
|
||||
|
|
94
melisa/models/message/file.py
Normal file
94
melisa/models/message/file.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
# Copyright MelisaDev 2022 - Present
|
||||
# Full MIT License can be found in `LICENSE.txt` at the project root.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import os
|
||||
from typing import Union, Dict, Any, List, Tuple
|
||||
|
||||
from aiohttp import FormData, Payload
|
||||
|
||||
import melisa.utils.json as json
|
||||
|
||||
|
||||
def create_form(payload: Dict[str, Any], files: List[File]):
|
||||
"""
|
||||
Creates an aiohttp payload from an array of File objects.
|
||||
"""
|
||||
form = FormData()
|
||||
form.add_field("payload_json", json.dumps(payload))
|
||||
|
||||
for index, file in enumerate(files):
|
||||
form.add_field(
|
||||
"file",
|
||||
file.filepath,
|
||||
filename=file.filename,
|
||||
content_type="application/octet-stream",
|
||||
)
|
||||
|
||||
payload = form()
|
||||
return payload.headers['Content-Type'], payload
|
||||
|
||||
|
||||
class File:
|
||||
"""
|
||||
A parameter object used for sending file objects.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
filepath: Union[:class:`os.PathLike`, :class:`io.BufferedIOBase`]
|
||||
A file-like object opened in binary mode and read mode
|
||||
or a filename representing a file in the hard drive to
|
||||
open.
|
||||
|
||||
.. note::
|
||||
If the file-like object passed is opened via ``open`` then the
|
||||
modes 'rb' should be used.
|
||||
To pass binary data, consider usage of ``io.BytesIO``.
|
||||
filename: Optional[:class:`str`]
|
||||
The filename to display when uploading to Discord.
|
||||
If this is not given then it defaults to ``fp.name`` or if ``filepath`` is
|
||||
a string then the ``filename`` will default to the string given.
|
||||
spoiler: :class:`bool`
|
||||
Whether the attachment is a spoiler.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
filepath: Union[str, bytes, os.PathLike, io.BufferedIOBase],
|
||||
*,
|
||||
filename: str = None,
|
||||
spoiler: bool = False,
|
||||
):
|
||||
# Some features are from the discord.py lib, thanks discord.py devs.
|
||||
if isinstance(filepath, io.IOBase):
|
||||
if not (filepath.seekable() and filepath.readable()):
|
||||
raise ValueError(
|
||||
f"File buffer {filepath!r} must be seekable and readable"
|
||||
)
|
||||
|
||||
self.filepath = filepath
|
||||
self._owner = False
|
||||
else:
|
||||
self.filepath = open(filepath, "rb")
|
||||
self._owner = True
|
||||
|
||||
if filename is None:
|
||||
if isinstance(filepath, str):
|
||||
_, self.filename = os.path.split(filepath)
|
||||
else:
|
||||
self.filename = getattr(filepath, "name", None)
|
||||
else:
|
||||
self.filename = filename
|
||||
|
||||
self.spoiler = spoiler or (
|
||||
self.filename is not None and self.filename.startswith("SPOILER_")
|
||||
)
|
||||
|
||||
if self.spoiler:
|
||||
self.filename = "SPOILER_" + filename
|
||||
|
||||
def close(self):
|
||||
if self._owner:
|
||||
self.filepath.close()
|
Loading…
Reference in a new issue