diff --git a/pyproject.toml b/pyproject.toml index 8f6832b..45b8eca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "twitter-cli" -version = "0.4.1" +version = "0.4.2" description = "A CLI for Twitter/X — feed, bookmarks, and user timeline in terminal" readme = "README.md" license = "Apache-2.0" diff --git a/tests/test_client.py b/tests/test_client.py index a707a37..d31ba38 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -176,6 +176,23 @@ class TestBuildGraphqlUrl: url = _build_graphql_url("x", "Op", {}, {}, {"toggle": True}) assert "fieldToggles=" in url + def test_false_features_omitted_from_url(self): + """False-valued features should be omitted to keep URL short (avoid 414).""" + features = {"enabled_flag": True, "disabled_flag": False, "another_enabled": True} + url = _build_graphql_url("q", "Op", {}, features) + assert "enabled_flag" in url + assert "another_enabled" in url + assert "disabled_flag" not in url + + def test_url_length_with_full_features(self): + """URL with full FEATURES dict should stay under 8000 chars (server limit).""" + url = _build_graphql_url( + "abc123", "SearchTimeline", + {"rawQuery": "AI agent", "querySource": "typed_query", "product": "Latest", "count": 50}, + FEATURES, + ) + assert len(url) < 8000, f"URL too long: {len(url)} chars" + # ── _best_chrome_target ────────────────────────────────────────────────── diff --git a/twitter_cli/client.py b/twitter_cli/client.py index 3cb9848..de10d0e 100644 --- a/twitter_cli/client.py +++ b/twitter_cli/client.py @@ -164,12 +164,18 @@ def _url_fetch(url, headers=None): def _build_graphql_url(query_id, operation_name, variables, features, field_toggles=None): # type: (str, str, Dict[str, Any], Dict[str, Any], Optional[Dict[str, Any]]) -> str - """Build GraphQL GET URL with encoded variables/features/fieldToggles.""" + """Build GraphQL GET URL with encoded variables/features/fieldToggles. + + Only includes True-valued feature flags in the URL to avoid 414 URI Too Long. + Twitter's API defaults missing features to False. + """ + # Compact features: omit False values to keep URL under server limits + compact_features = {k: v for k, v in features.items() if v is not False} url = "https://x.com/i/api/graphql/%s/%s?variables=%s&features=%s" % ( query_id, operation_name, urllib.parse.quote(json.dumps(variables, separators=(",", ":"))), - urllib.parse.quote(json.dumps(features, separators=(",", ":"))), + urllib.parse.quote(json.dumps(compact_features, separators=(",", ":"))), ) if field_toggles: url += "&fieldToggles=%s" % urllib.parse.quote( diff --git a/uv.lock b/uv.lock index 21eec6a..fe72ff6 100644 --- a/uv.lock +++ b/uv.lock @@ -950,7 +950,7 @@ wheels = [ [[package]] name = "twitter-cli" -version = "0.4.0" +version = "0.4.1" source = { editable = "." } dependencies = [ { name = "beautifulsoup4" },