Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions interactions/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ def get_guild_websocket(self, id: "Snowflake_Type") -> GatewayClient:
def _sanity_check(self) -> None:
"""Checks for possible and common errors in the bot's configuration."""
self.logger.debug("Running client sanity checks...")

contexts = {
self.interaction_context: InteractionContext,
self.component_context: ComponentContext,
Expand Down Expand Up @@ -911,6 +912,11 @@ async def login(self, token: str | None = None) -> None:
# so im gathering commands here
self._gather_callbacks()

if any(v for v in constants.CLIENT_FEATURE_FLAGS.values()):
# list all enabled flags
enabled_flags = [k for k, v in constants.CLIENT_FEATURE_FLAGS.items() if v]
self.logger.info(f"Enabled feature flags: {', '.join(enabled_flags)}")

self.logger.debug("Attempting to login")
me = await self.http.login(self.token)
self._user = ClientUser.from_dict(me, self)
Expand Down
17 changes: 17 additions & 0 deletions interactions/client/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
T TypeVar: A type variable used for generic typing.
Absent Union[T, Missing]: A type hint for a value that may be MISSING.

CLIENT_FEATURE_FLAGS dict: A dict of feature flags that can be enabled or disabled for the client.
has_feature_flag Callable[[str], bool]: A function that returns whether a feature flag is enabled.

"""
import inspect
import logging
Expand Down Expand Up @@ -78,6 +81,8 @@
"LIB_PATH",
"RECOVERABLE_WEBSOCKET_CLOSE_CODES",
"NON_RESUMABLE_WEBSOCKET_CLOSE_CODES",
"CLIENT_FEATURE_FLAGS",
"has_client_feature",
)

_ver_info = sys.version_info
Expand Down Expand Up @@ -195,6 +200,18 @@ class MentionPrefix(Sentinel):
},
)

CLIENT_FEATURE_FLAGS = {
"FOLLOWUP_INTERACTIONS_FOR_IMAGES": False, # Experimental fix to bypass Discord's broken image proxy
}


def has_client_feature(feature: str) -> bool:
"""Checks if a feature is enabled for the client."""
if feature.upper() not in CLIENT_FEATURE_FLAGS:
get_logger().warning(f"Unknown feature {feature!r} - Known features: {list(CLIENT_FEATURE_FLAGS)}")
return False
return CLIENT_FEATURE_FLAGS[feature.upper()]


GUILD_WELCOME_MESSAGES = (
"{0} joined the party.",
Expand Down
21 changes: 18 additions & 3 deletions interactions/models/internal/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import discord_typings
from aiohttp import FormData

from interactions.client import const
from interactions.client.const import get_logger, MISSING
from interactions.models.discord.components import BaseComponent
from interactions.models.discord.file import UPLOADABLE_TYPE
Expand Down Expand Up @@ -401,6 +403,14 @@ async def defer(self, *, ephemeral: bool = False) -> None:
async def _send_http_request(
self, message_payload: dict, files: typing.Iterable["UPLOADABLE_TYPE"] | None = None
) -> dict:
if const.has_client_feature("FOLLOWUP_INTERACTIONS_FOR_IMAGES") and not self.deferred:
# experimental bypass for discords broken image proxy
if embeds := message_payload.get("embeds", {}):
if any(e.get("image") for e in embeds):
if MessageFlags.EPHEMERAL in message_payload.get("flags", MessageFlags.NONE):
self.ephemeral = True
await self.defer(ephemeral=self.ephemeral)

if self.responded:
message_data = await self.client.http.post_followup(
message_payload, self.client.app.id, self.token, files=files
Expand All @@ -409,9 +419,14 @@ async def _send_http_request(
if isinstance(message_payload, FormData) and not self.deferred:
await self.defer(ephemeral=self.ephemeral)
if self.deferred:
message_data = await self.client.http.edit_interaction_message(
message_payload, self.client.app.id, self.token, files=files
)
if const.has_client_feature("FOLLOWUP_INTERACTIONS_FOR_IMAGES"):
message_data = await self.client.http.post_followup(
message_payload, self.client.app.id, self.token, files=files
)
else:
message_data = await self.client.http.edit_interaction_message(
message_payload, self.client.app.id, self.token, files=files
)
else:
payload = {
"type": CallbackType.CHANNEL_MESSAGE_WITH_SOURCE,
Expand Down