feat(twitter): GraphQL-based lists + list-tweets + list-add/remove#1076
Merged
jackwener merged 4 commits intojackwener:mainfrom Apr 18, 2026
Merged
feat(twitter): GraphQL-based lists + list-tweets + list-add/remove#1076jackwener merged 4 commits intojackwener:mainfrom
jackwener merged 4 commits intojackwener:mainfrom
Conversation
Contributor
Author
|
Twitter List(列表)是 OpenCLI 极其重要的功能模块。它打破了 5000 人的关注上限,允许用户精细化管理信息流。通过实现此功能,用户可以: 使用 OpenCLI 批量获取特定 List 下的推文。 配合 OpenCLI 的 AI 模块,对 List 内容进行自动化处理、摘要和过滤,极大地提升阅读效率。 |
The DOM-scraping / detail-click approach in PR jackwener#1053 remained fragile against X's frequent overview-page rendering changes and slow (N+1 page loads per list). Rewrite `twitter lists` to call `ListsManagementPageTimeline` GraphQL directly — one request returns all owned + subscribed lists with id/name/member_count/subscriber_count/mode. Also add `twitter list-tweets <listId>` for pulling the tweet stream from a list, completing the read-side chain (lists → pick an id → list-tweets). - lists: drop positional `user` arg (GraphQL returns only logged-in user's lists), add `id` column, change followers to exact integer from subscriber_count. - list-tweets: same GraphQL pattern as bookmarks/likes (BEARER + ct0 + dynamic queryId with static fallback + cursor pagination). - Delete obsolete lists-parser.js and lists.d.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new commands to toggle list membership. X's list dialog uses a "click-to-stage, Save-to-commit" pattern — clicking a row only updates optimistic UI; the actual POST fires when the user clicks the top-right "Save" button. Pressing ESC or the close-X silently cancels the change. Implementation: - Resolve listId → name via ListsManagementPageTimeline GraphQL, so we match the dialog row by name (dialog rows have no data-testid listId). - Open profile page → DOM click "…" menu → "Add/remove from Lists". - Scroll dialog to locate target row (virtualized list). - page.nativeClick on row — trusted CDP Input.dispatchMouseEvent fires React's onclick, flips aria-checked (.click() alone does not suffice; X ignores non-trusted events for list mutations). - page.nativeClick on the Save button — commits to server. - Verify by re-fetching ListsManagementPageTimeline and diffing member_count: success only if N→N±1. No silent successes. This fixes the pattern where batch `list-add` calls returned success for every user but committed zero to the server (optimistic UI lied). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
72b8521 to
673031a
Compare
Contributor
Author
Code reviewFound 1 issue:
Lines 15160 to 15166 in 673031a Likely cause: I regenerated the manifest via 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概要
在 #1053 基础上做两类增强,改进 Twitter list 处理:
ListsManagementPageTimeline重写twitter lists(commit 1)。一次 API 调用即返回所有 owned + subscribed 列表,字段精确(member_count/subscriber_count)。去掉 PR fix(twitter): repair lists scraping from detail pages #1053 里脆弱的 DOM-scraping + detail-click 主循环(约 240 行),也省掉"N 个 list = N+1 次整页加载"的性能开销。X 改 overview 页 DOM 时这条命令不会再跟着崩。twitter list-tweets <listId>(commit 1)。读取 list 的推文流,走bookmarks.js/likes.js同一套 GraphQL 模板(BEARER + ct0 + 动态 queryId + cursor 分页)。读侧闭环:lists拿 id →list-tweets拉推文。twitter list-add/twitter list-remove(commit 2)。写操作,把用户加入 / 移出自己的 list。commit 2 为何是这个实现
落地之前尝试了三种方案都不通:
ListAddMembermutation → X 返回BadRequest: com.twitter.strato.serialization.DecodeException。这个 mutation 的 body schema 受 features 字段约束,从 client-web bundle 里抓不到可自动化复用的完整形状。/1.1/lists/members/create.json→ HTTP 401(需要 OAuth1.0a 签名,仅靠 bearer+ct0 不足以写 v1.1)。.click()点 dialog 里的 row + 关闭 → 静默什么都没做。X 的 list dialog 是 "click 只做暂存,Save 才真正提交" 模式:点 row 仅翻转aria-checked(乐观 UI),不会发 POST;按 ESC 或关闭 X 等于 取消。只有点右上角的 Save 按钮才会提交到服务端。而且.click()(非 trusted event)在 row 上也不管用——X 对 list 写操作做了isTrusted=true过滤。最终可靠的路径:
page.nativeClick(CDPInput.dispatchMouseEvent)点 row —— trusted 事件,aria-checked 翻转page.nativeClick点 Save 按钮 —— 触发 X 真正提交ListsManagementPageTimeline对比member_count前后差:只有N → N±1才算 success。绝不相信乐观 UI真实场景验证:批量把 15 个用户加进同一个 list,每一条都能看到
member_count递增,没有假 success。附带变化
lists去掉位置参数user(GraphQL 只会返回当前登录用户的 list;旧版那种"抓别人 /lists 页"的行为其实从来都不稳定)。lists返回的followers改成精确整数(如"8747"),不再是 overview 页的近似字符串("8.7K")。lists-parser.js和lists.d.ts。测试
npx vitest run clis/twitter/lists.test.js clis/twitter/list-tweets.test.js clis/twitter/list-add.test.js clis/twitter/list-remove.test.js— 14/14 通过npx tsc --noEmit— cleanopencli twitter lists --limit 10 -f json— 返回所有 list + idopencli twitter list-tweets <id> --limit 5 -f json— 正常返回推文opencli twitter list-add <id> <user>— 批量连续 15 次,每次都看到member_count N→N+1opencli twitter list-remove <id> <user>—member_count正常递减背景
基于已合入的 #1053。X list dialog 的 "Save-to-commit" 模式文档里查不到,踩坑细节在 commit 2 的 message 里也有。
🤖 Generated with Claude Code