@jackwener/opencli 1.0.6 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/cross-project-adapter-migration/SKILL.md +2 -2
- package/.github/pull_request_template.md +7 -0
- package/.github/workflows/doc-check.yml +36 -0
- package/.github/workflows/docs.yml +7 -42
- package/CHANGELOG.md +23 -0
- package/CLI-EXPLORER.md +9 -8
- package/README.md +51 -10
- package/README.zh-CN.md +29 -11
- package/SKILL.md +102 -33
- package/dist/browser/cdp.js +6 -1
- package/dist/browser/page.d.ts +4 -1
- package/dist/browser/page.js +7 -1
- package/dist/build-manifest.js +23 -16
- package/dist/cli-manifest.json +951 -296
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +225 -148
- package/dist/clis/antigravity/serve.js +296 -47
- package/dist/clis/apple-podcasts/commands.test.d.ts +2 -0
- package/dist/clis/apple-podcasts/commands.test.js +76 -0
- package/dist/clis/apple-podcasts/search.js +2 -2
- package/dist/clis/apple-podcasts/top.js +9 -2
- package/dist/clis/arxiv/paper.js +21 -0
- package/dist/clis/arxiv/search.js +24 -0
- package/dist/clis/arxiv/utils.d.ts +18 -0
- package/dist/clis/arxiv/utils.js +49 -0
- package/dist/clis/bilibili/dynamic.js +1 -1
- package/dist/clis/bilibili/favorite.js +1 -1
- package/dist/clis/bilibili/feed.js +1 -1
- package/dist/clis/bilibili/following.js +1 -1
- package/dist/clis/bilibili/history.js +1 -1
- package/dist/clis/bilibili/me.js +1 -1
- package/dist/clis/bilibili/ranking.js +1 -1
- package/dist/clis/bilibili/search.js +3 -3
- package/dist/clis/bilibili/subtitle.js +1 -1
- package/dist/clis/bilibili/user-videos.js +1 -1
- package/dist/{bilibili.d.ts → clis/bilibili/utils.d.ts} +1 -1
- package/dist/clis/bloomberg/businessweek.d.ts +1 -0
- package/dist/clis/bloomberg/businessweek.js +17 -0
- package/dist/clis/bloomberg/economics.d.ts +1 -0
- package/dist/clis/bloomberg/economics.js +17 -0
- package/dist/clis/bloomberg/feeds.d.ts +1 -0
- package/dist/clis/bloomberg/feeds.js +15 -0
- package/dist/clis/bloomberg/industries.d.ts +1 -0
- package/dist/clis/bloomberg/industries.js +17 -0
- package/dist/clis/bloomberg/main.d.ts +1 -0
- package/dist/clis/bloomberg/main.js +17 -0
- package/dist/clis/bloomberg/markets.d.ts +1 -0
- package/dist/clis/bloomberg/markets.js +17 -0
- package/dist/clis/bloomberg/news.d.ts +1 -0
- package/dist/clis/bloomberg/news.js +105 -0
- package/dist/clis/bloomberg/opinions.d.ts +1 -0
- package/dist/clis/bloomberg/opinions.js +17 -0
- package/dist/clis/bloomberg/politics.d.ts +1 -0
- package/dist/clis/bloomberg/politics.js +17 -0
- package/dist/clis/bloomberg/tech.d.ts +1 -0
- package/dist/clis/bloomberg/tech.js +17 -0
- package/dist/clis/bloomberg/utils.d.ts +34 -0
- package/dist/clis/bloomberg/utils.js +364 -0
- package/dist/clis/bloomberg/utils.test.d.ts +1 -0
- package/dist/clis/bloomberg/utils.test.js +129 -0
- package/dist/clis/boss/batchgreet.d.ts +1 -0
- package/dist/clis/boss/batchgreet.js +147 -0
- package/dist/clis/boss/chatlist.js +2 -2
- package/dist/clis/boss/detail.js +2 -2
- package/dist/clis/boss/exchange.d.ts +1 -0
- package/dist/clis/boss/exchange.js +111 -0
- package/dist/clis/boss/greet.d.ts +1 -0
- package/dist/clis/boss/greet.js +175 -0
- package/dist/clis/boss/invite.d.ts +1 -0
- package/dist/clis/boss/invite.js +158 -0
- package/dist/clis/boss/joblist.d.ts +1 -0
- package/dist/clis/boss/joblist.js +55 -0
- package/dist/clis/boss/mark.d.ts +1 -0
- package/dist/clis/boss/mark.js +141 -0
- package/dist/clis/boss/recommend.d.ts +1 -0
- package/dist/clis/boss/recommend.js +83 -0
- package/dist/clis/boss/search.js +1 -1
- package/dist/clis/boss/send.js +1 -1
- package/dist/clis/boss/stats.d.ts +1 -0
- package/dist/clis/boss/stats.js +116 -0
- package/dist/clis/chaoxing/assignments.js +1 -1
- package/dist/clis/chaoxing/exams.js +1 -1
- package/dist/{chaoxing.d.ts → clis/chaoxing/utils.d.ts} +1 -1
- package/dist/{chaoxing.js → clis/chaoxing/utils.js} +0 -2
- package/dist/clis/chaoxing/utils.test.d.ts +1 -0
- package/dist/{chaoxing.test.js → clis/chaoxing/utils.test.js} +1 -1
- package/dist/clis/chatgpt/read.js +1 -1
- package/dist/clis/chatwise/export.js +1 -1
- package/dist/clis/chatwise/model.js +2 -2
- package/dist/clis/chatwise/screenshot.js +1 -1
- package/dist/clis/codex/export.js +1 -1
- package/dist/clis/codex/model.js +2 -2
- package/dist/clis/codex/screenshot.js +1 -1
- package/dist/clis/coupang/add-to-cart.js +3 -4
- package/dist/clis/coupang/search.js +2 -4
- package/dist/clis/coupang/utils.test.d.ts +1 -0
- package/dist/{coupang.test.js → clis/coupang/utils.test.js} +1 -1
- package/dist/clis/ctrip/search.js +1 -1
- package/dist/clis/cursor/export.js +1 -1
- package/dist/clis/cursor/model.js +2 -2
- package/dist/clis/cursor/screenshot.js +1 -1
- package/dist/clis/jike/comment.js +2 -3
- package/dist/clis/jike/create.js +1 -2
- package/dist/clis/jike/feed.js +0 -1
- package/dist/clis/jike/like.js +1 -2
- package/dist/clis/jike/notifications.js +0 -1
- package/dist/clis/jike/post.yaml +1 -0
- package/dist/clis/jike/repost.js +1 -2
- package/dist/clis/jike/search.js +2 -3
- package/dist/clis/jike/topic.yaml +1 -0
- package/dist/clis/jike/user.yaml +1 -0
- package/dist/clis/jimeng/history.yaml +0 -1
- package/dist/clis/linkedin/search.js +7 -7
- package/dist/clis/linux-do/category.yaml +1 -0
- package/dist/clis/linux-do/search.yaml +4 -3
- package/dist/clis/linux-do/topic.yaml +1 -0
- package/dist/clis/notion/export.js +1 -1
- package/dist/clis/reddit/comment.js +3 -4
- package/dist/clis/reddit/read.js +4 -5
- package/dist/clis/reddit/save.js +2 -3
- package/dist/clis/reddit/saved.js +0 -1
- package/dist/clis/reddit/search.yaml +1 -0
- package/dist/clis/reddit/subscribe.js +0 -1
- package/dist/clis/reddit/upvote.js +2 -3
- package/dist/clis/reddit/upvoted.js +0 -1
- package/dist/clis/reddit/user-comments.yaml +1 -0
- package/dist/clis/reddit/user-posts.yaml +1 -0
- package/dist/clis/reddit/user.yaml +1 -0
- package/dist/clis/reuters/search.js +1 -1
- package/dist/clis/sinafinance/news.d.ts +7 -0
- package/dist/clis/sinafinance/news.js +61 -0
- package/dist/clis/smzdm/search.js +2 -3
- package/dist/clis/stackoverflow/search.yaml +1 -0
- package/dist/clis/steam/top-sellers.yaml +29 -0
- package/dist/clis/twitter/accept.js +2 -2
- package/dist/clis/twitter/article.js +2 -2
- package/dist/clis/twitter/block.d.ts +1 -0
- package/dist/clis/twitter/block.js +88 -0
- package/dist/clis/twitter/delete.js +1 -1
- package/dist/clis/twitter/hide-reply.d.ts +1 -0
- package/dist/clis/twitter/hide-reply.js +66 -0
- package/dist/clis/twitter/like.js +1 -1
- package/dist/clis/twitter/post.js +1 -1
- package/dist/clis/twitter/reply-dm.js +1 -1
- package/dist/clis/twitter/reply.js +2 -2
- package/dist/clis/twitter/search.js +1 -1
- package/dist/clis/twitter/thread.js +2 -2
- package/dist/clis/twitter/trending.d.ts +1 -0
- package/dist/clis/twitter/trending.js +91 -0
- package/dist/clis/twitter/unblock.d.ts +1 -0
- package/dist/clis/twitter/unblock.js +71 -0
- package/dist/clis/v2ex/topic.yaml +1 -0
- package/dist/clis/weibo/hot.js +0 -1
- package/dist/clis/weread/book.js +1 -1
- package/dist/clis/weread/highlights.js +1 -1
- package/dist/clis/weread/notes.js +1 -1
- package/dist/clis/weread/search.js +1 -1
- package/dist/clis/wikipedia/search.d.ts +1 -0
- package/dist/clis/wikipedia/search.js +30 -0
- package/dist/clis/wikipedia/summary.d.ts +1 -0
- package/dist/clis/wikipedia/summary.js +28 -0
- package/dist/clis/wikipedia/utils.d.ts +8 -0
- package/dist/clis/wikipedia/utils.js +18 -0
- package/dist/clis/xiaohongshu/creator-note-detail.d.ts +79 -5
- package/dist/clis/xiaohongshu/creator-note-detail.js +323 -70
- package/dist/clis/xiaohongshu/creator-note-detail.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/creator-note-detail.test.js +258 -0
- package/dist/clis/xiaohongshu/creator-notes-summary.d.ts +28 -0
- package/dist/clis/xiaohongshu/creator-notes-summary.js +92 -0
- package/dist/clis/xiaohongshu/creator-notes-summary.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/creator-notes-summary.test.js +49 -0
- package/dist/clis/xiaohongshu/creator-notes.d.ts +18 -5
- package/dist/clis/xiaohongshu/creator-notes.js +189 -71
- package/dist/clis/xiaohongshu/creator-notes.test.d.ts +1 -0
- package/dist/clis/xiaohongshu/creator-notes.test.js +191 -0
- package/dist/clis/xiaohongshu/creator-profile.js +0 -1
- package/dist/clis/xiaohongshu/creator-stats.js +0 -1
- package/dist/clis/xiaohongshu/download.js +2 -3
- package/dist/clis/xiaohongshu/feed.yaml +0 -1
- package/dist/clis/xiaohongshu/notifications.yaml +0 -1
- package/dist/clis/xiaohongshu/search.js +2 -2
- package/dist/clis/xiaohongshu/user.js +1 -2
- package/dist/clis/yahoo-finance/quote.js +0 -1
- package/dist/clis/youtube/search.js +1 -1
- package/dist/clis/youtube/transcript.js +1 -1
- package/dist/clis/youtube/video.js +1 -1
- package/dist/clis/zhihu/download.js +1 -2
- package/dist/clis/zhihu/question.js +1 -1
- package/dist/clis/zhihu/search.yaml +4 -3
- package/dist/commanderAdapter.d.ts +21 -0
- package/dist/commanderAdapter.js +111 -0
- package/dist/{engine.d.ts → discovery.d.ts} +0 -6
- package/dist/{engine.js → discovery.js} +1 -98
- package/dist/download/index.d.ts +2 -6
- package/dist/download/index.js +19 -46
- package/dist/engine.test.d.ts +1 -1
- package/dist/engine.test.js +8 -7
- package/dist/execution.d.ts +22 -0
- package/dist/execution.js +129 -0
- package/dist/explore.js +121 -107
- package/dist/external-clis.yaml +48 -0
- package/dist/external.d.ts +25 -0
- package/dist/external.js +156 -0
- package/dist/main.js +1 -1
- package/dist/pipeline/steps/browser.js +8 -2
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +2 -0
- package/dist/runtime.d.ts +5 -0
- package/dist/runtime.js +8 -0
- package/dist/serialization.d.ts +34 -0
- package/dist/serialization.js +63 -0
- package/dist/types.d.ts +4 -1
- package/docs/.vitepress/config.mts +14 -3
- package/docs/adapters/browser/arxiv.md +27 -0
- package/docs/adapters/browser/barchart.md +32 -0
- package/docs/adapters/browser/bloomberg.md +70 -0
- package/docs/adapters/browser/chaoxing.md +39 -0
- package/docs/adapters/browser/grok.md +35 -0
- package/docs/adapters/browser/hf.md +42 -0
- package/docs/adapters/browser/jike.md +45 -0
- package/docs/adapters/browser/jimeng.md +39 -0
- package/docs/adapters/browser/linux-do.md +45 -0
- package/docs/adapters/browser/sinafinance.md +35 -0
- package/docs/adapters/browser/stackoverflow.md +35 -0
- package/docs/adapters/browser/steam.md +26 -0
- package/docs/adapters/browser/twitter.md +3 -0
- package/docs/adapters/browser/weread.md +48 -0
- package/docs/adapters/browser/wikipedia.md +30 -0
- package/docs/adapters/browser/xiaohongshu.md +5 -1
- package/docs/adapters/desktop/chatgpt.md +3 -3
- package/docs/adapters/index.md +13 -0
- package/docs/advanced/download.md +4 -4
- package/docs/developer/architecture.md +17 -4
- package/package.json +1 -1
- package/scripts/check-doc-coverage.sh +69 -0
- package/scripts/copy-yaml.cjs +7 -0
- package/src/browser/cdp.ts +9 -4
- package/src/browser/page.ts +7 -1
- package/src/build-manifest.ts +25 -19
- package/src/cli.ts +253 -119
- package/src/clis/antigravity/serve.ts +323 -50
- package/src/clis/apple-podcasts/commands.test.ts +95 -0
- package/src/clis/apple-podcasts/search.ts +2 -2
- package/src/clis/apple-podcasts/top.ts +12 -2
- package/src/clis/arxiv/paper.ts +21 -0
- package/src/clis/arxiv/search.ts +24 -0
- package/src/clis/arxiv/utils.ts +63 -0
- package/src/clis/bilibili/dynamic.ts +1 -1
- package/src/clis/bilibili/favorite.ts +1 -1
- package/src/clis/bilibili/feed.ts +1 -1
- package/src/clis/bilibili/following.ts +1 -1
- package/src/clis/bilibili/history.ts +1 -1
- package/src/clis/bilibili/me.ts +1 -1
- package/src/clis/bilibili/ranking.ts +1 -1
- package/src/clis/bilibili/search.ts +3 -3
- package/src/clis/bilibili/subtitle.ts +1 -1
- package/src/clis/bilibili/user-videos.ts +1 -1
- package/src/{bilibili.ts → clis/bilibili/utils.ts} +1 -1
- package/src/clis/bloomberg/businessweek.ts +18 -0
- package/src/clis/bloomberg/economics.ts +18 -0
- package/src/clis/bloomberg/feeds.ts +16 -0
- package/src/clis/bloomberg/industries.ts +18 -0
- package/src/clis/bloomberg/main.ts +18 -0
- package/src/clis/bloomberg/markets.ts +18 -0
- package/src/clis/bloomberg/news.ts +136 -0
- package/src/clis/bloomberg/opinions.ts +18 -0
- package/src/clis/bloomberg/politics.ts +18 -0
- package/src/clis/bloomberg/tech.ts +18 -0
- package/src/clis/bloomberg/utils.test.ts +135 -0
- package/src/clis/bloomberg/utils.ts +429 -0
- package/src/clis/boss/batchgreet.ts +167 -0
- package/src/clis/boss/chatlist.ts +2 -2
- package/src/clis/boss/detail.ts +2 -2
- package/src/clis/boss/exchange.ts +126 -0
- package/src/clis/boss/greet.ts +198 -0
- package/src/clis/boss/invite.ts +177 -0
- package/src/clis/boss/joblist.ts +63 -0
- package/src/clis/boss/mark.ts +155 -0
- package/src/clis/boss/recommend.ts +94 -0
- package/src/clis/boss/search.ts +1 -1
- package/src/clis/boss/send.ts +1 -1
- package/src/clis/boss/stats.ts +130 -0
- package/src/clis/chaoxing/assignments.ts +1 -1
- package/src/clis/chaoxing/exams.ts +1 -1
- package/src/{chaoxing.test.ts → clis/chaoxing/utils.test.ts} +1 -1
- package/src/{chaoxing.ts → clis/chaoxing/utils.ts} +1 -3
- package/src/clis/chatgpt/README.zh-CN.md +3 -3
- package/src/clis/chatgpt/read.ts +1 -1
- package/src/clis/chatwise/export.ts +1 -1
- package/src/clis/chatwise/model.ts +2 -2
- package/src/clis/chatwise/screenshot.ts +1 -1
- package/src/clis/codex/export.ts +1 -1
- package/src/clis/codex/model.ts +2 -2
- package/src/clis/codex/screenshot.ts +1 -1
- package/src/clis/coupang/add-to-cart.ts +3 -4
- package/src/clis/coupang/search.ts +2 -4
- package/src/{coupang.test.ts → clis/coupang/utils.test.ts} +1 -1
- package/src/clis/ctrip/search.ts +1 -1
- package/src/clis/cursor/export.ts +1 -1
- package/src/clis/cursor/model.ts +2 -2
- package/src/clis/cursor/screenshot.ts +1 -1
- package/src/clis/jike/comment.ts +2 -3
- package/src/clis/jike/create.ts +1 -2
- package/src/clis/jike/feed.ts +0 -1
- package/src/clis/jike/like.ts +1 -2
- package/src/clis/jike/notifications.ts +0 -1
- package/src/clis/jike/post.yaml +1 -0
- package/src/clis/jike/repost.ts +1 -2
- package/src/clis/jike/search.ts +2 -3
- package/src/clis/jike/topic.yaml +1 -0
- package/src/clis/jike/user.yaml +1 -0
- package/src/clis/jimeng/history.yaml +0 -1
- package/src/clis/linkedin/search.ts +7 -7
- package/src/clis/linux-do/category.yaml +1 -0
- package/src/clis/linux-do/search.yaml +4 -3
- package/src/clis/linux-do/topic.yaml +1 -0
- package/src/clis/notion/export.ts +1 -1
- package/src/clis/reddit/comment.ts +3 -4
- package/src/clis/reddit/read.ts +4 -5
- package/src/clis/reddit/save.ts +2 -3
- package/src/clis/reddit/saved.ts +0 -1
- package/src/clis/reddit/search.yaml +1 -0
- package/src/clis/reddit/subscribe.ts +0 -1
- package/src/clis/reddit/upvote.ts +2 -3
- package/src/clis/reddit/upvoted.ts +0 -1
- package/src/clis/reddit/user-comments.yaml +1 -0
- package/src/clis/reddit/user-posts.yaml +1 -0
- package/src/clis/reddit/user.yaml +1 -0
- package/src/clis/reuters/search.ts +1 -1
- package/src/clis/sinafinance/news.ts +76 -0
- package/src/clis/smzdm/search.ts +2 -3
- package/src/clis/stackoverflow/search.yaml +1 -0
- package/src/clis/steam/top-sellers.yaml +29 -0
- package/src/clis/twitter/accept.ts +2 -2
- package/src/clis/twitter/article.ts +2 -2
- package/src/clis/twitter/block.ts +92 -0
- package/src/clis/twitter/delete.ts +1 -1
- package/src/clis/twitter/hide-reply.ts +70 -0
- package/src/clis/twitter/like.ts +1 -1
- package/src/clis/twitter/post.ts +1 -1
- package/src/clis/twitter/reply-dm.ts +1 -1
- package/src/clis/twitter/reply.ts +2 -2
- package/src/clis/twitter/search.ts +1 -1
- package/src/clis/twitter/thread.ts +2 -2
- package/src/clis/twitter/trending.ts +113 -0
- package/src/clis/twitter/unblock.ts +75 -0
- package/src/clis/v2ex/topic.yaml +1 -0
- package/src/clis/weibo/hot.ts +0 -1
- package/src/clis/weread/book.ts +1 -1
- package/src/clis/weread/highlights.ts +1 -1
- package/src/clis/weread/notes.ts +1 -1
- package/src/clis/weread/search.ts +1 -1
- package/src/clis/wikipedia/search.ts +32 -0
- package/src/clis/wikipedia/summary.ts +28 -0
- package/src/clis/wikipedia/utils.ts +20 -0
- package/src/clis/xiaohongshu/creator-note-detail.test.ts +272 -0
- package/src/clis/xiaohongshu/creator-note-detail.ts +425 -73
- package/src/clis/xiaohongshu/creator-notes-summary.test.ts +54 -0
- package/src/clis/xiaohongshu/creator-notes-summary.ts +120 -0
- package/src/clis/xiaohongshu/creator-notes.test.ts +211 -0
- package/src/clis/xiaohongshu/creator-notes.ts +254 -75
- package/src/clis/xiaohongshu/creator-profile.ts +0 -1
- package/src/clis/xiaohongshu/creator-stats.ts +0 -1
- package/src/clis/xiaohongshu/download.ts +2 -3
- package/src/clis/xiaohongshu/feed.yaml +0 -1
- package/src/clis/xiaohongshu/notifications.yaml +0 -1
- package/src/clis/xiaohongshu/search.ts +2 -2
- package/src/clis/xiaohongshu/user.ts +1 -2
- package/src/clis/yahoo-finance/quote.ts +0 -1
- package/src/clis/youtube/search.ts +1 -1
- package/src/clis/youtube/transcript.ts +1 -1
- package/src/clis/youtube/video.ts +1 -1
- package/src/clis/zhihu/download.ts +1 -2
- package/src/clis/zhihu/question.ts +1 -1
- package/src/clis/zhihu/search.yaml +4 -3
- package/src/commanderAdapter.ts +113 -0
- package/src/daemon.ts +3 -3
- package/src/{engine.ts → discovery.ts} +1 -108
- package/src/download/index.ts +21 -54
- package/src/engine.test.ts +8 -7
- package/src/execution.ts +138 -0
- package/src/explore.ts +135 -109
- package/src/external-clis.yaml +48 -0
- package/src/external.ts +185 -0
- package/src/main.ts +1 -1
- package/src/pipeline/steps/browser.ts +7 -2
- package/src/registry.ts +5 -0
- package/src/runtime.ts +9 -0
- package/src/serialization.ts +79 -0
- package/src/types.ts +1 -1
- package/tests/e2e/browser-public.test.ts +25 -0
- package/tests/e2e/public-commands.test.ts +55 -1
- package/dist/clis/twitter/trending.yaml +0 -46
- package/src/clis/twitter/trending.yaml +0 -46
- /package/dist/{chaoxing.test.d.ts → clis/arxiv/paper.d.ts} +0 -0
- /package/dist/{coupang.test.d.ts → clis/arxiv/search.d.ts} +0 -0
- /package/dist/{bilibili.js → clis/bilibili/utils.js} +0 -0
- /package/dist/{coupang.d.ts → clis/coupang/utils.d.ts} +0 -0
- /package/dist/{coupang.js → clis/coupang/utils.js} +0 -0
- /package/src/{coupang.ts → clis/coupang/utils.ts} +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import type { IPage } from '../../types.js';
|
|
3
|
+
|
|
4
|
+
cli({
|
|
5
|
+
site: 'twitter',
|
|
6
|
+
name: 'block',
|
|
7
|
+
description: 'Block a Twitter user',
|
|
8
|
+
domain: 'x.com',
|
|
9
|
+
strategy: Strategy.UI,
|
|
10
|
+
browser: true,
|
|
11
|
+
args: [
|
|
12
|
+
{ name: 'username', type: 'string', positional: true, required: true, help: 'Twitter screen name (without @)' },
|
|
13
|
+
],
|
|
14
|
+
columns: ['status', 'message'],
|
|
15
|
+
func: async (page: IPage | null, kwargs: any) => {
|
|
16
|
+
if (!page) throw new Error('Requires browser');
|
|
17
|
+
const username = kwargs.username.replace(/^@/, '');
|
|
18
|
+
|
|
19
|
+
await page.goto(`https://x.com/${username}`);
|
|
20
|
+
await page.wait(5);
|
|
21
|
+
|
|
22
|
+
const result = await page.evaluate(`(async () => {
|
|
23
|
+
try {
|
|
24
|
+
let attempts = 0;
|
|
25
|
+
|
|
26
|
+
// Check if already blocked (profile shows "Blocked" / unblock button)
|
|
27
|
+
while (attempts < 20) {
|
|
28
|
+
const blockedIndicator = document.querySelector('[data-testid$="-unblock"]');
|
|
29
|
+
if (blockedIndicator) {
|
|
30
|
+
return { ok: true, message: 'Already blocking @${username}.' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const moreBtn = document.querySelector('[data-testid="userActions"]');
|
|
34
|
+
if (moreBtn) break;
|
|
35
|
+
|
|
36
|
+
await new Promise(r => setTimeout(r, 500));
|
|
37
|
+
attempts++;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const moreBtn = document.querySelector('[data-testid="userActions"]');
|
|
41
|
+
if (!moreBtn) {
|
|
42
|
+
return { ok: false, message: 'Could not find user actions menu. Are you logged in?' };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Open the more actions menu
|
|
46
|
+
moreBtn.click();
|
|
47
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
48
|
+
|
|
49
|
+
// Find the Block menu item
|
|
50
|
+
const menuItems = document.querySelectorAll('[role="menuitem"]');
|
|
51
|
+
let blockItem = null;
|
|
52
|
+
for (const item of menuItems) {
|
|
53
|
+
if (item.textContent && item.textContent.includes('Block')) {
|
|
54
|
+
blockItem = item;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!blockItem) {
|
|
60
|
+
return { ok: false, message: 'Could not find Block option in menu.' };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
blockItem.click();
|
|
64
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
65
|
+
|
|
66
|
+
// Confirm the block in the dialog
|
|
67
|
+
const confirmBtn = document.querySelector('[data-testid="confirmationSheetConfirm"]');
|
|
68
|
+
if (confirmBtn) {
|
|
69
|
+
confirmBtn.click();
|
|
70
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Verify
|
|
74
|
+
const verify = document.querySelector('[data-testid$="-unblock"]');
|
|
75
|
+
if (verify) {
|
|
76
|
+
return { ok: true, message: 'Successfully blocked @${username}.' };
|
|
77
|
+
} else {
|
|
78
|
+
return { ok: false, message: 'Block action initiated but UI did not update.' };
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {
|
|
81
|
+
return { ok: false, message: e.toString() };
|
|
82
|
+
}
|
|
83
|
+
})()`);
|
|
84
|
+
|
|
85
|
+
if (result.ok) await page.wait(2);
|
|
86
|
+
|
|
87
|
+
return [{
|
|
88
|
+
status: result.ok ? 'success' : 'failed',
|
|
89
|
+
message: result.message
|
|
90
|
+
}];
|
|
91
|
+
}
|
|
92
|
+
});
|
|
@@ -9,7 +9,7 @@ cli({
|
|
|
9
9
|
strategy: Strategy.UI, // Utilizes internal DOM flows for interaction
|
|
10
10
|
browser: true,
|
|
11
11
|
args: [
|
|
12
|
-
{ name: 'url', type: 'string', required: true, help: 'The URL of the tweet to delete' },
|
|
12
|
+
{ name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the tweet to delete' },
|
|
13
13
|
],
|
|
14
14
|
columns: ['status', 'message'],
|
|
15
15
|
func: async (page: IPage | null, kwargs: any) => {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import type { IPage } from '../../types.js';
|
|
3
|
+
|
|
4
|
+
cli({
|
|
5
|
+
site: 'twitter',
|
|
6
|
+
name: 'hide-reply',
|
|
7
|
+
description: 'Hide a reply on your tweet (useful for hiding bot/spam replies)',
|
|
8
|
+
domain: 'x.com',
|
|
9
|
+
strategy: Strategy.UI,
|
|
10
|
+
browser: true,
|
|
11
|
+
args: [
|
|
12
|
+
{ name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the reply tweet to hide' },
|
|
13
|
+
],
|
|
14
|
+
columns: ['status', 'message'],
|
|
15
|
+
func: async (page: IPage | null, kwargs: any) => {
|
|
16
|
+
if (!page) throw new Error('Requires browser');
|
|
17
|
+
|
|
18
|
+
await page.goto(kwargs.url);
|
|
19
|
+
await page.wait(5);
|
|
20
|
+
|
|
21
|
+
const result = await page.evaluate(`(async () => {
|
|
22
|
+
try {
|
|
23
|
+
let attempts = 0;
|
|
24
|
+
let moreMenu = null;
|
|
25
|
+
|
|
26
|
+
while (attempts < 20) {
|
|
27
|
+
moreMenu = document.querySelector('[aria-label="More"]');
|
|
28
|
+
if (moreMenu) break;
|
|
29
|
+
await new Promise(r => setTimeout(r, 500));
|
|
30
|
+
attempts++;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!moreMenu) {
|
|
34
|
+
return { ok: false, message: 'Could not find the "More" menu on this tweet. Are you logged in?' };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
moreMenu.click();
|
|
38
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
39
|
+
|
|
40
|
+
// Look for the "Hide reply" menu item
|
|
41
|
+
const items = document.querySelectorAll('[role="menuitem"]');
|
|
42
|
+
let hideItem = null;
|
|
43
|
+
for (const item of items) {
|
|
44
|
+
if (item.textContent && item.textContent.includes('Hide reply')) {
|
|
45
|
+
hideItem = item;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!hideItem) {
|
|
51
|
+
return { ok: false, message: 'Could not find "Hide reply" option. This may not be a reply on your tweet.' };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
hideItem.click();
|
|
55
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
56
|
+
|
|
57
|
+
return { ok: true, message: 'Reply successfully hidden.' };
|
|
58
|
+
} catch (e) {
|
|
59
|
+
return { ok: false, message: e.toString() };
|
|
60
|
+
}
|
|
61
|
+
})()`);
|
|
62
|
+
|
|
63
|
+
if (result.ok) await page.wait(2);
|
|
64
|
+
|
|
65
|
+
return [{
|
|
66
|
+
status: result.ok ? 'success' : 'failed',
|
|
67
|
+
message: result.message
|
|
68
|
+
}];
|
|
69
|
+
}
|
|
70
|
+
});
|
package/src/clis/twitter/like.ts
CHANGED
|
@@ -9,7 +9,7 @@ cli({
|
|
|
9
9
|
strategy: Strategy.UI, // Utilizes internal DOM flows for interaction
|
|
10
10
|
browser: true,
|
|
11
11
|
args: [
|
|
12
|
-
{ name: 'url', type: 'string', required: true, help: 'The URL of the tweet to like' },
|
|
12
|
+
{ name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the tweet to like' },
|
|
13
13
|
],
|
|
14
14
|
columns: ['status', 'message'],
|
|
15
15
|
func: async (page: IPage | null, kwargs: any) => {
|
package/src/clis/twitter/post.ts
CHANGED
|
@@ -9,7 +9,7 @@ cli({
|
|
|
9
9
|
strategy: Strategy.UI,
|
|
10
10
|
browser: true,
|
|
11
11
|
args: [
|
|
12
|
-
{ name: 'text', type: 'string', required: true, help: 'The text content of the tweet' },
|
|
12
|
+
{ name: 'text', type: 'string', required: true, positional: true, help: 'The text content of the tweet' },
|
|
13
13
|
],
|
|
14
14
|
columns: ['status', 'message', 'text'],
|
|
15
15
|
func: async (page: IPage | null, kwargs: any) => {
|
|
@@ -10,7 +10,7 @@ cli({
|
|
|
10
10
|
browser: true,
|
|
11
11
|
timeoutSeconds: 600, // 10 min — batch operation
|
|
12
12
|
args: [
|
|
13
|
-
{ name: 'text', type: 'string', required: true, help: 'Message text to send (e.g. "我的微信 wxkabi")' },
|
|
13
|
+
{ name: 'text', type: 'string', required: true, positional: true, help: 'Message text to send (e.g. "我的微信 wxkabi")' },
|
|
14
14
|
{ name: 'max', type: 'int', required: false, default: 20, help: 'Maximum number of conversations to reply to (default: 20)' },
|
|
15
15
|
{ name: 'skip-replied', type: 'boolean', required: false, default: true, help: 'Skip conversations where you already sent the same text (default: true)' },
|
|
16
16
|
],
|
|
@@ -9,8 +9,8 @@ cli({
|
|
|
9
9
|
strategy: Strategy.UI, // Uses the UI directly to input and click post
|
|
10
10
|
browser: true,
|
|
11
11
|
args: [
|
|
12
|
-
{ name: 'url', type: 'string', required: true, help: 'The URL of the tweet to reply to' },
|
|
13
|
-
{ name: 'text', type: 'string', required: true, help: 'The text content of your reply' },
|
|
12
|
+
{ name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the tweet to reply to' },
|
|
13
|
+
{ name: 'text', type: 'string', required: true, positional: true, help: 'The text content of your reply' },
|
|
14
14
|
],
|
|
15
15
|
columns: ['status', 'message', 'text'],
|
|
16
16
|
func: async (page: IPage | null, kwargs: any) => {
|
|
@@ -8,7 +8,7 @@ cli({
|
|
|
8
8
|
strategy: Strategy.INTERCEPT, // Use intercept strategy
|
|
9
9
|
browser: true,
|
|
10
10
|
args: [
|
|
11
|
-
{ name: 'query', type: 'string', required: true },
|
|
11
|
+
{ name: 'query', type: 'string', required: true, positional: true },
|
|
12
12
|
{ name: 'limit', type: 'int', default: 15 },
|
|
13
13
|
],
|
|
14
14
|
columns: ['id', 'author', 'text', 'likes', 'views', 'url'],
|
|
@@ -122,12 +122,12 @@ cli({
|
|
|
122
122
|
strategy: Strategy.COOKIE,
|
|
123
123
|
browser: true,
|
|
124
124
|
args: [
|
|
125
|
-
{ name: '
|
|
125
|
+
{ name: 'tweet-id', type: 'string', required: true },
|
|
126
126
|
{ name: 'limit', type: 'int', default: 50 },
|
|
127
127
|
],
|
|
128
128
|
columns: ['id', 'author', 'text', 'likes', 'retweets', 'url'],
|
|
129
129
|
func: async (page, kwargs) => {
|
|
130
|
-
let tweetId = kwargs
|
|
130
|
+
let tweetId = kwargs['tweet-id'];
|
|
131
131
|
const urlMatch = tweetId.match(/\/status\/(\d+)/);
|
|
132
132
|
if (urlMatch) tweetId = urlMatch[1];
|
|
133
133
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
|
|
3
|
+
// ── Twitter GraphQL constants ──────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
const BEARER_TOKEN = 'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';
|
|
6
|
+
|
|
7
|
+
// ── Types ──────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
interface TrendItem {
|
|
10
|
+
rank: number;
|
|
11
|
+
topic: string;
|
|
12
|
+
tweets: string;
|
|
13
|
+
category: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ── CLI definition ────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
cli({
|
|
19
|
+
site: 'twitter',
|
|
20
|
+
name: 'trending',
|
|
21
|
+
description: 'Twitter/X trending topics',
|
|
22
|
+
domain: 'x.com',
|
|
23
|
+
strategy: Strategy.COOKIE,
|
|
24
|
+
browser: true,
|
|
25
|
+
args: [
|
|
26
|
+
{ name: 'limit', type: 'int', default: 20, help: 'Number of trends to show' },
|
|
27
|
+
],
|
|
28
|
+
columns: ['rank', 'topic', 'tweets', 'category'],
|
|
29
|
+
func: async (page, kwargs) => {
|
|
30
|
+
const limit = kwargs.limit || 20;
|
|
31
|
+
|
|
32
|
+
// Navigate to trending page
|
|
33
|
+
await page.goto('https://x.com/explore/tabs/trending');
|
|
34
|
+
await page.wait(3);
|
|
35
|
+
|
|
36
|
+
// Extract CSRF token to verify login
|
|
37
|
+
const ct0 = await page.evaluate(`(() => {
|
|
38
|
+
return document.cookie.split(';').map(c=>c.trim()).find(c=>c.startsWith('ct0='))?.split('=')[1] || null;
|
|
39
|
+
})()`);
|
|
40
|
+
if (!ct0) throw new Error('Not logged into x.com (no ct0 cookie)');
|
|
41
|
+
|
|
42
|
+
// Try legacy guide.json API first (faster than DOM scraping)
|
|
43
|
+
let trends: TrendItem[] = [];
|
|
44
|
+
|
|
45
|
+
const apiData = await page.evaluate(`(async () => {
|
|
46
|
+
const ct0 = document.cookie.split(';').map(c=>c.trim()).find(c=>c.startsWith('ct0='))?.split('=')[1] || '';
|
|
47
|
+
const r = await fetch('/i/api/2/guide.json?include_page_configuration=true', {
|
|
48
|
+
credentials: 'include',
|
|
49
|
+
headers: {
|
|
50
|
+
'x-twitter-active-user': 'yes',
|
|
51
|
+
'x-csrf-token': ct0,
|
|
52
|
+
'authorization': 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA'
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return r.ok ? await r.json() : null;
|
|
56
|
+
})()`);
|
|
57
|
+
|
|
58
|
+
if (apiData) {
|
|
59
|
+
const instructions = apiData?.timeline?.instructions || [];
|
|
60
|
+
const entries = instructions.flatMap((inst: any) => inst?.addEntries?.entries || inst?.entries || []);
|
|
61
|
+
const apiTrends = entries
|
|
62
|
+
.filter((e: any) => e.content?.timelineModule)
|
|
63
|
+
.flatMap((e: any) => e.content.timelineModule.items || [])
|
|
64
|
+
.map((t: any) => t?.item?.content?.trend)
|
|
65
|
+
.filter(Boolean);
|
|
66
|
+
|
|
67
|
+
trends = apiTrends.map((t: any, i: number) => ({
|
|
68
|
+
rank: i + 1,
|
|
69
|
+
topic: t.name,
|
|
70
|
+
tweets: t.tweetCount ? String(t.tweetCount) : 'N/A',
|
|
71
|
+
category: t.trendMetadata?.domainContext || '',
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Fallback: scrape from the loaded DOM
|
|
76
|
+
if (trends.length === 0) {
|
|
77
|
+
await page.wait(2);
|
|
78
|
+
const domTrends = await page.evaluate(`(() => {
|
|
79
|
+
const items = [];
|
|
80
|
+
const cells = document.querySelectorAll('[data-testid="trend"]');
|
|
81
|
+
cells.forEach((cell) => {
|
|
82
|
+
const text = cell.textContent || '';
|
|
83
|
+
if (text.includes('Promoted')) return;
|
|
84
|
+
const container = cell.querySelector(':scope > div');
|
|
85
|
+
if (!container) return;
|
|
86
|
+
const divs = container.children;
|
|
87
|
+
// Structure: divs[0] = rank + category, divs[1] = topic name, divs[2] = extra
|
|
88
|
+
const topicEl = divs.length >= 2 ? divs[1] : null;
|
|
89
|
+
const topic = topicEl ? topicEl.textContent.trim() : '';
|
|
90
|
+
const catEl = divs.length >= 1 ? divs[0] : null;
|
|
91
|
+
const catText = catEl ? catEl.textContent.trim() : '';
|
|
92
|
+
const category = catText.replace(/^\\d+\\s*/, '').replace(/^\\xB7\\s*/, '').trim();
|
|
93
|
+
const extraEl = divs.length >= 3 ? divs[2] : null;
|
|
94
|
+
const extra = extraEl ? extraEl.textContent.trim() : '';
|
|
95
|
+
if (topic) {
|
|
96
|
+
items.push({ rank: items.length + 1, topic, tweets: extra || 'N/A', category });
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return items;
|
|
100
|
+
})()`);
|
|
101
|
+
|
|
102
|
+
if (Array.isArray(domTrends) && domTrends.length > 0) {
|
|
103
|
+
trends = domTrends;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (trends.length === 0) {
|
|
108
|
+
throw new Error('No trending data found. API may have changed or login may be required.');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return trends.slice(0, limit);
|
|
112
|
+
},
|
|
113
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import type { IPage } from '../../types.js';
|
|
3
|
+
|
|
4
|
+
cli({
|
|
5
|
+
site: 'twitter',
|
|
6
|
+
name: 'unblock',
|
|
7
|
+
description: 'Unblock a Twitter user',
|
|
8
|
+
domain: 'x.com',
|
|
9
|
+
strategy: Strategy.UI,
|
|
10
|
+
browser: true,
|
|
11
|
+
args: [
|
|
12
|
+
{ name: 'username', type: 'string', positional: true, required: true, help: 'Twitter screen name (without @)' },
|
|
13
|
+
],
|
|
14
|
+
columns: ['status', 'message'],
|
|
15
|
+
func: async (page: IPage | null, kwargs: any) => {
|
|
16
|
+
if (!page) throw new Error('Requires browser');
|
|
17
|
+
const username = kwargs.username.replace(/^@/, '');
|
|
18
|
+
|
|
19
|
+
await page.goto(`https://x.com/${username}`);
|
|
20
|
+
await page.wait(5);
|
|
21
|
+
|
|
22
|
+
const result = await page.evaluate(`(async () => {
|
|
23
|
+
try {
|
|
24
|
+
let attempts = 0;
|
|
25
|
+
let unblockBtn = null;
|
|
26
|
+
|
|
27
|
+
while (attempts < 20) {
|
|
28
|
+
// Check if not blocked (follow button visible means not blocked)
|
|
29
|
+
const followBtn = document.querySelector('[data-testid$="-follow"]');
|
|
30
|
+
if (followBtn) {
|
|
31
|
+
return { ok: true, message: 'Not blocking @${username} (already unblocked).' };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
unblockBtn = document.querySelector('[data-testid$="-unblock"]');
|
|
35
|
+
if (unblockBtn) break;
|
|
36
|
+
|
|
37
|
+
await new Promise(r => setTimeout(r, 500));
|
|
38
|
+
attempts++;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!unblockBtn) {
|
|
42
|
+
return { ok: false, message: 'Could not find Unblock button. Are you logged in?' };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Click the unblock button — this opens a confirmation dialog
|
|
46
|
+
unblockBtn.click();
|
|
47
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
48
|
+
|
|
49
|
+
// Confirm the unblock in the dialog
|
|
50
|
+
const confirmBtn = document.querySelector('[data-testid="confirmationSheetConfirm"]');
|
|
51
|
+
if (confirmBtn) {
|
|
52
|
+
confirmBtn.click();
|
|
53
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Verify
|
|
57
|
+
const verify = document.querySelector('[data-testid$="-follow"]');
|
|
58
|
+
if (verify) {
|
|
59
|
+
return { ok: true, message: 'Successfully unblocked @${username}.' };
|
|
60
|
+
} else {
|
|
61
|
+
return { ok: false, message: 'Unblock action initiated but UI did not update.' };
|
|
62
|
+
}
|
|
63
|
+
} catch (e) {
|
|
64
|
+
return { ok: false, message: e.toString() };
|
|
65
|
+
}
|
|
66
|
+
})()`);
|
|
67
|
+
|
|
68
|
+
if (result.ok) await page.wait(2);
|
|
69
|
+
|
|
70
|
+
return [{
|
|
71
|
+
status: result.ok ? 'success' : 'failed',
|
|
72
|
+
message: result.message
|
|
73
|
+
}];
|
|
74
|
+
}
|
|
75
|
+
});
|
package/src/clis/v2ex/topic.yaml
CHANGED
package/src/clis/weibo/hot.ts
CHANGED
|
@@ -17,7 +17,6 @@ cli({
|
|
|
17
17
|
func: async (page, kwargs) => {
|
|
18
18
|
const count = Math.min(kwargs.limit || 30, 50);
|
|
19
19
|
await page.goto('https://weibo.com');
|
|
20
|
-
await page.wait(2);
|
|
21
20
|
const data = await page.evaluate(`
|
|
22
21
|
(async () => {
|
|
23
22
|
const resp = await fetch('/ajax/statuses/hot_band', {credentials: 'include'});
|
package/src/clis/weread/book.ts
CHANGED
|
@@ -9,7 +9,7 @@ cli({
|
|
|
9
9
|
domain: 'weread.qq.com',
|
|
10
10
|
strategy: Strategy.COOKIE,
|
|
11
11
|
args: [
|
|
12
|
-
{ name: '
|
|
12
|
+
{ name: 'book-id', positional: true, required: true, help: 'Book ID (numeric, from search or shelf results)' },
|
|
13
13
|
],
|
|
14
14
|
columns: ['title', 'author', 'publisher', 'intro', 'category', 'rating'],
|
|
15
15
|
func: async (page: IPage, args) => {
|
|
@@ -9,7 +9,7 @@ cli({
|
|
|
9
9
|
domain: 'weread.qq.com',
|
|
10
10
|
strategy: Strategy.COOKIE,
|
|
11
11
|
args: [
|
|
12
|
-
{ name: '
|
|
12
|
+
{ name: 'book-id', positional: true, required: true, help: 'Book ID (from shelf or search results)' },
|
|
13
13
|
{ name: 'limit', type: 'int', default: 20, help: 'Max results' },
|
|
14
14
|
],
|
|
15
15
|
columns: ['chapter', 'text', 'createTime'],
|
package/src/clis/weread/notes.ts
CHANGED
|
@@ -9,7 +9,7 @@ cli({
|
|
|
9
9
|
domain: 'weread.qq.com',
|
|
10
10
|
strategy: Strategy.COOKIE,
|
|
11
11
|
args: [
|
|
12
|
-
{ name: '
|
|
12
|
+
{ name: 'book-id', positional: true, required: true, help: 'Book ID (from shelf or search results)' },
|
|
13
13
|
{ name: 'limit', type: 'int', default: 20, help: 'Max results' },
|
|
14
14
|
],
|
|
15
15
|
columns: ['chapter', 'text', 'review', 'createTime'],
|
|
@@ -9,7 +9,7 @@ cli({
|
|
|
9
9
|
strategy: Strategy.PUBLIC,
|
|
10
10
|
browser: false,
|
|
11
11
|
args: [
|
|
12
|
-
{ name: '
|
|
12
|
+
{ name: 'query', positional: true, required: true, help: 'Search keyword' },
|
|
13
13
|
{ name: 'limit', type: 'int', default: 10, help: 'Max results' },
|
|
14
14
|
],
|
|
15
15
|
columns: ['rank', 'title', 'author', 'bookId'],
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { CliError } from '../../errors.js';
|
|
3
|
+
import { wikiFetch } from './utils.js';
|
|
4
|
+
|
|
5
|
+
interface WikiSearchResult { title: string; snippet: string; }
|
|
6
|
+
|
|
7
|
+
cli({
|
|
8
|
+
site: 'wikipedia',
|
|
9
|
+
name: 'search',
|
|
10
|
+
description: 'Search Wikipedia articles',
|
|
11
|
+
strategy: Strategy.PUBLIC,
|
|
12
|
+
browser: false,
|
|
13
|
+
args: [
|
|
14
|
+
{ name: 'query', positional: true, required: true, help: 'Search keyword' },
|
|
15
|
+
{ name: 'limit', type: 'int', default: 10, help: 'Max results' },
|
|
16
|
+
{ name: 'lang', default: 'en', help: 'Language code (e.g. en, zh, ja)' },
|
|
17
|
+
],
|
|
18
|
+
columns: ['title', 'snippet', 'url'],
|
|
19
|
+
func: async (_page, args) => {
|
|
20
|
+
const limit = Math.max(1, Math.min(Number(args.limit), 50));
|
|
21
|
+
const lang = args.lang || 'en';
|
|
22
|
+
const q = encodeURIComponent(args.keyword);
|
|
23
|
+
const data = await wikiFetch(lang, `/w/api.php?action=query&list=search&srsearch=${q}&srlimit=${limit}&format=json&utf8=1`) as { query?: { search?: WikiSearchResult[] } };
|
|
24
|
+
const results = data?.query?.search;
|
|
25
|
+
if (!results?.length) throw new CliError('NOT_FOUND', 'No articles found', 'Try a different keyword');
|
|
26
|
+
return results.map((r) => ({
|
|
27
|
+
title: r.title,
|
|
28
|
+
snippet: r.snippet.replace(/<[^>]+>/g, '').slice(0, 120),
|
|
29
|
+
url: `https://${lang}.wikipedia.org/wiki/${encodeURIComponent(r.title.replace(/ /g, '_'))}`,
|
|
30
|
+
}));
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { CliError } from '../../errors.js';
|
|
3
|
+
import { wikiFetch } from './utils.js';
|
|
4
|
+
|
|
5
|
+
cli({
|
|
6
|
+
site: 'wikipedia',
|
|
7
|
+
name: 'summary',
|
|
8
|
+
description: 'Get Wikipedia article summary',
|
|
9
|
+
strategy: Strategy.PUBLIC,
|
|
10
|
+
browser: false,
|
|
11
|
+
args: [
|
|
12
|
+
{ name: 'title', positional: true, required: true, help: 'Article title (e.g. "Transformer (machine learning model)")' },
|
|
13
|
+
{ name: 'lang', default: 'en', help: 'Language code (e.g. en, zh, ja)' },
|
|
14
|
+
],
|
|
15
|
+
columns: ['title', 'description', 'extract', 'url'],
|
|
16
|
+
func: async (_page, args) => {
|
|
17
|
+
const lang = args.lang || 'en';
|
|
18
|
+
const title = encodeURIComponent(args.title.replace(/ /g, '_'));
|
|
19
|
+
const data = await wikiFetch(lang, `/api/rest_v1/page/summary/${title}`) as { title?: string; description?: string; extract?: string; content_urls?: { desktop?: { page?: string } } };
|
|
20
|
+
if (!data?.title) throw new CliError('NOT_FOUND', `Article "${args.title}" not found`, 'Try searching first: opencli wikipedia search <keyword>');
|
|
21
|
+
return [{
|
|
22
|
+
title: data.title,
|
|
23
|
+
description: data.description ?? '-',
|
|
24
|
+
extract: (data.extract ?? '').slice(0, 300),
|
|
25
|
+
url: data.content_urls?.desktop?.page ?? `https://${lang}.wikipedia.org/wiki/${title}`,
|
|
26
|
+
}];
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wikipedia adapter utilities.
|
|
3
|
+
*
|
|
4
|
+
* Uses the public MediaWiki REST API and Action API — no key required.
|
|
5
|
+
* REST API: https://en.wikipedia.org/api/rest_v1/
|
|
6
|
+
* Action API: https://en.wikipedia.org/w/api.php
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { CliError } from '../../errors.js';
|
|
10
|
+
|
|
11
|
+
export async function wikiFetch(lang: string, path: string): Promise<unknown> {
|
|
12
|
+
const url = `https://${lang}.wikipedia.org${path}`;
|
|
13
|
+
const resp = await fetch(url, {
|
|
14
|
+
headers: { 'User-Agent': 'opencli/1.0 (https://github.com/jackwener/opencli)' },
|
|
15
|
+
});
|
|
16
|
+
if (!resp.ok) {
|
|
17
|
+
throw new CliError('FETCH_ERROR', `Wikipedia API HTTP ${resp.status}`, `Check your title or search term`);
|
|
18
|
+
}
|
|
19
|
+
return resp.json();
|
|
20
|
+
}
|