Commit Graph

133 Commits

Author SHA1 Message Date
jackwener
a139b1052c chore: bump version to 0.6.4 2026-03-11 17:09:35 +08:00
jackwener
1de88ea2ed fix: update Likes queryId and response path (fixes #8)
- Update Likes fallback queryId: aeJWz7GtGNHHO2Z3GrjCWg -> dv5-II7_Bup_PHish7p6fw
- Fix response path: data.user.result.timeline.timeline.instructions
  (Twitter renamed timeline_v2 to timeline; code now tries both)
- Verified via Playwright: API returns 200 with correct tweet entries

Fixes #8
2026-03-11 17:08:14 +08:00
jackwener
60e1e7c580 feat: improve cookie extraction diagnostics and add doctor command
- Add _diagnose_keychain_issues() for macOS Keychain/SSH detection
- Extraction functions now return (cookies, diagnostics) tuples
- Error messages include actionable Keychain hints (e.g. unlock-keychain)
- Add 'twitter doctor' diagnostic command for troubleshooting
- Enhance bug_report.yml with browser/access method/diagnostics fields
- Expand README troubleshooting (EN+CN) with Keychain/SSH solutions
- Add 5 new tests for Keychain diagnostics

Closes #11
2026-03-11 16:53:06 +08:00
jackwener
47be88e62d fix: use importlib.metadata for __version__ (was hardcoded stale) 2026-03-11 14:11:07 +08:00
jackwener
e96d5142f2 chore: bump version to 0.6.2 2026-03-11 13:36:22 +08:00
jackwener
9f735cb96b chore: Update twitter-cli package version to 0.6.1 in uv.lock. 2026-03-11 13:29:31 +08:00
jackwener
d3441cf6ad chore: bump version to v0.6.1 2026-03-11 13:09:11 +08:00
jackwener
3a50be26af chore: add GitHub issue templates (bug report + feature request) 2026-03-11 13:04:21 +08:00
jackwener
6d6108436f chore: bump version to 0.6.0
Changes since 0.5.1:
- feat: Chrome multi-profile cookie extraction (#6)
- fix: warn on private likes (#8)
- fix: add logger definition (CI fix)
- refactor: specific ImportError handling (#10)
- docs: upgrade instructions in README and SKILL.md
2026-03-11 12:57:34 +08:00
jackwener
53a700ec60 feat: support Chrome multi-profile cookie extraction
Auto-iterates all Chrome/Arc/Edge/Brave profiles (Default, Profile 1,
Profile 2, ...) to find Twitter cookies. Falls back to the default
browser_cookie3 behavior when no profile dirs are found.

Set TWITTER_CHROME_PROFILE env var to specify a profile explicitly:
  TWITTER_CHROME_PROFILE='Profile 2' twitter feed

Closes #6
2026-03-11 12:53:25 +08:00
jackwener
84504b1477 docs: add upgrade instructions to README and SKILL.md 2026-03-11 12:43:51 +08:00
jackwener
93840ba99b fix: add logger definition to cli.py (fix CI lint F821) 2026-03-11 12:37:28 +08:00
aidiff-kobe
f01ce77f20 refactor: Use specific ImportError instead of broad Exception (#10)
Changed the exception handling in _best_chrome_target() to catch
specific ImportError instead of broad Exception. This improves
code clarity and avoids masking unexpected errors.

Also added a debug log message to help diagnose when curl_cffi
is not available.

Co-authored-by: Security Bot <agent@example.com>
2026-03-11 12:36:13 +08:00
jackwener
e9efa11fa5 fix: warn when querying other users' likes (private since Jun 2024)
Twitter/X made all likes private since June 2024. The likes command now:
- Detects if the target user differs from the authenticated user
- Shows a clear warning that only your own likes are visible
- Updated SKILL.md and README.md with likes privacy limitation

Closes #8
2026-03-11 12:34:17 +08:00
jackwener
f31830f058 fix: fix ClawHub publish workflow
- Add export VERSION (NODE_ENV was not getting version)
- Fix API endpoint: api.clawhub.io → clawhub.ai
- Add NODE_TLS_REJECT_UNAUTHORIZED=0 for SSL cert issue
2026-03-11 01:11:54 +08:00
jackwener
e38f2033e9 chore: bump version to 0.5.1 2026-03-11 00:45:55 +08:00
jackwener
1f267008ad fix: update stale Followers/Following queryIds and retry on 422
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.
2026-03-11 00:45:13 +08:00
jackwener
5c1015f1fd fix: handle changed /account/multi/list.json response format
Twitter changed the response format from a list with nested 'user'
objects to {"users": [{user_id, name, screen_name, ...}]} with
minimal fields. Now extracts screen_name from the new format and
fetches the full profile via GraphQL UserByScreenName endpoint.
2026-03-11 00:40:07 +08:00
jackwener
8505428264 docs: update cross-references (xhs-cli → xiaohongshu-cli) 2026-03-11 00:33:14 +08:00
jackwener
34d2491ba2 chore: bump version to 0.5.0
- refactor: split client.py into graphql.py + parser.py
- refactor: structured exception hierarchy (exceptions.py)
- feat: integration smoke tests
- docs: updated project structure
2026-03-11 00:30:19 +08:00
jackwener
21b4c28d63 docs: update project structure in README (add graphql.py, parser.py, exceptions.py, output.py) 2026-03-10 23:45:14 +08:00
jackwener
f125d0fe7f refactor: replace all RuntimeError with specific exception types
- 5x RuntimeError → TwitterAPIError (create_tweet, fetch_me, quote_tweet, follow/unfollow)
- 2x RuntimeError(str(exc)) → bare raise (preserve original TwitterAPIError in _graphql_get/post)
2026-03-10 23:32:10 +08:00
jackwener
c2b9be4669 refactor: split client.py into graphql.py + parser.py modules
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.
2026-03-10 23:18:59 +08:00
jackwener
4afc4fc246 refactor: add exceptions.py module with structured exception hierarchy
- Create exceptions.py with 7 exception types: TwitterError, AuthenticationError,
  RateLimitError, NotFoundError, NetworkError, QueryIdError, TwitterAPIError
- Remove inline TwitterAPIError from client.py, import from exceptions module
- Replace RuntimeError('Cannot resolve queryId') with QueryIdError
- Replace RuntimeError('User not found') with NotFoundError
- Update test assertion for new TwitterAPIError message format
2026-03-10 23:05:05 +08:00
jackwener
9cf74abd56 feat: add integration smoke tests
CLI-level smoke tests using --yaml output against real Twitter API.
Default skipped via @pytest.mark.smoke marker + pyproject.toml addopts.
Run locally with: uv run pytest -m smoke -v
2026-03-10 22:26:46 +08:00
jackwener
fa6255f2ee docs: document structured output schema 2026-03-10 21:22:35 +08:00
jackwener
4c2c02efd5 feat: unify structured error output 2026-03-10 21:18:38 +08:00
jackwener
9b7bdf3b06 feat: wrap structured outputs in agent schema 2026-03-10 21:10:48 +08:00
jackwener
642ffe84a8 feat: unify agent status schema 2026-03-10 21:02:08 +08:00
jackwener
32d074dc9f feat: anti-detection hardening, transaction cache, article parsing, structured write output
Anti-detection:
- Add 6 sec-ch-ua-* Client Hints headers (arch, bitness, full-version, etc.)
- POST requests now send Referer: x.com/compose/post + Priority: u=1, i
- follow/unfollow REST adds include_profile_interstitial_type param

Performance:
- Transaction ID cache with 1h TTL (~/.twitter-cli/transaction_cache.json)
- resolve_user_id: auto-detect screen_name vs numeric user_id

Features:
- Twitter Article parsing: extract long-form content as Markdown
- Write operations emit structured JSON/YAML when piped or OUTPUT env set
  ActionResult: {success, action, id, url, ...}

84 tests passing
2026-03-10 20:48:42 +08:00
jackwener
97708889c9 docs: enhance SKILL.md with auth guide, agent workflows, compact mode, error ref
- Add step-by-step authentication guidance for agents (3 methods)
- Add 10+ agent workflow examples (post/reply/quote/thread/search+jq)
- Document compact mode (-c) for LLM token efficiency
- Add new commands: whoami, reply, quote, follow/unfollow
- Add complete error reference table
- Add limitations section
2026-03-10 20:13:47 +08:00
jackwener
49d3e237c4 feat: add whoami, reply, quote, follow/unfollow commands and --compact mode
- whoami: fetch current authenticated user profile
- reply <id> <text>: standalone reply command
- quote <id> <text>: quote-tweet command
- follow/unfollow <handle>: follow/unfollow users
- --compact/-c: global flag for LLM-friendly minimal JSON output
- client.py: add fetch_me, quote_tweet, follow_user, unfollow_user
- serialization.py: add tweet_to_compact_dict, tweets_to_compact_json
- 7 new tests (82 total, all passing)
2026-03-10 20:09:08 +08:00
jackwener
250fca46f0 docs: add structured output guidance for AI agents 2026-03-10 15:47:06 +08:00
jackwener
d22a729fb9 docs: add related CLI project links 2026-03-10 14:15:35 +08:00
jackwener
d71ad45a0a fix: harden pagination auth and runtime headers 2026-03-10 12:33:04 +08:00
jackwener
4f144d1591 test: track parser fixture files 2026-03-10 12:08:47 +08:00
jackwener
19ab11d6a4 fix: harden auth flow and sync browser support docs 2026-03-10 11:02:34 +08:00
jakevin
db7d7e8874 Merge pull request #5 from y0u-0/arc-browser-support
feat: add Arc browser support
2026-03-10 11:02:08 +08:00
Ali Aldhamen
5eb0b1a8f4 feat: add arc browser support 2026-03-09 21:04:48 +03:00
jackwener
7fb02ba18c feat: cookie file cache with TTL + user --json
- Cookie cache: save to ~/.cache/twitter-cli/cookies.json (24h TTL)
- On 401/403 auth failure: auto-invalidate cache, re-extract from browser
- Cache uses 0600 permissions for security
- Add --json option to twitter user command for scripting
- Priority: env vars → cache file → browser extraction
2026-03-09 21:47:23 +08:00
jackwener
bbe0bc8de3 docs: rename rt/unrt → retweet/unretweet, update SKILL.md + README.md
- Rename rt/unrt commands to retweet/unretweet for clarity
- SKILL.md: fix Python version, add --output/--filter examples, safety notes
- README.md: dynamic Chrome fingerprint, write delays, --output examples
- Sync Chinese section with English changes
2026-03-09 21:20:05 +08:00
jackwener
8313a7012f refactor: dynamic UA matching, session reuse, score Optional, --output on all commands
- constants.py: sync_chrome_version() aligns UA/sec-ch-ua with impersonate target
- auth.py: reuse shared cffi session instead of creating duplicate
- filter.py: eliminate double weight building in score_tweet
- models.py: Tweet.score → Optional[float] for accurate display
- cli.py: add --output to search/likes/user-posts for consistency
2026-03-09 21:15:28 +08:00
jackwener
fda9b1c3dc fix: 431 Request Too Large — reduce FEATURES to 15 essential keys, dynamic update only updates existing keys 2026-03-09 20:59:16 +08:00
jackwener
d20c5699fd fix: 414 URI Too Long — omit False-valued features from GET URL, add regression tests 2026-03-09 20:50:45 +08:00
jackwener
12f425abea feat: write operation delays, dynamic FEATURES update, 30+ client.py tests, fix README proxy wording 2026-03-09 20:45:51 +08:00
jackwener
b9c226b804 fix: dynamic Chrome impersonation + in-process macOS Keychain cookie extraction (closes #4) 2026-03-09 20:38:47 +08:00
jackwener
59b5df7f71 fix: ClawHub publish (acceptLicenseTerms), add LICENSE, fix score display bug, remove unused var 2026-03-09 20:32:45 +08:00
jackwener
94e21fba9a fix: remove unused requests import (CI lint), fix README wording, drop requests dep 2026-03-09 20:25:24 +08:00
jackwener
a6ad246071 fix: improve error handling for write operation rate limits (retweet/like) 2026-03-09 20:22:46 +08:00
jackwener
27b02578c8 docs: update README with anti-detection features, proxy support, and ban prevention tips 2026-03-09 19:17:01 +08:00