fix: 431 Request Too Large — reduce FEATURES to 15 essential keys, dynamic update only updates existing keys

This commit is contained in:
jackwener
2026-03-09 20:59:16 +08:00
parent d20c5699fd
commit fda9b1c3dc
4 changed files with 25 additions and 46 deletions

View File

@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "twitter-cli"
version = "0.4.2"
version = "0.4.3"
description = "A CLI for Twitter/X — feed, bookmarks, and user timeline in terminal"
readme = "README.md"
license = "Apache-2.0"

View File

@@ -213,30 +213,28 @@ class TestBestChromeTarget:
# ── _update_features_from_html ───────────────────────────────────────────
class TestUpdateFeaturesFromHtml:
def test_extracts_feature_flags(self):
# Save original state
def test_updates_existing_feature_flags(self):
"""Should update existing FEATURES keys, not add new ones."""
original = dict(FEATURES)
try:
html = '''
"responsive_web_test_feature":{"value":true},
"responsive_web_another_feature":{"value":false},
"rweb_some_flag":{"value":true}
'''
# Use a key that exists in FEATURES
existing_key = list(FEATURES.keys())[0]
original_value = FEATURES[existing_key]
opposite = "false" if original_value else "true"
html = '"%s":{"value":%s}' % (existing_key, opposite)
_update_features_from_html(html)
assert FEATURES["responsive_web_test_feature"] is True
assert FEATURES["responsive_web_another_feature"] is False
assert FEATURES["rweb_some_flag"] is True
assert FEATURES[existing_key] != original_value
finally:
# Restore original state
FEATURES.clear()
FEATURES.update(original)
def test_ignores_non_feature_keys(self):
def test_does_not_add_new_keys(self):
"""Should never add keys not already in FEATURES (prevents URL bloat)."""
original = dict(FEATURES)
try:
html = '"some_random_key":{"value":true}'
html = '"responsive_web_brand_new_feature":{"value":true}'
_update_features_from_html(html)
assert "some_random_key" not in FEATURES
assert "responsive_web_brand_new_feature" not in FEATURES
finally:
FEATURES.clear()
FEATURES.update(original)

View File

@@ -53,43 +53,24 @@ TWITTER_OPENAPI_URL = (
"main/src/config/placeholder.json"
)
# Essential features only — keep this list SMALL to avoid 414/431 URI Too Long.
# Twitter's API defaults missing features to False, so we only need True-valued ones
# that affect tweet data we actually consume. Each additional key adds ~60 chars to URL.
_DEFAULT_FEATURES = {
"rweb_video_screen_enabled": False,
"profile_label_improvements_pcf_label_in_post_enabled": True,
"responsive_web_profile_redirect_enabled": False,
"rweb_tipjar_consumption_enabled": False,
"verified_phone_label_enabled": False,
"creator_subscriptions_tweet_preview_api_enabled": True,
"responsive_web_graphql_timeline_navigation_enabled": True,
"responsive_web_graphql_skip_user_profile_image_extensions_enabled": False,
"premium_content_api_read_enabled": False,
"communities_web_enable_tweet_community_results_fetch": True,
"c9s_tweet_anatomy_moderator_badge_enabled": True,
"responsive_web_grok_analyze_button_fetch_trends_enabled": False,
"responsive_web_grok_analyze_post_followups_enabled": True,
"responsive_web_jetfuel_frame": True,
"responsive_web_grok_share_attachment_enabled": True,
"responsive_web_grok_annotations_enabled": True,
"articles_preview_enabled": True,
"responsive_web_edit_tweet_api_enabled": True,
"graphql_is_translatable_rweb_tweet_is_translatable_enabled": True,
"view_counts_everywhere_api_enabled": True,
"longform_notetweets_consumption_enabled": True,
"responsive_web_twitter_article_tweet_consumption_enabled": True,
"tweet_awards_web_tipping_enabled": False,
"content_disclosure_indicator_enabled": True,
"content_disclosure_ai_generated_indicator_enabled": True,
"responsive_web_grok_show_grok_translated_post": True,
"responsive_web_grok_analysis_button_from_backend": True,
"post_ctas_fetch_enabled": True,
"freedom_of_speech_not_reach_fetch_enabled": True,
"standardized_nudges_misinfo": True,
"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": True,
"longform_notetweets_rich_text_read_enabled": True,
"longform_notetweets_inline_media_enabled": False,
"responsive_web_grok_image_annotation_enabled": True,
"responsive_web_grok_imagine_annotation_enabled": True,
"responsive_web_grok_community_note_auto_translation_is_enabled": False,
"freedom_of_speech_not_reach_fetch_enabled": True,
"standardized_nudges_misinfo": True,
"responsive_web_graphql_timeline_navigation_enabled": True,
"responsive_web_enhance_cards_enabled": False,
}
@@ -226,11 +207,9 @@ def _update_features_from_html(html):
Twitter embeds feature switch config in inline scripts on the homepage.
We parse these to keep FEATURES in sync with the current frontend.
Only UPDATES existing keys — never adds new ones to avoid URL bloat.
"""
try:
# Look for feature flags in inline script content
# Pattern: "featureSwitch":{"...":{"value":true/false},...}
# Also try: features:{key:!0, key2:!1, ...} in JS bundles
feature_pattern = re.compile(
r'"([a-z][a-z0-9_]+)":\s*\{\s*"value"\s*:\s*(true|false)',
re.IGNORECASE,
@@ -239,8 +218,10 @@ def _update_features_from_html(html):
for match in feature_pattern.finditer(html):
key = match.group(1)
value = match.group(2).lower() == "true"
# Only update keys that look like feature flags
if any(prefix in key for prefix in ("responsive_web_", "rweb_", "longform_", "creator_", "communities_", "c9s_")):
# Only update keys already in FEATURES — never add new ones
# Adding new keys inflates URL length, causing 414/431 errors
if key in FEATURES and FEATURES[key] != value:
logger.debug("Feature flag updated: %s = %s -> %s", key, FEATURES[key], value)
FEATURES[key] = value
found += 1
if found:

2
uv.lock generated
View File

@@ -950,7 +950,7 @@ wheels = [
[[package]]
name = "twitter-cli"
version = "0.4.1"
version = "0.4.2"
source = { editable = "." }
dependencies = [
{ name = "beautifulsoup4" },