Twitter migrated SearchTimeline from GET to POST; update fetch_search
to route through _graphql_post via a new use_post flag on _fetch_timeline,
avoiding duplicating the pagination logic (closes#39, refs #40, #42).
Also guard against get_ondemand_file_url returning None so the error
message is clear instead of crashing on NoneType.split (closes #43-adjacent).
Refresh all FALLBACK_QUERY_IDS from live JS bundles — 18 operations updated.
Add `twitter bookmarks folders` command to list bookmark folders
and `twitter bookmarks folders <id>` to fetch tweets from a specific
folder, with --since date filtering and pagination support.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Twitter now returns HTTP 422 GRAPHQL_VALIDATION_FAILED (not just 404)
when a queryId goes stale. Updated fallback IDs and added 422 to the
stale-queryId retry logic in both _graphql_get and _graphql_post.
Split the monolithic client.py (1341 lines) into three focused modules:
- graphql.py (~200 lines): queryId resolution, URL building, JS bundle
scanning, feature flag management
- parser.py (~270 lines): Tweet/User/Media/Article parsing, utility functions
(_deep_get, _parse_int, _extract_cursor, _extract_media)
- client.py (~700 lines): TwitterClient class with HTTP engine, anti-detection,
session management, and all public API methods
Backward compatibility: client.py re-exports all previously public symbols
so existing test imports work without modification. 88/88 tests pass.