from typing import TYPE_CHECKING, Union, Any, Optional
from . import utils
from .embeds import Embed
from .enums import ResponseType
from .file import File
from .flag import MessageFlags
from .mentions import AllowedMentions
from .multipart import MultipartData
from .object import Snowflake
from .view import View, Modal
if TYPE_CHECKING:
from .http import DiscordAPI
from .message import MessageReference, Poll
from .user import PartialUser, User
MISSING = utils.MISSING
__all__ = (
"AutocompleteResponse",
"DeferResponse",
"MessageResponse",
"Ping",
)
[docs]
class Ping(Snowflake):
def __init__(
self,
*,
state: "DiscordAPI",
data: dict
):
super().__init__(id=int(data["id"]))
self._state = state
self._raw_user = data["user"]
self.application_id: int = int(data["application_id"])
self.version: int = int(data["version"])
def __repr__(self) -> str:
return f"<Ping application={self.application} user='{self.user}'>"
@property
def application(self) -> "PartialUser":
""" `User`: Returns the user object of the bot """
from .user import PartialUser
return PartialUser(state=self._state, id=self.application_id)
@property
def user(self) -> "User":
""" `User`: Returns the user object of the bot """
from .user import User
return User(state=self._state, data=self._raw_user)
class BaseResponse:
def __init__(self):
pass
@property
def content_type(self) -> str:
""" `str`: Returns the content type of the response """
multidata = MultipartData()
return multidata.content_type
def to_dict(self) -> dict:
""" Default method to convert the response to a `dict` """
raise NotImplementedError
def to_multipart(self) -> bytes:
""" Default method to convert the response to a `bytes` """
raise NotImplementedError
[docs]
class DeferResponse(BaseResponse):
def __init__(
self,
*,
ephemeral: bool = False,
thinking: bool = False
):
self.ephemeral = ephemeral
self.thinking = thinking
[docs]
def to_dict(self) -> dict:
""" `dict`: Returns the response as a `dict` """
return {
"type": (
int(ResponseType.deferred_channel_message_with_source)
if self.thinking else int(ResponseType.deferred_update_message)
),
"data": {
"flags": (
MessageFlags.ephemeral.value
if self.ephemeral else 0
)
}
}
[docs]
def to_multipart(self) -> bytes:
""" `bytes`: Returns the response as a `bytes` """
multidata = MultipartData()
multidata.attach("payload_json", self.to_dict())
return multidata.finish()
[docs]
class AutocompleteResponse(BaseResponse):
def __init__(
self,
choices: dict[Any, str]
):
self.choices = choices
[docs]
def to_dict(self) -> dict:
""" `dict`: Returns the response as a `dict` """
return {
"type": int(ResponseType.application_command_autocomplete_result),
"data": {
"choices": [
{"name": value, "value": key}
for key, value in self.choices.items()
][:25] # Discord only allows 25 choices, so we limit it
}
}
[docs]
def to_multipart(self) -> bytes:
""" `bytes`: Returns the response as a `bytes` """
multidata = MultipartData()
multidata.attach("payload_json", self.to_dict())
return multidata.finish()
class ModalResponse(BaseResponse):
def __init__(self, modal: Modal):
self.modal = modal
def to_dict(self) -> dict:
""" `dict`: Returns the response as a `dict` """
return {
"type": int(ResponseType.modal),
"data": self.modal.to_dict()
}
def to_multipart(self) -> bytes:
""" `bytes`: Returns the response as a `bytes` """
multidata = MultipartData()
multidata.attach("payload_json", self.to_dict())
return multidata.finish()
[docs]
class MessageResponse(BaseResponse):
def __init__(
self,
content: Optional[str] = MISSING,
*,
file: Optional[File] = MISSING,
files: Optional[list[File]] = MISSING,
embed: Optional[Embed] = MISSING,
embeds: Optional[list[Embed]] = MISSING,
attachment: Optional[File] = MISSING,
attachments: Optional[list[File]] = MISSING,
view: Optional[View] = MISSING,
tts: Optional[bool] = False,
allowed_mentions: Optional[AllowedMentions] = MISSING,
message_reference: Optional["MessageReference"] = MISSING,
poll: Optional["Poll"] = MISSING,
type: Union[ResponseType, int] = 4,
ephemeral: Optional[bool] = False,
):
self.content = content
self.files = files
self.embeds = embeds
self.attachments = attachments
self.ephemeral = ephemeral
self.view = view
self.tts = tts
self.type = type
self.allowed_mentions = allowed_mentions
self.message_reference = message_reference
self.poll = poll
if file is not MISSING and files is not MISSING:
raise TypeError("Cannot pass both file and files")
if file is not MISSING:
self.files = [file]
if embed is not MISSING and embeds is not MISSING:
raise TypeError("Cannot pass both embed and embeds")
if embed is not MISSING:
if embed is None:
self.embeds = []
else:
self.embeds = [embed]
if attachment is not MISSING and attachments is not MISSING:
raise TypeError("Cannot pass both attachment and attachments")
if attachment is not MISSING:
if attachment is None:
self.attachments = []
else:
self.attachments = [attachment]
if self.view is not MISSING and self.view is None:
self.view = View()
if self.attachments is not MISSING:
self.files = (
[a for a in self.attachments if isinstance(a, File)]
if self.attachments is not None else None
)
[docs]
def to_dict(self, is_request: bool = False) -> dict:
"""
The JSON data that is sent to Discord.
Parameters
----------
is_request: `bool`
Whether the data is being sent to Discord or not.
Returns
-------
`dict`
The JSON data that can either be sent
to Discord or forwarded to a new parser
"""
output: dict[str, Any] = {
"flags": (
MessageFlags.ephemeral.value
if self.ephemeral else 0
)
}
if self.content is not MISSING:
output["content"] = self.content
if self.tts:
output["tts"] = self.tts
if self.message_reference is not MISSING:
output["message_reference"] = self.message_reference.to_dict()
if self.embeds is not MISSING:
output["embeds"] = [
embed.to_dict() for embed in self.embeds # type: ignore
if isinstance(embed, Embed)
]
if self.poll is not MISSING:
output["poll"] = self.poll.to_dict()
if self.view is not MISSING:
output["components"] = self.view.to_dict()
if self.allowed_mentions is not MISSING:
output["allowed_mentions"] = self.allowed_mentions.to_dict()
if self.attachments is not MISSING:
if self.attachments is None:
output["attachments"] = []
else:
_index = 0
_file_payload = []
for a in self.attachments:
if not isinstance(a, File):
continue
_file_payload.append(a.to_dict(_index))
_index += 1
output["attachments"] = _file_payload
if is_request:
return output
return {"type": int(self.type), "data": output}
[docs]
def to_multipart(self, is_request: bool = False) -> bytes:
"""
The multipart data that is sent to Discord.
Parameters
----------
is_request: `bool`
Whether the data is being sent to Discord or not.
Returns
-------
`bytes`
The multipart data that can either be sent
"""
multidata = MultipartData()
if isinstance(self.files, list):
for i, file in enumerate(self.files):
multidata.attach(
f"files[{i}]",
file, # type: ignore
filename=file.filename
)
multidata.attach(
"payload_json",
self.to_dict(is_request=is_request)
)
return multidata.finish()