fix(wechat): switch search from miku_ai to Exa, upgrade to tier 0 (#233)
miku_ai (Sogou WeChat search) has been unmaintained for 20 months. Exa can search AND read WeChat articles via mp.weixin.qq.com domain filtering — verified: search returns articles, crawling returns full text. Jina Reader fails on WeChat (CAPTCHA block). - wechat.py: check() now detects Exa (primary) + Camoufox (optional) - tier downgraded from 2 to 0 (Exa is zero-config) - skill/references/web.md: updated WeChat commands to use Exa - 104 tests passing Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""WeChat Official Account articles — read and search.
|
"""WeChat Official Account articles — read and search.
|
||||||
|
|
||||||
Read: wechat-article-for-ai (Camoufox stealth browser)
|
Read: Exa crawling (primary) / Camoufox stealth browser (optional)
|
||||||
Search: miku_ai (Sogou WeChat search)
|
Search: Exa web_search with includeDomains mp.weixin.qq.com
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import shutil
|
import shutil
|
||||||
@@ -10,11 +10,25 @@ import subprocess
|
|||||||
from .base import Channel
|
from .base import Channel
|
||||||
|
|
||||||
|
|
||||||
|
def _exa_available() -> bool:
|
||||||
|
mcporter = shutil.which("mcporter")
|
||||||
|
if not mcporter:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
r = subprocess.run(
|
||||||
|
[mcporter, "config", "list"],
|
||||||
|
capture_output=True, text=True, timeout=5,
|
||||||
|
)
|
||||||
|
return "exa" in r.stdout.lower()
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class WeChatChannel(Channel):
|
class WeChatChannel(Channel):
|
||||||
name = "wechat"
|
name = "wechat"
|
||||||
description = "微信公众号文章"
|
description = "微信公众号文章"
|
||||||
backends = ["wechat-article-for-ai (Camoufox)", "miku_ai (搜狗搜索)"]
|
backends = ["Exa via mcporter (搜索+阅读)", "Camoufox (可选阅读)"]
|
||||||
tier = 2
|
tier = 0
|
||||||
|
|
||||||
def can_handle(self, url: str) -> bool:
|
def can_handle(self, url: str) -> bool:
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
@@ -22,36 +36,28 @@ class WeChatChannel(Channel):
|
|||||||
return "mp.weixin.qq.com" in d or "weixin.qq.com" in d
|
return "mp.weixin.qq.com" in d or "weixin.qq.com" in d
|
||||||
|
|
||||||
def check(self, config=None):
|
def check(self, config=None):
|
||||||
has_read = False
|
has_exa = _exa_available()
|
||||||
has_search = False
|
has_camoufox = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import camoufox # noqa: F401
|
import camoufox # noqa: F401
|
||||||
has_read = True
|
has_camoufox = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
if has_exa and has_camoufox:
|
||||||
import miku_ai # noqa: F401
|
return "ok", "完整可用(Exa 搜索 + Exa/Camoufox 阅读公众号文章)"
|
||||||
has_search = True
|
elif has_exa:
|
||||||
except ImportError:
|
return "ok", (
|
||||||
pass
|
"通过 Exa 搜索和阅读微信公众号文章(免费,无需额外配置)。"
|
||||||
|
"可选安装 Camoufox 获得更好的全文阅读效果。"
|
||||||
if has_read and has_search:
|
)
|
||||||
return "ok", "完整可用(搜索 + 阅读公众号文章)"
|
elif has_camoufox:
|
||||||
elif has_read:
|
|
||||||
return "ok", "可阅读公众号文章(URL → Markdown)。安装 miku_ai 可解锁搜索:pip install miku_ai"
|
|
||||||
elif has_search:
|
|
||||||
return "warn", (
|
return "warn", (
|
||||||
"可搜索公众号文章但无法阅读全文。安装阅读工具:\n"
|
"Camoufox 可阅读公众号文章,但搜索功能需要 Exa。"
|
||||||
" pip install camoufox[geoip] markdownify beautifulsoup4 httpx mcp"
|
"运行 `agent-reach install --env=auto` 安装 Exa。"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return "off", (
|
return "off", (
|
||||||
"需要安装微信公众号工具:\n"
|
"需要 mcporter + Exa MCP 来搜索和阅读微信公众号文章。\n"
|
||||||
" # 阅读(URL → Markdown):\n"
|
"运行 `agent-reach install --env=auto` 安装。"
|
||||||
" pip install camoufox[geoip] markdownify beautifulsoup4 httpx mcp\n"
|
|
||||||
" # 搜索(关键词 → 文章列表):\n"
|
|
||||||
" pip install miku_ai\n"
|
|
||||||
" 详见 https://github.com/bzd6661/wechat-article-for-ai"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,28 +31,27 @@ mcporter call 'web-reader.webReader(url: "https://example.com", return_format: "
|
|||||||
|
|
||||||
## 微信公众号 / WeChat Articles
|
## 微信公众号 / WeChat Articles
|
||||||
|
|
||||||
**注意**: 微信公众号文章无法用 Jina Reader 或 curl 直接读取,必须使用专用工具。
|
### 搜索公众号文章(通过 Exa)
|
||||||
|
|
||||||
### 搜索文章 (miku_ai)
|
```bash
|
||||||
|
# 搜索微信公众号文章
|
||||||
```python
|
mcporter call 'exa.web_search_exa(query: "搜索关键词", numResults: 5, includeDomains: ["mp.weixin.qq.com"])'
|
||||||
python3 -c "
|
|
||||||
import asyncio
|
|
||||||
from miku_ai import get_wexin_article
|
|
||||||
async def s():
|
|
||||||
for a in await get_wexin_article('query', 5):
|
|
||||||
print(f'{a[\"title\"]} | {a[\"url\"]}')
|
|
||||||
asyncio.run(s())
|
|
||||||
"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 读取文章 (Camoufox - 绕过微信反爬)
|
### 阅读公众号文章全文(通过 Exa)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 抓取文章全文
|
||||||
|
mcporter call 'exa.crawling_exa(urls: ["https://mp.weixin.qq.com/s/ARTICLE_ID"], maxCharacters: 10000)'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 可选:Camoufox 阅读(反爬更强)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/.agent-reach/tools/wechat-article-for-ai && python3 main.py "https://mp.weixin.qq.com/s/ARTICLE_ID"
|
cd ~/.agent-reach/tools/wechat-article-for-ai && python3 main.py "https://mp.weixin.qq.com/s/ARTICLE_ID"
|
||||||
```
|
```
|
||||||
|
|
||||||
> **重要**: 微信文章必须用 Camoufox 读取,其他方法会失败。
|
> **注意**: Jina Reader 无法读取微信文章(被 CAPTCHA 拦截),推荐用 Exa。
|
||||||
|
|
||||||
## RSS (feedparser)
|
## RSS (feedparser)
|
||||||
|
|
||||||
@@ -72,6 +71,6 @@ for e in feedparser.parse('FEED_URL').entries[:5]:
|
|||||||
|-----|---------|
|
|-----|---------|
|
||||||
| 通用网页 | Jina Reader (`curl r.jina.ai`) |
|
| 通用网页 | Jina Reader (`curl r.jina.ai`) |
|
||||||
| 需要图片/格式控制 | web-reader MCP |
|
| 需要图片/格式控制 | web-reader MCP |
|
||||||
| 微信公众号 | Camoufox (读取) + miku_ai (搜索) |
|
| 微信公众号 | Exa (搜索+阅读) / Camoufox (可选阅读) |
|
||||||
| RSS 订阅 | feedparser |
|
| RSS 订阅 | feedparser |
|
||||||
| 微博/知乎等 | Jina Reader |
|
| 微博/知乎等 | Jina Reader |
|
||||||
|
|||||||
Reference in New Issue
Block a user