diff --git a/README.zh-CN.md b/README.zh-CN.md index 624760a7..a24cb9b6 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -178,6 +178,8 @@ npm install -g @jackwener/opencli@latest | **producthunt** | `posts` `today` `hot` `browse` | 公开 / 浏览器 | | **instagram** | `explore` `profile` `search` `user` `followers` `following` `follow` `unfollow` `like` `unlike` `comment` `save` `unsave` `saved` | 浏览器 | | **lobsters** | `hot` `newest` `active` `tag` | 公开 | +| **qiita** | `search` `user` `articles` | 公开 | +| **note.com** | `search` `user` `articles` | 公开 / 浏览器 | | **medium** | `feed` `search` `user` | 浏览器 | | **sinablog** | `hot` `search` `article` `user` | 浏览器 | | **substack** | `feed` `search` `publication` | 浏览器 | diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 5b4a58cd..35829deb 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -108,6 +108,8 @@ export default defineConfig({ { text: 'Wikipedia', link: '/adapters/browser/wikipedia' }, { text: 'Lobsters', link: '/adapters/browser/lobsters' }, { text: 'Steam', link: '/adapters/browser/steam' }, + { text: 'Qiita', link: '/adapters/browser/qiita' }, + { text: 'note.com', link: '/adapters/browser/note' }, ], }, { diff --git a/docs/adapters/browser/bluesky.md b/docs/adapters/browser/bluesky.md index 6e519f3b..b0063122 100644 --- a/docs/adapters/browser/bluesky.md +++ b/docs/adapters/browser/bluesky.md @@ -10,6 +10,7 @@ | `opencli bluesky user` | Recent posts from a user | | `opencli bluesky trending` | Trending topics | | `opencli bluesky search` | Search users | +| `opencli bluesky search-posts` | Search posts | | `opencli bluesky feeds` | Popular feed generators | | `opencli bluesky followers` | User's followers | | `opencli bluesky following` | Accounts a user follows | @@ -44,6 +45,9 @@ opencli bluesky thread --uri "at://did:.../app.bsky.feed.post/..." # Starter packs opencli bluesky starter-packs --handle bsky.app +# Search posts +opencli bluesky search-posts "AI agent" --limit 10 + # JSON output opencli bluesky profile --handle bsky.app -f json ``` diff --git a/docs/adapters/browser/note.md b/docs/adapters/browser/note.md new file mode 100644 index 00000000..0de8ae12 --- /dev/null +++ b/docs/adapters/browser/note.md @@ -0,0 +1,32 @@ +# note.com + +**Mode**: 🌐 / 🔐 · **Domain**: `note.com` + +## Commands + +| Command | Description | Mode | +|---------|-------------|------| +| `opencli note search ` | Search note.com articles | 🔐 Browser | +| `opencli note user ` | Get note.com user profile | 🌐 Public | +| `opencli note articles ` | List articles by a note.com user | 🌐 Public | + +## Usage Examples + +```bash +# Search articles (requires browser + login) +opencli note search "AI" --limit 5 + +# User profile +opencli note user masuyohasiri + +# User's articles +opencli note articles masuyohasiri --limit 6 + +# JSON output +opencli note user masuyohasiri -f json +``` + +## Prerequisites + +- `user` and `articles` commands use public API — no browser required +- `search` command requires the opencli Browser Bridge extension and a logged-in note.com session diff --git a/docs/adapters/browser/qiita.md b/docs/adapters/browser/qiita.md new file mode 100644 index 00000000..53e6aadc --- /dev/null +++ b/docs/adapters/browser/qiita.md @@ -0,0 +1,31 @@ +# Qiita + +**Mode**: 🌐 Public · **Domain**: `qiita.com` + +## Commands + +| Command | Description | +|---------|-------------| +| `opencli qiita search ` | Search Qiita articles | +| `opencli qiita user ` | Get Qiita user profile | +| `opencli qiita articles ` | List articles by a Qiita user | + +## Usage Examples + +```bash +# Search articles +opencli qiita search "ChatGPT" --limit 5 + +# User profile +opencli qiita user jnchito + +# User's articles +opencli qiita articles jnchito --limit 10 + +# JSON output +opencli qiita search "LLM" -f json +``` + +## Prerequisites + +- No browser required — uses Qiita public API v2 diff --git a/docs/adapters/index.md b/docs/adapters/index.md index 7a82f177..fbc1a24d 100644 --- a/docs/adapters/index.md +++ b/docs/adapters/index.md @@ -66,6 +66,8 @@ Run `opencli list` for the live registry. | **[wikipedia](/adapters/browser/wikipedia)** | `search` `summary` `random` `trending` | 🌐 Public | | **[lobsters](/adapters/browser/lobsters)** | `hot` `newest` `active` `tag` | 🌐 Public | | **[steam](/adapters/browser/steam)** | `top-sellers` | 🌐 Public | +| **[qiita](/adapters/browser/qiita)** | `search` `user` `articles` | 🌐 Public | +| **[note.com](/adapters/browser/note)** | `search` `user` `articles` | 🌐 / 🔐 | ## Desktop Adapters diff --git a/src/clis/bluesky/search-posts.yaml b/src/clis/bluesky/search-posts.yaml new file mode 100644 index 00000000..b49d61a4 --- /dev/null +++ b/src/clis/bluesky/search-posts.yaml @@ -0,0 +1,38 @@ +site: bluesky +name: search-posts +description: Search Bluesky posts +domain: public.api.bsky.app +strategy: public +browser: false + +args: + query: + type: str + required: true + positional: true + description: Search query + limit: + type: int + default: 25 + description: Number of results + +pipeline: + - fetch: + url: https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts + params: + q: ${{ args.query }} + limit: ${{ args.limit }} + + - select: posts + + - map: + rank: ${{ index + 1 }} + author: ${{ item.author.handle }} + text: ${{ item.record.text }} + likes: ${{ item.likeCount }} + reposts: ${{ item.repostCount }} + replies: ${{ item.replyCount }} + + - limit: ${{ args.limit }} + +columns: [rank, author, text, likes, reposts, replies] diff --git a/src/clis/note/articles.yaml b/src/clis/note/articles.yaml new file mode 100644 index 00000000..b871ea9a --- /dev/null +++ b/src/clis/note/articles.yaml @@ -0,0 +1,38 @@ +site: note +name: articles +description: List articles by a note.com user +domain: note.com +strategy: public +browser: false + +args: + username: + type: str + required: true + positional: true + description: "note.com username (urlname)" + limit: + type: int + default: 6 + description: Number of articles + +pipeline: + - fetch: + url: https://note.com/api/v2/creators/${{ args.username }}/contents + params: + kind: note + page: "1" + + - select: data.contents + + - map: + rank: ${{ index + 1 }} + title: ${{ item.name }} + likes: ${{ item.likeCount }} + comments: ${{ item.commentCount }} + created: "${{ item.publishAt ? item.publishAt.slice(0, 10) : '' }}" + url: ${{ item.noteUrl }} + + - limit: ${{ args.limit }} + +columns: [rank, title, likes, comments, created, url] diff --git a/src/clis/note/search.yaml b/src/clis/note/search.yaml new file mode 100644 index 00000000..fac14bdc --- /dev/null +++ b/src/clis/note/search.yaml @@ -0,0 +1,49 @@ +site: note +name: search +description: Search note.com articles +domain: note.com +strategy: cookie + +args: + query: + type: str + required: true + positional: true + description: Search query + limit: + type: int + default: 10 + description: Number of results + +pipeline: + - navigate: "https://note.com/search?q=${{ args.query }}&context=note&mode=search" + + - evaluate: | + (async () => { + for (let i = 0; i < 20; i++) { + await new Promise(r => setTimeout(r, 500)); + if (document.querySelectorAll('a[href*="/n/n"]').length > 0) break; + } + const links = document.querySelectorAll('a[href*="/n/n"]'); + const seen = new Set(); + const results = []; + for (const a of links) { + const href = a.getAttribute('href'); + if (!href || seen.has(href)) continue; + seen.add(href); + const title = a.textContent?.trim() || ''; + if (!title || title.length < 4) continue; + const url = href.startsWith('http') ? href : 'https://note.com' + href; + results.push({ title, url }); + } + return results; + })() + + - map: + rank: ${{ index + 1 }} + title: ${{ item.title }} + url: ${{ item.url }} + + - limit: ${{ args.limit }} + +columns: [rank, title, url] diff --git a/src/clis/note/user.yaml b/src/clis/note/user.yaml new file mode 100644 index 00000000..b5fae86d --- /dev/null +++ b/src/clis/note/user.yaml @@ -0,0 +1,30 @@ +site: note +name: user +description: Get note.com user profile +domain: note.com +strategy: public +browser: false + +args: + username: + type: str + required: true + positional: true + description: "note.com username (urlname)" + +pipeline: + - fetch: + url: https://note.com/api/v2/creators/${{ args.username }} + + - select: data + + - map: + id: ${{ item.urlname }} + name: ${{ item.nickname }} + bio: ${{ item.profile }} + followers: ${{ item.followerCount }} + following: ${{ item.followingCount }} + articles: ${{ item.noteCount }} + url: "https://note.com/${{ item.urlname }}" + +columns: [id, name, bio, followers, following, articles, url] diff --git a/src/clis/qiita/articles.yaml b/src/clis/qiita/articles.yaml new file mode 100644 index 00000000..d007e012 --- /dev/null +++ b/src/clis/qiita/articles.yaml @@ -0,0 +1,35 @@ +site: qiita +name: articles +description: List articles by a Qiita user +domain: qiita.com +strategy: public +browser: false + +args: + username: + type: str + required: true + positional: true + description: Qiita username + limit: + type: int + default: 20 + description: Number of articles + +pipeline: + - fetch: + url: https://qiita.com/api/v2/users/${{ args.username }}/items + params: + per_page: ${{ args.limit }} + + - map: + rank: ${{ index + 1 }} + title: ${{ item.title }} + likes: ${{ item.likes_count }} + tags: ${{ item.tags.map(t => t.name).join(', ') }} + created: ${{ item.created_at.slice(0, 10) }} + url: ${{ item.url }} + + - limit: ${{ args.limit }} + +columns: [rank, title, likes, tags, created, url] diff --git a/src/clis/qiita/search.yaml b/src/clis/qiita/search.yaml new file mode 100644 index 00000000..530a382b --- /dev/null +++ b/src/clis/qiita/search.yaml @@ -0,0 +1,36 @@ +site: qiita +name: search +description: Search Qiita articles +domain: qiita.com +strategy: public +browser: false + +args: + query: + type: str + required: true + positional: true + description: Search query + limit: + type: int + default: 20 + description: Number of results + +pipeline: + - fetch: + url: https://qiita.com/api/v2/items + params: + query: ${{ args.query }} + per_page: ${{ args.limit }} + + - map: + rank: ${{ index + 1 }} + title: ${{ item.title }} + author: ${{ item.user.id }} + likes: ${{ item.likes_count }} + tags: ${{ item.tags.map(t => t.name).join(', ') }} + url: ${{ item.url }} + + - limit: ${{ args.limit }} + +columns: [rank, title, author, likes, tags, url] diff --git a/src/clis/qiita/user.yaml b/src/clis/qiita/user.yaml new file mode 100644 index 00000000..332d6166 --- /dev/null +++ b/src/clis/qiita/user.yaml @@ -0,0 +1,28 @@ +site: qiita +name: user +description: Get Qiita user profile +domain: qiita.com +strategy: public +browser: false + +args: + username: + type: str + required: true + positional: true + description: Qiita username + +pipeline: + - fetch: + url: https://qiita.com/api/v2/users/${{ args.username }} + + - map: + id: ${{ item.id }} + name: ${{ item.name }} + bio: ${{ item.description }} + followers: ${{ item.followers_count }} + following: ${{ item.followees_count }} + items: ${{ item.items_count }} + url: "https://qiita.com/${{ item.id }}" + +columns: [id, name, bio, followers, following, items, url]