Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 79 additions & 5 deletions interactions/api/http/interaction.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from typing import TYPE_CHECKING, List, Optional, Union

from aiohttp import MultipartWriter

from ..models import Snowflake
from ..models.message import File
from .request import _Request
from .route import Route

Expand Down Expand Up @@ -216,17 +219,41 @@ async def get_all_application_command_permissions(
)

async def create_interaction_response(
self, token: str, application_id: int, data: dict
self,
token: str,
application_id: int,
data: dict,
files: Optional[List[File]] = None,
) -> None:
"""
Posts initial response to an interaction, but you need to add the token.

:param token: Token.
:param application_id: Application ID snowflake
:param data: The data to send.
:param files: The files to send.
"""

file_data = None
if files:

file_data = MultipartWriter("form-data")
part = file_data.append_json(data)
part.set_content_disposition("form-data", name="payload_json")
data = None

for id, file in enumerate(files):
part = file_data.append(
file._fp,
)
part.set_content_disposition(
"form-data", name=f"files[{str(id)}]", filename=file._filename
)

return await self._req.request(
Route("POST", f"/interactions/{application_id}/{token}/callback"), json=data
Route("POST", f"/interactions/{application_id}/{token}/callback"),
json=data,
data=file_data,
)

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

async def edit_interaction_response(
self, data: dict, token: str, application_id: str, message_id: str = "@original"
self,
data: dict,
token: str,
application_id: str,
files: Optional[List[File]] = None,
message_id: str = "@original",
) -> dict:
"""
Edits an existing interaction message, but token needs to be manually called.

:param data: A dictionary containing the new response.
:param token: the token of the interaction
:param application_id: Application ID snowflake.
:param files: The files to send.
:param message_id: Message ID snowflake. Defaults to `@original` which represents the initial response msg.
:return: Updated message data.
"""
# ^ again, I don't know if python will let me
file_data = None
if files is not None:

file_data = MultipartWriter("form-data")
part = file_data.append_json(data)
part.set_content_disposition("form-data", name="payload_json")
data = None

for id, file in enumerate(files):
part = file_data.append(
file._fp,
)
part.set_content_disposition(
"form-data", name=f"files[{id}]", filename=file._filename
)

return await self._req.request(
Route("PATCH", f"/webhooks/{application_id}/{token}/messages/{message_id}"),
json=data,
data=file_data,
)

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

async def _post_followup(self, data: dict, token: str, application_id: str) -> dict:
async def _post_followup(
self,
data: dict,
token: str,
application_id: str,
files: Optional[List[File]] = None,
) -> dict:
"""
Send a followup to an interaction.

:param data: the payload to send
:param application_id: the id of the application
:param token: the token of the interaction
:param files: the files to send
"""

file_data = None
if files is not None:
file_data = MultipartWriter("form-data")
part = file_data.append_json(data)
part.set_content_disposition("form-data", name="payload_json")
data = None

for id, file in enumerate(files):
part = file_data.append(
file._fp,
)
part.set_content_disposition(
"form-data", name=f"files[{id}]", filename=file._filename
)

