目标:让 AI/终端能读企业微信里的文档(日报、周报等)。2026-10-20 实战总结,包含走过的弯路。 一句话结论:别自己拼 API,用官方
wecom-cli;它一次扫码授权后,能搜文档、读正文。
# 1. 装(命令名 wecom-cli,npm 包名是 @wecom/cli)
npm install -g @wecom/cli
# 2. 授权(只需一次,扫码;5 分钟内有效)
wecom-cli auth init
# 非交互环境(脚本/agent)用这条,会把二维码存成 PNG:
wecom-cli auth init --noninteractive --no-browser --output-qrcode /tmp/wecom-qr.png
# 确认状态(应显示 authorized + Bot ID)
wecom-cli auth show
wecom-cli auth show --status # 只输出 authorized / unauthorized
# 3. 按名字搜文档(这是原生 API 做不到的事)
wecom-cli doc search --keywords 日报 --limit 20
wecom-cli doc search --keywords 申亮工作日报 --search-scope title
# 4. 读内容
# 普通文档(doc):走 doc contents
# 智能文档(smartpage):先看页面结构,再按 page_id 取 markdown
wecom-cli smartpage pages get --docid <docid>
wecom-cli smartpage pages get --docid <docid> --page-id <page_id>
doc search 返回的关键字段:docid、doc_type(doc / sheet / smartsheet / smartpage / ppt)、doc_name、creator_userid_name、modify_time、url、title_highlight / text_highlight(带 <em> 高亮)。
doc_type 决定后面用哪个子命令读——智能文档必须用 smartpage,用 doc 是读不出来的。
| # | 坑 | 真相 |
|---|---|---|
| 1 | 以为没有 CLI,先手拼 HTTP API | 官方有 CLI,只是包名 @wecom/cli ≠ 命令名 wecom-cli,按 wecom / wxwork 搜 PATH 找不到 |
| 2 | 本机直连 qyapi.weixin.qq.com 报错 |
errcode 60020 not allow to access from your ip:自建应用配了可信 IP 白名单,只放行服务器 IP。换机器 → 换 IP → 直接被拒 |
| 3 | 以为自建应用的 Secret 能喂给 CLI | 两套凭证:CLI 要的是智能机器人的 Bot ID + Secret(或扫码绑定),自建应用的 corpid/agentid/secret 是另一回事 |
| 4 | 想按名字找文档 | 企业微信文档 API 没有「列文档 / 搜文档」接口:wedoc/document/list、wedoc/doc/list、wedoc/search 全部 404 或空返回 |
| 5 | 想从微盘翻文件 | wedrive/space/list、wedrive/file/list 全 48002 api forbidden(应用没开微盘权限),且微盘是文件而非在线文档 |
| 6 | 文档名对不上 | 「申亮的工作日报」实际叫「申亮工作日报」,且是 smartpage 不是 doc;名字差一个字、类型猜错就白跑 |
| 7 | 往文档里写多行 | API 的 insert_text 里 \n 不换行(渲染成一整段);往同一 index 反复插会倒序堆叠。要分行必须逐行 insert_paragraph:先在最后一段 end 处插空段,再在新段 begin 处 insert_text |
| 8 | auth init 在 agent 里卡住 |
交互式要终端;非 TTY 必须加 --noninteractive,二维码用 --output-qrcode <png> 落盘给人扫 |
扫码绑定后,CLI 以机器人身份代真人执行:
有,但只在这些场景:需要无人值守(不想依赖某个人扫码)、或要批量写进某个固定文档(比如每天写日报)。
# 必须在可信 IP 的机器上(本例:生产服务器)执行
T=$(curl -s "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=<CORPID>&corpsecret=<SECRET>" | jq -r .access_token)
curl -s -X POST "https://qyapi.weixin.qq.com/cgi-bin/wedoc/create_doc?access_token=$T" -H 'Content-Type: application/json' -d '{"doc_type":3,"doc_name":"标题"}'
curl -s -X POST "https://qyapi.weixin.qq.com/cgi-bin/wedoc/document/batch_update?access_token=$T" -H 'Content-Type: application/json' -d '{"docid":"...","requests":[{"insert_paragraph":{"location":{"index":10},"paragraph":{"elements":[{"text_run":{"text":"一行"}}]}}}]}'
curl -s -X POST "https://qyapi.weixin.qq.com/cgi-bin/wedoc/del_doc?access_token=$T" -H 'Content-Type: application/json' -d '{"docid":"..."}'
已验证可用:wedoc/create_doc、wedoc/document/get、wedoc/document/batch_update、wedoc/del_doc。
不存在的(别浪费时间):wedoc/document/delete、wedoc/doc/*、wedoc/search、wedoc/document/list。
.env。wecom-cli --help # 看全部服务
wecom-cli doc --help # 文档服务下有哪些方法
wecom-cli doc search --help # 参数(关键词/类型/创建者/时间/排序/分页)
wecom-cli smartpage pages get --help # 读智能文档
wecom-cli schema list # 所有服务的方法 schema
wecom-cli cache clear # 清 discovery 缓存(改了配置没生效时)
配置落在 ~/.config/wecom/(可用 WECOM_CLI_CONFIG_DIR 覆盖),凭据加密存在 credentials.enc。
D=a1_Af8AaQaNAIoCNDWNsx7JjT22K1AL1 # 「申亮工作日报」docid(smartpage)
wecom-cli smartpage pages get --docid $D # 看现有页与 parent_id(根页 x7a87i)
wecom-cli smartpage pages update --docid $D \
--create-page '{"index":2,"page_name":"10 月 20 日","parent_page_id":"x7a87i"}'
# → {"page_id":"UuGm9t", ...}
wecom-cli smartpage pages append --docid $D --page-id UuGm9t \
--content-type markdown --file-path /tmp/report_1020.md
wecom-cli smartpage pages update --docid $D \
--move-page '{"page_id":"UuGm9t","index":3,"new_parent_page_id":"x7a87i"}' # 挪到最后
再补两条新坑:
| 现象 | 正确做法 |
|---|---|
append 报 字段 'content' 必填且不能为空 |
--content-path / --content_path 都无效;要用 --file-path /abs/x.md,框架读文件后上传 |
append 报 'content' 类型不匹配,应为对象 |
别用 --json 自己塞 content 字符串;append/overwrite 的 schema 里只有 content_path / file_path |
| 新页跑到中间去了 | --create-page 的 index 是「插到第几位」,不是追加;建完用 --move-page 按 index 挪 |
写进去的内容格式:markdown 直传,- [x] / - [ ] 会渲染成可勾选的待办,#### 标题 渲染成小节标题,与既有页面风格一致。