diff --git a/PROGRESS.md b/PROGRESS.md index c5713eb..cbf886f 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -2,7 +2,7 @@ > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。 -最后更新:2026-06-24(微信接入第一期 ClawBot 后端 + 自包含绑定页;双渠道设计 §8.7 + bump 0.22.0) +最后更新:2026-06-24(微信绑定 UI 并入主 SPA:左栏 rail「微信」按钮 + 扫码 modal + bump 0.22.2) --- @@ -21,6 +21,12 @@ ## 已完成关键能力 +### 2026-06-24 / 微信绑定 UI 并入主 SPA(bump 0.22.2) + +- 上一版绑定页是独立 `/static/wechat_bind.html`,主界面没入口、用户找不到。 +- 集成:左栏 rail 加「微信」按钮(`hd-wechat`)→ 扫码绑定 modal(`wechat-modal`),复用 `api()` 调已有 5 端点(起码/轮询/查/解绑/自检),仿 `crons.js` modal 范式;过期自动换码、绑定成功提示去微信开口。文件:`web/static/js/wechat.js`(新)、`web/static/dev.html`(rail 按钮 + modal + CSS)、`web/static/js/main.js`(import 触发绑定 + Esc 关闭)。 +- 独立页 `web/static/wechat_bind.html` 保留作嵌入/兜底入口(同套端点)。 + ### 2026-06-24 / 修复顶栏 token 计量栏回复后不刷新(bump 0.22.1) - 现象:提问→助手答完后,对话顶栏的「总 token · 缓存命中 · 花费」计量栏停在发问前旧值,要切到别的 task 再切回才更新。 @@ -34,7 +40,7 @@ - **关键设计决策**:入站对话→每用户一条 persistent「微信」task(连续性,token 靠 §8.2 压缩);凭据(bot_token/context_token)加密列(env `ZCBOT_WECHAT_SECRET_KEY`),绝不进沙箱/日志;**入站出站一体**——主动推送依赖入站给的 context_token,故 getupdates 长轮询常驻(既收对话又刷新 24h 窗口)。 - **文件**(后端全部 import/编译自测过):`core/wechat/{ilink.py 协议客户端, crypto.py 凭据加密, service.py 绑定CRUD+推送+send_to_user 渠道抽象, inbound.py 长轮询管理器+回复提取}`;`core/storage/models.py` 加 `WeChatBotBinding` + migration `0012_wechat_bot_bindings`;`tools/wechat_bot.py` `WechatPushTool` + `core/agent_builder.py` 注册(有开关才挂);`core/scheduler.py` `deliver_notify` 加 `wechat` 通道(未送达退邮件兜底);`web/app.py` lifespan 起入站管理器 + `_run_wechat_message` 回调 + 5 端点(`/v1/wechat/bind/qrcode|status`、`/v1/wechat/bind` GET/DELETE、`/v1/wechat/test`);`web/static/wechat_bind.html` 自包含绑定页;`requirements.txt` 加 segno+cryptography。 - **env**:`ZCBOT_WECHAT_BOT_ENABLED=1`(渠道开关)+ `ZCBOT_WECHAT_SECRET_KEY=<串>`(凭据加密,缺则退明文标记)+ 可选 `ZCBOT_WECHAT_BASE_URL`。 -- **待办(部署后联调)**:migration `0012` 上库;起 web 进程端到端验(扫码绑定→对话→主动推→定时简报推);**SPA 集成**绑定 UI(当前是独立 `/static/wechat_bind.html`,后续并入主 SPA 设置,仿 crons.js modal 范式);**渠道 B 企业微信**(无条件推送,补 ClawBot 24h 窗口短板)按 §8.7「渠道 B」实现。 +- **待办(部署后联调)**:migration `0012` 上库;起 web 进程端到端验(扫码绑定→对话→主动推→定时简报推);**渠道 B 企业微信**(无条件推送,补 ClawBot 24h 窗口短板)按 §8.7「渠道 B」实现。SPA 集成已落(见下条)。 ### 2026-06-23 / 平台渲染层 rendering/:三 skill docx 统一 + chromium md→pdf(bump 0.21.0) diff --git a/RUN.md b/RUN.md index fd96c75..1adfdbb 100644 --- a/RUN.md +++ b/RUN.md @@ -62,7 +62,7 @@ ``` > litellm 在 import 时副作用加载 .env;入口走 `main.py`,`.env` 自动生效。直跑 `python -c "from core.storage import ..."` 不经 litellm 链路时记得自己 `import litellm` 触发,或手动 `export ZCBOT_DB_URL=...`。 - **依赖**:`pip install -r requirements.txt`(已在 `.venv` 里;含 `bcrypt`、`segno`、`cryptography`)。 -- **微信接入(ClawBot,§8.7)**:① `main.py db upgrade head` 带上 migration `0012`;② `.env` 设 `ZCBOT_WECHAT_BOT_ENABLED=1` + `ZCBOT_WECHAT_SECRET_KEY=<串>`;③ 用户登录后开 `/static/wechat_bind.html` 扫码绑定(需个人微信 8.0.70+ 且灰度到 ClawBot 插件)。绑定后在微信「微信 ClawBot」对话即走 zcbot;**主动推送需用户近 24h 在微信开口过一次**(冷启动/超期推不出,退邮件兜底)。绑定页目前独立、未并入主 SPA。 +- **微信接入(ClawBot,§8.7)**:① `main.py db upgrade head` 带上 migration `0012`;② `.env` 设 `ZCBOT_WECHAT_BOT_ENABLED=1` + `ZCBOT_WECHAT_SECRET_KEY=<串>`;③ 用户登录后点**左栏 rail「微信」按钮**(`/static/wechat_bind.html` 仍保留作独立/嵌入入口)扫码绑定(需个人微信 8.0.70+ 且灰度到 ClawBot 插件)。绑定后在微信「微信 ClawBot」对话即走 zcbot;**主动推送需用户近 24h 在微信开口过一次**(冷启动/超期推不出,退邮件兜底)。 - **PG**:`ZCBOT_DB_URL` 必填。本地 docker compose / 远端 dev / 生产任选;未设置时启动清晰报错,不引导 docker(§7.4)。 - **Auth env**:`PLATFORM_KEY` + `JWT_SECRET` 任一缺失 web 启动 fail-fast。生成随机串:`python -c "import secrets; print(secrets.token_urlsafe(48))"`。 - **用户管理**(`users.email/password_hash/role`,0005 UNIQUE(email)、0009 role):dev SPA 登录后端。发用户两条路径任选:CLI `main.py user add`(下方),或在登录页右下角"+ 管理员添加用户"链接(需先设 `ZCBOT_ADMIN_TOKEN` env,弹窗输入 email/密码/管理员口令/角色)。撤用户 `DELETE FROM users WHERE email=...`(先 DELETE 该 user 的 tasks)。**用户自助改密**:登录后顶栏「改密码」按钮(走 `POST /v1/auth/change_password`,需知道旧密码);改邮箱 / 用户忘了旧密码无法自助 → 手动 SQL(见故障兜底)。 diff --git a/core/__init__.py b/core/__init__.py index d73cbce..96a8bf8 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,3 +1,3 @@ # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # 改版本只动这一行。 -__version__ = "0.22.1" +__version__ = "0.22.2" diff --git a/web/static/dev.html b/web/static/dev.html index f8b45c1..f791e65 100644 --- a/web/static/dev.html +++ b/web/static/dev.html @@ -293,6 +293,18 @@ } /* 定时任务 modal(只读 + 停用/删除,DESIGN §8.5)— 复用 .sk-item/.sk-badge/.sk-empty */ + #wechat-modal { z-index: 112; } + #wechat-modal .card { width: 440px; max-width: 94vw; } + #wechat-modal h3 { display: flex; align-items: center; gap: 8px; } + #wechat-modal h3 .spacer { flex: 1; } + #wx-body { padding: 16px; overflow: auto; } + #wx-body .wx-status { padding: 10px 12px; border-radius: 8px; font-size: 14px; margin-bottom: 14px; background: var(--code-bg, #f6f8fa); } + #wx-body .wx-status.ok { background: #e6f4ea; color: #1a7f37; } + #wx-body .wx-status.err { background: #ffebe9; color: #cf222e; } + #wx-body .wx-status.wait { background: #fff8c5; color: #7d4e00; } + #wx-body .wx-acts { display: flex; gap: 8px; flex-wrap: wrap; } + #wx-qrbox { text-align: center; margin-top: 16px; } + #wx-qrbox img { width: 220px; height: 220px; border: 1px solid var(--line, #d0d7de); border-radius: 8px; } #crons-modal { z-index: 112; } #crons-modal .card { width: 880px; max-width: 94vw; height: 78vh; max-height: 78vh; @@ -1283,6 +1295,35 @@ + +