diff --git a/README.md b/README.md index f01dee0..c6f3a10 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ Agent Eyes 把最好的开源工具粘在一起,一次安装全部搞定。 | 平台 | 能力 | 配置难度 | 说明 | |------|------|:--------:|------| | 🌐 **网页** | 阅读 | 零配置 | 任意 URL → 干净 Markdown([Jina Reader](https://github.com/jina-ai/reader) ⭐9.8K 驱动) | -| 🐦 **Twitter/X** | 阅读 · 搜索 · **发推 · 回复** | Cookie | 不花 $100/月 Twitter API,浏览器 Cookie 就能玩转([birdx](https://github.com/runesleo/birdx) 驱动) | +| 🐦 **Twitter/X** | 阅读 · 搜索 | 零配置 / Cookie | 单条推文零配置可读(Jina Reader)。装 [birdx](https://github.com/runesleo/birdx) + Cookie 解锁搜索、时间线、发推 | | 📕 **小红书** | 阅读 · 搜索 · **发帖 · 评论 · 点赞 · 收藏** | Cookie | 完整操作能力:发图文/视频笔记、回复评论、查看用户主页 | | 🔍 **全网搜索** | 搜索 | 免费 Key | AI 语义搜索,一个 Key 搜全网 + Reddit + Twitter([Exa](https://exa.ai) 驱动) | -| 📦 **GitHub** | 阅读 · 搜索 · **创建 Issue/PR · 评论 · Review · Fork · Star** | 零配置 | 完整 GitHub 操作([gh CLI](https://cli.github.com/) 驱动) | +| 📦 **GitHub** | 阅读 · 搜索 | 零配置 | 公开仓库代码、README、搜索([GitHub API](https://docs.github.com/en/rest) 驱动)。设 token 可访问私有仓库 | | 📺 **YouTube** | 阅读 | 零配置 | 1800+ 视频网站字幕提取([yt-dlp](https://github.com/yt-dlp/yt-dlp) ⭐148K 驱动) | | 📺 **B站** | 阅读 | 零配置 / 代理 | 视频信息 + 字幕。本地直接用,服务器需代理 | | 📡 **RSS** | 阅读 | 零配置 | 任意 RSS/Atom 源([feedparser](https://github.com/kurtmckee/feedparser) ⭐2.3K 驱动) | @@ -109,11 +109,24 @@ agent-eyes configure proxy http://用户名:密码@IP:端口 $ agent-eyes doctor 👁️ Agent Eyes Status -✅ Web [Jina Reader] ✅ GitHub [API] ✅ RSS [feedparser] -✅ YouTube [yt-dlp] ✅ Bilibili [API] ✅ Twitter [birdx] -⬜ Search [need Exa key] ⬜ XiaoHongShu [cookies] ⬜ Reddit [proxy] +======================================== -6/9 active +✅ Ready (no setup needed): + ✅ GitHub repos and code — Public repos only + ✅ Twitter/X posts — Full access (search + timeline + threads) + ✅ YouTube video transcripts — yt-dlp + ⚠️ Bilibili video info — May be blocked on servers + ✅ RSS and Atom feeds — feedparser + ✅ Web pages (any URL) — Jina Reader API + +🔍 Search (need free Exa API key): + ⬜ Semantic web search + +🔧 Optional (advanced setup): + ⬜ Reddit posts and comments — Need proxy + ⬜ XiaoHongShu (小红书) — Need cookies + +Status: 6/9 channels active ``` --- diff --git a/agent_eyes/channels/bilibili.py b/agent_eyes/channels/bilibili.py index a44c938..1bb9724 100644 --- a/agent_eyes/channels/bilibili.py +++ b/agent_eyes/channels/bilibili.py @@ -20,6 +20,23 @@ class BilibiliChannel(Channel): domain = urlparse(url).netloc.lower() return "bilibili.com" in domain or "b23.tv" in domain + def check(self, config=None): + proxy = config.get("bilibili_proxy") if config else None + if proxy: + return "ok", "Via proxy" + # Detect if we're on a server (same logic as cli._detect_environment) + import os + indicators = [ + os.path.exists("/var/run/docker.sock"), + os.path.exists("/etc/cloud"), + "SSH_CONNECTION" in os.environ, + "container" in os.environ.get("container", ""), + ] + is_server = any(indicators) + if is_server: + return "warn", "May be blocked on servers. Fix: agent-eyes configure proxy URL" + return "ok", "Local access" + async def read(self, url: str, config=None) -> ReadResult: # Proxy support (Bilibili blocks server IPs) proxy = config.get("bilibili_proxy") if config else None diff --git a/agent_eyes/channels/github.py b/agent_eyes/channels/github.py index 000840d..65ae5a7 100644 --- a/agent_eyes/channels/github.py +++ b/agent_eyes/channels/github.py @@ -13,7 +13,7 @@ from typing import List class GitHubChannel(Channel): name = "github" - description = "GitHub repos, issues, PRs, code" + description = "GitHub repos and code" backends = ["GitHub API"] tier = 0 @@ -26,6 +26,12 @@ class GitHubChannel(Channel): h["Authorization"] = f"Bearer {token}" return h + def check(self, config=None): + token = config.get("github_token") if config else None + if token: + return "ok", "Full access (authenticated)" + return "ok", "Public repos only. Set github_token for private repos + higher rate limits" + def can_handle(self, url: str) -> bool: domain = urlparse(url).netloc.lower() return "github.com" in domain diff --git a/agent_eyes/channels/twitter.py b/agent_eyes/channels/twitter.py index 99ccb26..64221c6 100644 --- a/agent_eyes/channels/twitter.py +++ b/agent_eyes/channels/twitter.py @@ -16,7 +16,7 @@ import requests class TwitterChannel(Channel): name = "twitter" - description = "Twitter/X posts, search, timelines" + description = "Twitter/X posts" backends = ["birdx", "Jina Reader"] tier = 0 # Single tweet reading is zero-config @@ -27,7 +27,8 @@ class TwitterChannel(Channel): def check(self, config=None): # Basic reading always works (Jina fallback) if shutil.which("birdx"): - return "ok", "birdx (full: search + timeline + threads)" + return "ok", "Full access (search + timeline + threads)" + return "ok", "Read-only (single tweets via Jina). Install birdx for search + timelines" return "ok", "Jina Reader (single tweets only)" async def read(self, url: str, config=None) -> ReadResult: diff --git a/agent_eyes/doctor.py b/agent_eyes/doctor.py index becee0d..07d4ac0 100644 --- a/agent_eyes/doctor.py +++ b/agent_eyes/doctor.py @@ -37,11 +37,13 @@ def format_report(results: Dict[str, dict]) -> str: lines.append("") lines.append("✅ Ready (no setup needed):") for key, r in results.items(): - if r["tier"] == 0 and r["status"] == "ok": - backends = ", ".join(r["backends"]) if r["backends"] else "built-in" - lines.append(f" ✅ {r['name']} [{backends}]") - elif r["tier"] == 0 and r["status"] in ("warn", "off"): - lines.append(f" ⚠️ {r['name']} — {r['message']}") + if r["tier"] == 0: + if r["status"] == "ok": + lines.append(f" ✅ {r['name']} — {r['message']}") + elif r["status"] == "warn": + lines.append(f" ⚠️ {r['name']} — {r['message']}") + elif r["status"] in ("off", "error"): + lines.append(f" ❌ {r['name']} — {r['message']}") # Tier 1 — needs free key tier1 = {k: r for k, r in results.items() if r["tier"] == 1} diff --git a/tests/test_doctor.py b/tests/test_doctor.py index e0a92a6..f108617 100644 --- a/tests/test_doctor.py +++ b/tests/test_doctor.py @@ -16,7 +16,7 @@ class TestDoctor: results = check_all(tmp_config) assert results["web"]["status"] == "ok" assert results["github"]["status"] == "ok" - assert results["bilibili"]["status"] == "ok" + assert results["bilibili"]["status"] in ("ok", "warn") # warn on servers assert results["rss"]["status"] == "ok" def test_exa_off_without_key(self, tmp_config):