diff --git a/interactions/client/client.py b/interactions/client/client.py index ae3371d10..f1aabd9a4 100644 --- a/interactions/client/client.py +++ b/interactions/client/client.py @@ -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, @@ -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) diff --git a/interactions/client/const.py b/interactions/client/const.py index db562f5f7..1d0ced829 100644 --- a/interactions/client/const.py +++ b/interactions/client/const.py @@ -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 @@ -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 @@ -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.", diff --git a/interactions/models/internal/context.py b/interactions/models/internal/context.py index 0ec20c5a8..22606526a 100644 --- a/interactions/models/internal/context.py +++ b/interactions/models/internal/context.py @@ -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 @@ -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 @@ -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,