Skip to content

Commit dc4c623

Browse files
authored
feat: bypass discord's broken image proxy (#1414)
* feat: bypass discord's broken image proxy * fix: minor tweaks * fix: don't raise on unknown flag
1 parent def9017 commit dc4c623

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

interactions/client/client.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,7 @@ def get_guild_websocket(self, id: "Snowflake_Type") -> GatewayClient:
544544
def _sanity_check(self) -> None:
545545
"""Checks for possible and common errors in the bot's configuration."""
546546
self.logger.debug("Running client sanity checks...")
547+
547548
contexts = {
548549
self.interaction_context: InteractionContext,
549550
self.component_context: ComponentContext,
@@ -911,6 +912,11 @@ async def login(self, token: str | None = None) -> None:
911912
# so im gathering commands here
912913
self._gather_callbacks()
913914

915+
if any(v for v in constants.CLIENT_FEATURE_FLAGS.values()):
916+
# list all enabled flags
917+
enabled_flags = [k for k, v in constants.CLIENT_FEATURE_FLAGS.items() if v]
918+
self.logger.info(f"Enabled feature flags: {', '.join(enabled_flags)}")
919+
914920
self.logger.debug("Attempting to login")
915921
me = await self.http.login(self.token)
916922
self._user = ClientUser.from_dict(me, self)

interactions/client/const.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
T TypeVar: A type variable used for generic typing.
3333
Absent Union[T, Missing]: A type hint for a value that may be MISSING.
3434
35+
CLIENT_FEATURE_FLAGS dict: A dict of feature flags that can be enabled or disabled for the client.
36+
has_feature_flag Callable[[str], bool]: A function that returns whether a feature flag is enabled.
37+
3538
"""
3639
import inspect
3740
import logging
@@ -78,6 +81,8 @@
7881
"LIB_PATH",
7982
"RECOVERABLE_WEBSOCKET_CLOSE_CODES",
8083
"NON_RESUMABLE_WEBSOCKET_CLOSE_CODES",
84+
"CLIENT_FEATURE_FLAGS",
85+
"has_client_feature",
8186
)
8287

8388
_ver_info = sys.version_info
@@ -195,6 +200,18 @@ class MentionPrefix(Sentinel):
195200
},
196201
)
197202

203+
CLIENT_FEATURE_FLAGS = {
204+
"FOLLOWUP_INTERACTIONS_FOR_IMAGES": False, # Experimental fix to bypass Discord's broken image proxy
205+
}
206+
207+
208+
def has_client_feature(feature: str) -> bool:
209+
"""Checks if a feature is enabled for the client."""
210+
if feature.upper() not in CLIENT_FEATURE_FLAGS:
211+
get_logger().warning(f"Unknown feature {feature!r} - Known features: {list(CLIENT_FEATURE_FLAGS)}")
212+
return False
213+
return CLIENT_FEATURE_FLAGS[feature.upper()]
214+
198215

199216
GUILD_WELCOME_MESSAGES = (
200217
"{0} joined the party.",

interactions/models/internal/context.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
import discord_typings
88
from aiohttp import FormData
9+
10+
from interactions.client import const
911
from interactions.client.const import get_logger, MISSING
1012
from interactions.models.discord.components import BaseComponent
1113
from interactions.models.discord.file import UPLOADABLE_TYPE
@@ -401,6 +403,14 @@ async def defer(self, *, ephemeral: bool = False) -> None:
401403
async def _send_http_request(
402404
self, message_payload: dict, files: typing.Iterable["UPLOADABLE_TYPE"] | None = None
403405
) -> dict:
406+
if const.has_client_feature("FOLLOWUP_INTERACTIONS_FOR_IMAGES") and not self.deferred:
407+
# experimental bypass for discords broken image proxy
408+
if embeds := message_payload.get("embeds", {}):
409+
if any(e.get("image") for e in embeds):
410+
if MessageFlags.EPHEMERAL in message_payload.get("flags", MessageFlags.NONE):
411+
self.ephemeral = True
412+
await self.defer(ephemeral=self.ephemeral)
413+
404414
if self.responded:
405415
message_data = await self.client.http.post_followup(
406416
message_payload, self.client.app.id, self.token, files=files
@@ -409,9 +419,14 @@ async def _send_http_request(
409419
if isinstance(message_payload, FormData) and not self.deferred:
410420
await self.defer(ephemeral=self.ephemeral)
411421
if self.deferred:
412-
message_data = await self.client.http.edit_interaction_message(
413-
message_payload, self.client.app.id, self.token, files=files
414-
)
422+
if const.has_client_feature("FOLLOWUP_INTERACTIONS_FOR_IMAGES"):
423+
message_data = await self.client.http.post_followup(
424+
message_payload, self.client.app.id, self.token, files=files
425+
)
426+
else:
427+
message_data = await self.client.http.edit_interaction_message(
428+
message_payload, self.client.app.id, self.token, files=files
429+
)
415430
else:
416431
payload = {
417432
"type": CallbackType.CHANNEL_MESSAGE_WITH_SOURCE,

0 commit comments

Comments
 (0)