# WX-Scheduled-Publish **Repository Path**: efficientos/wx-scheduled-publish ## Basic Information - **Project Name**: WX-Scheduled-Publish - **Description**: “自动发布微信文章 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-28 - **Last Updated**: 2026-09-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 微信公众号草稿箱自动发布工具 基于 Playwright 驱动浏览器,扫码登录微信公众平台,将 Markdown 文章增量发布到草稿箱。 ## 功能 - 扫码登录微信公众平台(持久化登录态,2小时内免扫码) - 按系列扫描 `output/` 目录下的定稿 Markdown 文章 - 增量发布:自动跳过已发布文章,仅发布新文章 - **更新已有草稿**:已发布文章 hash 变化时自动更新草稿(非新建) - 文章涉及的本地图片通过素材库上传到微信图床(人工节奏操作,上传后校验显示) - Markdown 转微信兼容 HTML(图片 URL 回填) - 在草稿箱新建/更新图文:填标题 + 写正文 + 填摘要 + 保存为草稿 - 正文写入支持图片显示 + 行内格式保留(加粗/斜体/下划线/链接) - 交互式 prompt:选系列 → 确认篇数 → 执行(显示 [更新] 标记) - 单图上传失败重试 3 次仍失败则中断整个任务,已成功篇状态已保存,重新运行可续发 ## 正文写入方案(Hybrid) 正文写入采用 **hybrid 方案**(`setBodyViaHybrid`),分两步解决图片显示与行内格式保留: ### 步骤 1. **Paste 简化 HTML** — 去除行内格式标签(``/``/`` 等),保留图片和块级元素,通过 `ClipboardEvent` 派发 paste 事件。微信 paste handler 自动上传图片并设置 `data-imgfileid`(后端关联图片的关键 ID)。 2. **读取图片节点** — 从 ProseMirror 编辑器 state 中提取带 `data-imgfileid` 的完整图片 attrs。 3. **ProseMirror dispatch 完整文档** — 用 `textstyle` mark(`fontWeight`/`fontStyle`/`textDecoration`)实现行内格式,复用 paste 得到的图片 attrs 构造 image 节点。 ### 为什么需要 Hybrid | 单一方案 | 问题 | |----------|------| | ClipboardEvent paste | 含行内格式的段落粘贴失败(pmSize=4,显示占位符) | | ProseMirror dispatch | 无法自动获取 `data-imgfileid`,图片无法与后端关联 | | Hybrid(paste + dispatch) | paste 处理图片上传,dispatch 处理行内格式,两者优势互补 | ### 微信 ProseMirror Schema 要点 - 节点名:`para`(非 `paragraph`)、`codeblock`(非 `code_block`)、`nodeleaf`(包裹 inline 图片的 block 容器) - 行内格式 mark:`textstyle`(attrs: `fontWeight`, `fontStyle`, `textDecoration`, `color` 等) - 链接 mark:`link`(attrs: `href`, `style`, `textvalue` 等) - 编辑器实例:`window.__mpBodyChecktextView` ## 使用 ```powershell # 安装依赖 npm install npx playwright install chromium # 启动工具(交互式:选系列 → 确认篇数 → 执行) npm start # 指定系列发布(跳过系列选择,后续仍交互确认篇数) npm start -- --series=BOM # 非交互式批量发布(全程无人值守,需已登录或扫码一次) node src/browser/probe-cli-e2e.js --series=BOM # 列出可用系列 node src/browser/probe-cli-e2e.js --list ``` 运行后会弹出浏览器,请在浏览器中扫码登录微信公众平台,登录后工具自动继续。 ### seriesName 优先级 不指定 `--series` 时,按以下优先级确定发布系列: 1. **命令行参数** `--series=XXX` 2. **config.json** 的 `seriesName` 字段 3. **上次记录** `state/last-series.json`(发布完成后自动保存) 4. **交互式选择**(仅 `npm start`,上次系列标注 `(上次)`;`probe-cli-e2e.js` 则取第一个系列) ## 配置 `config/config.json`: | 字段 | 说明 | 默认值 | |------|------|--------| | articleRoot | 文章根目录 | `D:\CodeArts\SpaceProjects\铝业数据治理华为三阶十八步\微信文章` | | excludeDirs | 排除的子目录(非系列) | `["wx风格参考"]` | | seriesName | 默认发布系列(可选,优先级低于命令行参数,高于上次记录) | 无 | | stateDir | 状态文件目录 | `./state` | | delays.short/medium/long | 人工节奏延时(毫秒) | 600/1500/3000 | | retry.max | 单图上传重试次数 | 3 | | loginTimeoutMs | 扫码登录超时(毫秒) | 600000 | | fillSummary | 是否自动填入摘要(从首段提取前120字) | false | | readingSpeed | 阅读速度(字/分钟),用于计算阅读时长 | 500 | | imageReadSeconds | 每张图片附加阅读时间(秒) | 5 | ## 状态文件 - `state/published.json`:已发布文章记录(filePath → {title, contentHash, draftTitle, publishedAt, imageMap}) - `state/image-cache.json`:本地图片 → 微信图床 URL 缓存(跨篇去重) - `state/last-series.json`:上次发布的系列名(发布完成后自动保存,下次默认使用) ## 文章识别规则 - 系列子目录:`articleRoot` 下除 `excludeDirs` 外的所有子目录 - 定稿文章:`<系列>/output/` 下文件名匹配 `^第\d+篇_.*\.md$` 的文件 - 文章标题:Markdown 首行 `# 标题` - 图片引用:`![alt](images/xxx.png)`,相对 md 文件所在目录 ## 探针脚本 ```powershell # 验证草稿箱新建图文(端到端) node src/browser/probe-draft.js ``` 探针会:上传图片 → 转换 Markdown → 创建草稿 → 保存。运行后到微信草稿箱确认结果。 ## 测试 ```powershell npm test ``` ## 限制 - 不处理封面图字段,需用户在草稿箱中后续自行设置 - 表格节点在部分文章中可能降级为纯文本(微信 schema 对 table attrs 有特殊约束) ## 开发实践 详见 [docs/best-practices.md](docs/best-practices.md) — 微信公众号浏览器自动化的最佳开发实践。