return await self._req.request(
Route("POST", f"/webhooks/{application_id}/{token}"), json=data
Route("POST", f"/webhooks/{application_id}/{token}"),
json=data,
data=file_data,
)
72 changes: 55 additions & 17 deletions interactions/client/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from ..api.models.flags import Permissions
from ..api.models.guild import Guild
from ..api.models.member import Member
from ..api.models.message import Attachment, Embed, Message, MessageReference
from ..api.models.message import Attachment, Embed, File, Message, MessageReference
from ..api.models.misc import AllowedMentions, Snowflake
from ..api.models.user import User
from ..base import get_logger
Expand Down Expand Up @@ -112,6 +112,7 @@ async def send(
*,
tts: Optional[bool] = MISSING,
attachments: Optional[List[Attachment]] = MISSING,
files: Optional[Union[File, List[File]]] = MISSING,
embeds: Optional[Union[Embed, List[Embed]]] = MISSING,
allowed_mentions: Optional[Union[AllowedMentions, dict]] = MISSING,
components: Optional[
Expand All @@ -127,6 +128,7 @@ async def send(
:param Optional[str] content: The contents of the message as a string or string-converted value.
:param Optional[bool] tts: Whether the message utilizes the text-to-speech Discord programme or not.
:param Optional[List[Attachment]] attachments: The attachments to attach to the message. Needs to be uploaded to the CDN first
:param Optional[Union[File, List[File]]] files: The files to attach to the message.
:param Optional[Union[Embed, List[Embed]]] embeds: An embed, or list of embeds for the message.
:param Optional[Union[AllowedMentions, dict]] allowed_mentions: The allowed mentions for the message.
:param Optional[Union[ActionRow, Button, SelectMenu, List[Union[ActionRow, Button, SelectMenu]]]] components: A component, or list of components for the message.
Expand Down Expand Up @@ -188,14 +190,27 @@ async def send(

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

return dict(
content=_content,
tts=_tts,
embeds=_embeds,
allowed_mentions=_allowed_mentions,
components=_components,
attachments=_attachments,
flags=_flags,
if not files or files is MISSING:
_files = []
elif isinstance(files, list):
_files = [file._json_payload(id) for id, file in enumerate(files)]
else:
_files = [files._json_payload(0)]
files = [files]

_files.extend(_attachments)

return (
dict(
content=_content,
tts=_tts,
embeds=_embeds,
allowed_mentions=_allowed_mentions,
components=_components,
attachments=_files,
flags=_flags,
),
files,
)

async def edit(
Expand All @@ -204,6 +219,7 @@ async def edit(
*,
tts: Optional[bool] = MISSING,
attachments: Optional[List[Attachment]] = MISSING,
files: Optional[Union[File, List[File]]] = MISSING,
embeds: Optional[Union[Embed, List[Embed]]] = MISSING,
allowed_mentions: Optional[Union[AllowedMentions, dict]] = MISSING,
message_reference: Optional[MessageReference] = MISSING,
Expand All @@ -225,6 +241,7 @@ async def edit(
if self.message.content is not None or content is not MISSING:
_content: str = self.message.content if content is MISSING else content
payload["content"] = _content

_tts: bool = False if tts is MISSING else tts
payload["tts"] = _tts

Expand Down Expand Up @@ -252,7 +269,17 @@ async def edit(
else []
)

payload["attachments"] = _attachments
if not files or files is MISSING:
_files = []
elif isinstance(files, list):
_files = [file._json_payload(id) for id, file in enumerate(files)]
else:
_files = [files._json_payload(0)]
files = [files]

_files.extend(_attachments)

payload["attachments"] = _files

_allowed_mentions: dict = (
{}
Expand All @@ -276,7 +303,7 @@ async def edit(

payload["components"] = _components

return payload
return payload, files

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

payload = await super().edit(content, **kwargs)
payload, files = await super().edit(content, **kwargs)
msg = None

if self.deferred:
Expand All @@ -385,7 +412,7 @@ async def edit(
):
try:
res = await self._client.edit_message(
int(self.channel_id), int(self.message.id), payload=payload
int(self.channel_id), int(self.message.id), payload=payload, files=files
)
except LibraryException as e:
if e.code in {10015, 10018}:
Expand All @@ -401,6 +428,7 @@ async def edit(
token=self.token,
application_id=str(self.id),
data=payload,
files=files,
message_id=self.message.id
if self.message and self.message.flags != 64
else "@original",
Expand All @@ -416,7 +444,10 @@ async def edit(
else:
try:
res = await self._client.edit_interaction_response(
token=self.token, application_id=str(self.application_id), data=payload
token=self.token,
application_id=str(self.application_id),
data=payload,
files=files,
)
except LibraryException as e:
if e.code in {10015, 10018}:
Expand Down Expand Up @@ -450,7 +481,7 @@ async def defer(self, ephemeral: Optional[bool] = False) -> None:
self.responded = True

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

if not self.deferred:
self.callback = InteractionCallbackType.CHANNEL_MESSAGE_WITH_SOURCE
Expand All @@ -461,6 +492,7 @@ async def send(self, content: Optional[str] = MISSING, **kwargs) -> Message:
if self.responded:
res = await self._client._post_followup(
data=payload,
files=files,
token=self.token,
application_id=str(self.application_id),
)
Expand All @@ -470,6 +502,7 @@ async def send(self, content: Optional[str] = MISSING, **kwargs) -> Message:
token=self.token,
application_id=int(self.id),
data=_payload,
files=files,
)

try:
Expand Down Expand Up @@ -571,13 +604,14 @@ class ComponentContext(_Context):

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

payload = await super().edit(content, **kwargs)
payload, files = await super().edit(content, **kwargs)
msg = None

if not self.deferred:
self.callback = InteractionCallbackType.UPDATE_MESSAGE
await self._client.create_interaction_response(
data={"type": self.callback.value, "data": payload},
files=files,
token=self.token,
application_id=int(self.id),
)
Expand All @@ -595,12 +629,14 @@ async def edit(self, content: Optional[str] = MISSING, **kwargs) -> Message:
elif self.callback != InteractionCallbackType.DEFERRED_UPDATE_MESSAGE:
await self._client._post_followup(
data=payload,
files=files,
token=self.token,
application_id=str(self.application_id),
)
else:
res = await self._client.edit_interaction_response(
data=payload,
files=files,
token=self.token,
application_id=str(self.application_id),
)
Expand All @@ -610,7 +646,7 @@ async def edit(self, content: Optional[str] = MISSING, **kwargs) -> Message:
return msg if msg is not None else Message(**payload, _client=self._client)

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

if not self.deferred:
self.callback = InteractionCallbackType.CHANNEL_MESSAGE_WITH_SOURCE
Expand All @@ -621,6 +657,7 @@ async def send(self, content: Optional[str] = MISSING, **kwargs) -> Message:
if self.responded:
res = await self._client._post_followup(
data=payload,
files=files,
token=self.token,
application_id=str(self.application_id),
)
Expand All @@ -630,6 +667,7 @@ async def send(self, content: Optional[str] = MISSING, **kwargs) -> Message:
token=self.token,
application_id=int(self.id),
data=_payload,
files=files,
)

try:
Expand Down