le-wechat-article v1.1:停点升级为回合终止契约(6 固定停点)+ 多账号支持(分账号画像 / --account 凭证)+ 文案通用化
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# le-wechat-article
|
# le-wechat-article
|
||||||
|
|
||||||
> Leroy 专属的微信公众号文章写作工坊:把主题、素材或旧稿写成可直接发布的高质量公众号文章。
|
> 微信公众号文章写作工坊(多账号):把主题、素材或旧稿写成可直接发布的高质量公众号文章;运营多个公众号时按账号加载各自的画像与同步凭证。
|
||||||
|
|
||||||
**状态**:shared 档,随 LeMetaSkill 项目安装使用。
|
**状态**:shared 档,随 LeMetaSkill 项目安装使用。
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: le-wechat-article
|
name: le-wechat-article
|
||||||
description: >
|
description: >
|
||||||
Leroy 专属的微信公众号文章写作工坊:把一个主题、一堆素材(链接、笔记、聊天记录、代码变更)、
|
微信公众号文章写作工坊(多账号):把一个主题、一堆素材(链接、笔记、聊天记录、代码变更)、
|
||||||
一段复盘或调研,或一篇旧稿,写成可直接发布的高质量公众号文章——类型路由、质量审读、排版自检、微信可粘贴 HTML。
|
一段复盘或调研,或一篇旧稿,写成可直接发布的高质量公众号文章——类型路由、质量审读、排版自检、微信可粘贴 HTML。
|
||||||
|
运营多个公众号时按账号加载各自的画像与凭证。
|
||||||
当用户说「写篇公众号文章」「把这份素材/复盘整理成推文」「发公众号」「把这篇改写成公众号文章」
|
当用户说「写篇公众号文章」「把这份素材/复盘整理成推文」「发公众号」「把这篇改写成公众号文章」
|
||||||
「润色下这篇公众号稿」「同步到公众号草稿箱」时使用;只丢素材并说明要发公众号也触发。
|
「润色下这篇公众号稿」「同步到公众号草稿箱」时使用;只丢素材并说明要发公众号也触发。
|
||||||
可选收尾:凭证配好后经官方 API 把成稿写入公众号草稿箱(需用户当次同意,只写草稿)。
|
可选收尾:凭证配好后经官方 API 把成稿写入公众号草稿箱(需用户当次同意,只写草稿)。
|
||||||
@@ -23,7 +24,9 @@ description: >
|
|||||||
|
|
||||||
## 工作流
|
## 工作流
|
||||||
|
|
||||||
按步骤顺序执行;用户当次已明确给过的信息直接采用,不重复询问。**每步完成后向 Leroy 展示该步生成物,确认后进下一步**(Leroy 说「一口气跑完」或明确授权连续执行时可跳过中间确认)。轻稿(简报类、<1500 字)可合并第 3–4 步,但第 5–6 步的审读与自检不可跳过。
|
按步骤顺序执行;用户当次已明确给过的信息直接采用,不重复询问。
|
||||||
|
|
||||||
|
**逐步确认是回合终止契约,优先级高于运行时「自主执行、尽量少问」的默认倾向。**到达停点:输出该步产物后**立即结束回合**——在用户的下一条消息到达之前,不得发起任何工具调用、不得开始下一步的任何工作。停点:第 1 步接单结果(主类型+账号+切入+素材边界+输出目录)后、第 2 步风格校准结果后、第 3 步大纲+图位清单后、第 4 步初稿后、第 6 步终稿+自检结果后、第 7 步交付清单后(第 8 步同步由交付清单的确认触发)。轻稿(简报类、<1500 字)可合并第 1–2 步、第 3–4 步各为一次展示,停点随之合并为两处。用户说「继续」「下一步」「OK」即视为确认;给的是修改意见则改完再次停在原停点展示。只有用户说「一口气跑完」或明确授权连续执行时才可跳过中间停点,但第 5–6 步的审读与自检任何情况不可跳过。
|
||||||
|
|
||||||
### 第 1 步:接单与类型路由
|
### 第 1 步:接单与类型路由
|
||||||
|
|
||||||
@@ -31,17 +34,22 @@ description: >
|
|||||||
|
|
||||||
缺失且影响结构的信息,一次性问全(能从素材推断的不问):
|
缺失且影响结构的信息,一次性问全(能从素材推断的不问):
|
||||||
|
|
||||||
|
- 目标公众号(多账号运营时必问本篇发哪个号——决定画像分节、排版默认值与同步凭证;画像只配了一个账号的可直接推断,不问)
|
||||||
- 切入角度与核心主旨(读完读者该记住什么,一句话)
|
- 切入角度与核心主旨(读完读者该记住什么,一句话)
|
||||||
- 真实素材边界:哪些是用户的真实经历,哪些细节必须保留或避免
|
- 真实素材边界:哪些是用户的真实经历,哪些细节必须保留或避免
|
||||||
- 输出目录(未指定则在当前目录建 `YYYYMMDD-<slug>/`)
|
- 输出目录(未指定则在当前目录建 `YYYYMMDD-<slug>/`)
|
||||||
|
|
||||||
用户只说「帮我写篇公众号文章」没给主题 → 先给 3–5 个选题角度卡片(标题候选 + 一句话切入 + 适合的类型),等用户选定再进入流程;禁止未经选定就开始调研或写稿。
|
用户只说「帮我写篇公众号文章」没给主题 → 先给 3–5 个选题角度卡片(标题候选 + 一句话切入 + 适合的类型),等用户选定再进入流程;禁止未经选定就开始调研或写稿。
|
||||||
|
|
||||||
|
**接单结果是固定停点**:把「主类型及理由 + 目标公众号 + 切入主旨 + 素材边界 + 输出目录」一屏呈现,用户确认或调整后才进第 2 步。信息是从素材推断出来的更要展示——推断错了在这一步改,代价最低。
|
||||||
|
|
||||||
### 第 2 步:校准风格与事实
|
### 第 2 步:校准风格与事实
|
||||||
|
|
||||||
读三份参考:[style-rules.md](references/style-rules.md)(排版硬规则与产出配额)、[author-profile.md](references/author-profile.md)(作者画像,管风格)、[topic-facts.md](references/topic-facts.md)(常用事实卡,管事实——涉及 Leroy 的项目/工具链/经历时从中取用,免于重新询问)。
|
读三份参考:[style-rules.md](references/style-rules.md)(排版硬规则与产出配额)、[author-profile.md](references/author-profile.md)(作者画像,管风格——**多账号时按第 1 步选定的账号读对应分节,个人层全账号通用,分节缺失的项回退个人层**)、[topic-facts.md](references/topic-facts.md)(常用事实卡,管事实——涉及作者本人的项目/工具链/经历时从中取用,免于重新询问)。
|
||||||
|
|
||||||
画像文件还是空模板时,用其中的「首访问题」问 Leroy 一次,把答复写回该文件(此文件随本 skill 存放,属于个人资产);之后不再重复问。事实卡留空则跳过,Leroy 随时可补。画像与事实卡都不能覆盖类型路由、事实核查和真实性底线。
|
画像还是空模板、或所选账号没有分节时,用其中的「首访问题」问用户一次,把答复写回该文件(此文件随本 skill 存放,属于个人资产);每个账号首次使用各问一次,之后不再重复问。事实卡留空则跳过,用户随时可补。画像与事实卡都不能覆盖类型路由、事实核查和真实性底线。
|
||||||
|
|
||||||
|
**校准结果是固定停点(轻量)**:展示本次将采用的约束——该账号画像要点(定位/声音/文末块/排版骨架与主题色)、将取用的事实卡条目、敏感词要求——用户确认或调整后进第 3 步。画像早已填好、无新信息时也要展示:给用户当场临时调整的机会(如本文换主题色、跳过文末块),展示可精简为几行。
|
||||||
|
|
||||||
### 第 3 步:大纲与图位
|
### 第 3 步:大纲与图位
|
||||||
|
|
||||||
@@ -94,13 +102,13 @@ python3 scripts/check_article.py <文章目录>/article.md --type <主类型>
|
|||||||
|
|
||||||
### 第 7 步:交付
|
### 第 7 步:交付
|
||||||
|
|
||||||
0. **图片生产(默认执行,Leroy 明确不要图时才跳过)**:正文图位与封面按 [visual-production.md](references/visual-production.md) 生产——交给 ckm-banner-design,或 SVG 自绘 + 浏览器截图均可,满足 brief 验收点即可。信息位图上的文字**逐字取自文章**,主题色与排版骨架一致;可编辑源文件(SVG/HTML)随 PNG 一起保留在 `imgs/src/`。生成后把正文图位占位替换为 `imgs/<文件名>.png` 真实路径——**终稿不得残留 placeholder**(此时 check_article 不再加 `--allow-placeholder`)。
|
0. **图片生产(默认执行,用户明确不要图时才跳过)**:正文图位与封面按 [visual-production.md](references/visual-production.md) 生产——交给 ckm-banner-design,或 SVG 自绘 + 浏览器截图均可,满足 brief 验收点即可。信息位图上的文字**逐字取自文章**,主题色与排版骨架一致;可编辑源文件(SVG/HTML)随 PNG 一起保留在 `imgs/src/`。生成后把正文图位占位替换为 `imgs/<文件名>.png` 真实路径——**终稿不得残留 placeholder**(此时 check_article 不再加 `--allow-placeholder`)。
|
||||||
|
|
||||||
1. 排版转换(纯本地,零依赖)。骨架按文章类型选:工程(技术/教程)、杂志(叙事/访谈)、简报(更新/快讯);骨架与色板以作者画像的「排版偏好」为默认:
|
1. 排版转换(纯本地,零依赖)。骨架按文章类型选:工程(技术/教程)、杂志(叙事/访谈)、简报(更新/快讯);骨架与色板以**该账号**画像的「排版偏好」为默认:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 scripts/md2wechat.py <文章目录>/article.md --skeleton <工程|杂志|简报> --palette <靛蓝|松绿|黛紫|绛红> -o <文章目录>/article.html
|
python3 scripts/md2wechat.py <文章目录>/article.md --skeleton <工程|杂志|简报> --palette <靛蓝|松绿|黛紫|绛红> -o <文章目录>/article.html
|
||||||
# 不确定选哪个时先看对照页:--preview 生成三骨架 375px 样张,让 Leroy 挑
|
# 不确定选哪个时先看对照页:--preview 生成三骨架 375px 样张,让用户挑
|
||||||
```
|
```
|
||||||
|
|
||||||
输出的 HTML 全内联样式,全文复制粘贴进公众号编辑器即可用。
|
输出的 HTML 全内联样式,全文复制粘贴进公众号编辑器即可用。
|
||||||
@@ -114,17 +122,19 @@ python3 scripts/check_article.py <文章目录>/article.md --type <主类型>
|
|||||||
|
|
||||||
### 第 8 步:同步到公众号草稿箱(默认执行)
|
### 第 8 步:同步到公众号草稿箱(默认执行)
|
||||||
|
|
||||||
交付确认后**直接同步**——凭证配置完成即视为持续授权(Leroy 说过「先审再同步」的,按其要求先展示等待确认)。Leroy 明确说不不同步时才跳过。封面必须就位(草稿接口必填封面),正文图未全部就位时同步前说明。
|
交付确认后**直接同步**——凭证配置完成即视为持续授权(用户说过「先审再同步」的,按其要求先展示等待确认)。用户明确说不不同步时才跳过。封面必须就位(草稿接口必填封面),正文图未全部就位时同步前说明。
|
||||||
|
|
||||||
前提:已认证公众号 + 环境变量 `WECHAT_APP_ID`、`WECHAT_APP_SECRET`(后台「设置与开发 → 基本配置」获取,放 shell 环境变量,不进任何 git 文件)+ 调用方公网 IP 已加入后台 IP 白名单。
|
前提:已认证公众号 + 环境变量凭证(后台「设置与开发 → 基本配置」获取,放 shell 环境变量,不进任何 git 文件)+ 调用方公网 IP 已加入**该账号**后台的 IP 白名单。**多账号**:默认账号配 `WECHAT_APP_ID` / `WECHAT_APP_SECRET`;每个号另配 `WECHAT_<账号key>_APP_ID` / `WECHAT_<账号key>_APP_SECRET`(key 用大写字母数字,如 `WECHAT_TECH_APP_ID`),同步时 `--account <key>` 选择——各账号的凭证、认证状态与 IP 白名单相互独立。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 首次使用或怀疑凭证/IP 问题时先验证(只调 token 接口,不写内容)
|
# 首次使用或怀疑凭证/IP 问题时先验证(只调 token 接口,不写内容)
|
||||||
python3 scripts/publish_draft.py . --check
|
python3 scripts/publish_draft.py . --check
|
||||||
|
# 多账号:验证/同步指定账号(读 WECHAT_<KEY>_APP_ID / WECHAT_<KEY>_APP_SECRET)
|
||||||
|
python3 scripts/publish_draft.py . --check --account <key>
|
||||||
# 确认执行计划(不联网)
|
# 确认执行计划(不联网)
|
||||||
python3 scripts/publish_draft.py <文章目录> --title "标题" --dry-run
|
python3 scripts/publish_draft.py <文章目录> --title "标题" --dry-run
|
||||||
# 正式同步:上传封面与正文图 → 替换为微信域名地址 → 写入草稿箱
|
# 正式同步:上传封面与正文图 → 替换为微信域名地址 → 写入草稿箱
|
||||||
python3 scripts/publish_draft.py <文章目录> --title "标题" --digest "摘要"
|
python3 scripts/publish_draft.py <文章目录> --title "标题" --digest "摘要" --account <key>
|
||||||
```
|
```
|
||||||
|
|
||||||
同步成功即止:提示用户到「内容与互动 → 草稿」人工预览,**发表永远由用户手动完成**。脚本默认随草稿提交 2.35:1 与 1:1 两个封面裁剪框(居中最大区域,防标题被默认裁掉;封面设计满足中央安全区时效果最佳),`--no-crop` 可跳过。脚本已内置错误码翻译(48001 = 账号未认证无 API 权限;40164 = IP 不在白名单;40113 = 图片编码被素材接口拒,转 jpg 重试);失败时保留本地文件原状,把错误提示给用户,不盲目重试超过一次。封面图缺失时先按封面 brief 生成(交给 ckm-banner-design)再同步。
|
同步成功即止:提示用户到「内容与互动 → 草稿」人工预览,**发表永远由用户手动完成**。脚本默认随草稿提交 2.35:1 与 1:1 两个封面裁剪框(居中最大区域,防标题被默认裁掉;封面设计满足中央安全区时效果最佳),`--no-crop` 可跳过。脚本已内置错误码翻译(48001 = 账号未认证无 API 权限;40164 = IP 不在白名单;40113 = 图片编码被素材接口拒,转 jpg 重试);失败时保留本地文件原状,把错误提示给用户,不盲目重试超过一次。封面图缺失时先按封面 brief 生成(交给 ckm-banner-design)再同步。
|
||||||
@@ -144,12 +154,14 @@ python3 scripts/publish_draft.py <文章目录> --title "标题" --digest "摘
|
|||||||
- 不把技术术语当 AI 味删掉;技术解析保持应有正式度,不统一改成聊天语气。
|
- 不把技术术语当 AI 味删掉;技术解析保持应有正式度,不统一改成聊天语气。
|
||||||
- 不在没跑 check_article.py 的情况下宣称「自检通过」。
|
- 不在没跑 check_article.py 的情况下宣称「自检通过」。
|
||||||
- 不把作者画像、素材中的私人内容复制进交付物或对外文件。
|
- 不把作者画像、素材中的私人内容复制进交付物或对外文件。
|
||||||
|
- 未获「一口气跑完」授权时,不把停点之间的多个步骤合并进同一回合执行。
|
||||||
|
- 不把「在回复中展示了产物」当作「已获得确认」——确认只能来自停点之后用户的下一条消息。
|
||||||
|
|
||||||
## Reference Map
|
## Reference Map
|
||||||
|
|
||||||
- 五类型路由与各类型结构:[article-types.md](references/article-types.md)
|
- 五类型路由与各类型结构:[article-types.md](references/article-types.md)
|
||||||
- 公众号排版硬规则、标点规范、产出配额、标题规范:[style-rules.md](references/style-rules.md)
|
- 公众号排版硬规则、标点规范、产出配额、标题规范:[style-rules.md](references/style-rules.md)
|
||||||
- 审读手册(去模板化 + AI 味指纹 + 平台合规 + 语义保真):[review-playbook.md](references/review-playbook.md)
|
- 审读手册(去模板化 + AI 味指纹 + 平台合规 + 语义保真):[review-playbook.md](references/review-playbook.md)
|
||||||
- Leroy 作者画像(风格,渐进填充):[author-profile.md](references/author-profile.md)
|
- 作者画像(风格;个人层 + 分账号层,渐进填充):[author-profile.md](references/author-profile.md)
|
||||||
- Leroy 常用事实卡(事实,渐进填充):[topic-facts.md](references/topic-facts.md)
|
- 常用事实卡(作者本人事实,渐进填充):[topic-facts.md](references/topic-facts.md)
|
||||||
- 封面与插图生产方法(七步推导 + ckm-banner-design 联动):[visual-production.md](references/visual-production.md)
|
- 封面与插图生产方法(七步推导 + ckm-banner-design 联动):[visual-production.md](references/visual-production.md)
|
||||||
|
|||||||
@@ -1,32 +1,50 @@
|
|||||||
# Leroy 的作者画像
|
# 作者画像(个人层 + 分账号层)
|
||||||
|
|
||||||
本文件随 skill 存放,属于个人资产:写作前加载,让每篇文章保持同一个人的声音。画像只细化偏好,不能覆盖文章类型路由、事实核查与真实性底线。
|
本文件随 skill 存放,属于个人资产:写作前按第 1 步选定的**目标公众号**加载「个人层 + 该账号分节」,让每个号保持自己的声音。画像只细化偏好,不能覆盖文章类型路由、事实核查与真实性底线。
|
||||||
|
|
||||||
## 使用方式
|
## 使用方式
|
||||||
|
|
||||||
- 首次使用时,用下方「首访问题」问 Leroy 一次,把答复写进对应小节,之后不再重复问
|
- 加载顺序:先个人层(全账号通用),再目标账号分节覆盖;分节缺失的项回退个人层默认
|
||||||
- Leroy 随时可以直接口述修改本文件;改完即时生效
|
- 某账号首次使用(还没有分节)时,用「首访问题」问用户一次,把答复写进该账号分节,之后不再重复问
|
||||||
|
- 用户随时可以直接口述修改本文件;改完即时生效
|
||||||
- 本文件内容不复制进文章交付物或对外文件
|
- 本文件内容不复制进文章交付物或对外文件
|
||||||
|
|
||||||
## 首访问题(未填写时问一次)
|
## 首访问题(新账号未建分节时问一次)
|
||||||
|
|
||||||
1. 你的公众号叫什么、主要写给谁看?(账号定位与读者画像)
|
1. 这个公众号叫什么、一句话定位?(写入分节标题与「账号清单」)
|
||||||
2. 你希望读者读完记住你是一个什么样的作者?(声音与气质)
|
2. 主要写给谁看?读者画像与知识水平假设?
|
||||||
3. 有没有你特别喜欢/不能接受的表达方式?(如:口语化程度、能否用梗、第一人称习惯)
|
3. 希望读者记住这是一个什么样的号?(声音与气质)
|
||||||
4. 文末固定要加什么?(关注引导/签名/往期推荐/什么都不加)
|
4. 有没有特别喜欢/不能接受的表达方式?(口语化程度、能否用梗、第一人称习惯)
|
||||||
5. 有没有偏好的排版主题色?(影响 md2wechat.py 的默认配色)
|
5. 文末固定要加什么?(关注引导/签名/往期推荐/什么都不加)
|
||||||
|
6. 排版偏好?(骨架:工程/杂志/简报;主题色:靛蓝/松绿/黛紫/绛红)
|
||||||
|
|
||||||
## 账号与读者
|
## 个人层(全账号通用)
|
||||||
|
|
||||||
读者定位:**入门科普向**——没用过微调但会用 AI 工具的人。类比优先,术语首次出现必须解释;不假设编程背景。(2026-10-05 首访确认)
|
### 身份与声音基线
|
||||||
|
|
||||||
## 声音与语气
|
|
||||||
|
|
||||||
默认中性基线:清楚、自然、有取舍;观点由素材支撑。个性化偏好写在这里:
|
默认中性基线:清楚、自然、有取舍;观点由素材支撑。个性化偏好写在这里:
|
||||||
|
|
||||||
(待填写)
|
(待填写)
|
||||||
|
|
||||||
## 各类型偏好(可选,按 article-types.md 的五类型分别覆盖)
|
### 敏感词清单(可选)
|
||||||
|
|
||||||
|
一行一个,审读时逐词扫描,命中即必改;留空则只按通用合规类别检查。个别账号另有忌讳的写进该账号分节:
|
||||||
|
|
||||||
|
(待填写)
|
||||||
|
|
||||||
|
## 账号清单
|
||||||
|
|
||||||
|
每行一个:`账号key(--account 用) — 公众号名称 — 一句话定位`。多账号同步凭证按 `WECHAT_<账号key>_APP_ID / _SECRET` 配置(见 SKILL.md 第 8 步):
|
||||||
|
|
||||||
|
- (待填写)
|
||||||
|
|
||||||
|
## 账号:待标注(2026-10-05 首访确认;多账号启用后补账号名与 key)
|
||||||
|
|
||||||
|
### 定位与读者
|
||||||
|
|
||||||
|
读者定位:**入门科普向**——没用过微调但会用 AI 工具的人。类比优先,术语首次出现必须解释;不假设编程背景。
|
||||||
|
|
||||||
|
### 各类型偏好(可选,按 article-types.md 的五类型分别覆盖)
|
||||||
|
|
||||||
- 新闻和软件更新简报:(待填写)
|
- 新闻和软件更新简报:(待填写)
|
||||||
- 产品体验和工具测评:(待填写)
|
- 产品体验和工具测评:(待填写)
|
||||||
@@ -34,7 +52,7 @@
|
|||||||
- 技术解析和教程:(待填写)
|
- 技术解析和教程:(待填写)
|
||||||
- 生活叙事和观点文章:(待填写)
|
- 生活叙事和观点文章:(待填写)
|
||||||
|
|
||||||
## 文末固定块
|
### 文末固定块
|
||||||
|
|
||||||
默认追加关注引导,逐字使用:
|
默认追加关注引导,逐字使用:
|
||||||
|
|
||||||
@@ -46,14 +64,8 @@
|
|||||||
|
|
||||||
(2026-10-05 首访确认;改文案直接编辑此处)
|
(2026-10-05 首访确认;改文案直接编辑此处)
|
||||||
|
|
||||||
## 排版偏好
|
### 排版偏好
|
||||||
|
|
||||||
默认排版骨架:**工程**(色条标题,适合技术文)
|
默认排版骨架:**工程**(色条标题,适合技术文)
|
||||||
默认主题色:**靛蓝 #1A5CFF**
|
默认主题色:**靛蓝 #1A5CFF**
|
||||||
默认字号:16px(公众号正文标准)
|
默认字号:16px(公众号正文标准)
|
||||||
|
|
||||||
## 敏感词清单(可选)
|
|
||||||
|
|
||||||
一行一个,审读时逐词扫描,命中即必改;留空则只按通用合规类别检查:
|
|
||||||
|
|
||||||
(待填写)
|
|
||||||
|
|||||||
@@ -28,11 +28,11 @@
|
|||||||
|
|
||||||
公众号内容合规是发文失败与限流的实际来源。逐类扫描,命中任何一条都按🔴必改处理,不带「建议」:
|
公众号内容合规是发文失败与限流的实际来源。逐类扫描,命中任何一条都按🔴必改处理,不带「建议」:
|
||||||
|
|
||||||
- **违禁内容**:政治敏感表述、暴恐、色情低俗、赌博诈骗类措辞——拿不准的具体表述列出来问 Leroy,不自行改写后放行
|
- **违禁内容**:政治敏感表述、暴恐、色情低俗、赌博诈骗类措辞——拿不准的具体表述列出来问用户,不自行改写后放行
|
||||||
- **广告法绝对化用语**:最、第一、唯一、100%、国家级、顶级、极致、史上最 X——用户素材里确有出处的数据表述(如「官方公布的 100% 兼容」须附来源)除外
|
- **广告法绝对化用语**:最、第一、唯一、100%、国家级、顶级、极致、史上最 X——用户素材里确有出处的数据表述(如「官方公布的 100% 兼容」须附来源)除外
|
||||||
- **医疗与金融承诺**:疗效保证、稳赚、保本、收益率承诺、荐股荐基类表述
|
- **医疗与金融承诺**:疗效保证、稳赚、保本、收益率承诺、荐股荐基类表述
|
||||||
- **未授权素材**:他人图片/大段引文无来源标注;无法确认授权的标「待作者确认」
|
- **未授权素材**:他人图片/大段引文无来源标注;无法确认授权的标「待作者确认」
|
||||||
- **自定义敏感词**:Leroy 在 [author-profile.md](author-profile.md) 维护的清单逐词扫描,命中即必改
|
- **自定义敏感词**:用户在 [author-profile.md](author-profile.md) 维护的清单(个人层 + 当前账号分节)逐词扫描,命中即必改
|
||||||
- **边界**:脚本不内置词库(会过时),靠模型按类别现场判断 + 自定义清单兜底;改写合规问题时同样受语义保真约束——把「第一」改成「领先」是降级表述,不是改事实
|
- **边界**:脚本不内置词库(会过时),靠模型按类别现场判断 + 自定义清单兜底;改写合规问题时同样受语义保真约束——把「第一」改成「领先」是降级表述,不是改事实
|
||||||
|
|
||||||
- 数字、日期、版本、事件能否在素材中找到出处
|
- 数字、日期、版本、事件能否在素材中找到出处
|
||||||
|
|||||||
@@ -58,4 +58,4 @@
|
|||||||
|
|
||||||
- 开头按类型路由走(见 [article-types.md](article-types.md));共同底线:不用「在当今…的时代」「随着…的发展」等零信息背景铺垫——时间与趋势本身是素材事实时才保留
|
- 开头按类型路由走(见 [article-types.md](article-types.md));共同底线:不用「在当今…的时代」「随着…的发展」等零信息背景铺垫——时间与趋势本身是素材事实时才保留
|
||||||
- 高频连接词(首先/其次/最后/综上所述/值得注意的是/不难发现/让我们)只在承担真实顺序、转折、总结功能时保留,纯铺垫删掉
|
- 高频连接词(首先/其次/最后/综上所述/值得注意的是/不难发现/让我们)只在承担真实顺序、转折、总结功能时保留,纯铺垫删掉
|
||||||
- 结尾按文章内容自然收束;**不自动追加**关注引导、账号名、推广信息——只有 Leroy 在当次任务明确要求或作者画像明确配置时才加
|
- 结尾按文章内容自然收束;**不自动追加**关注引导、账号名、推广信息——只有用户在当次任务明确要求或当前账号画像明确配置时才加
|
||||||
|
|||||||
@@ -4,12 +4,16 @@
|
|||||||
|
|
||||||
前置条件:
|
前置条件:
|
||||||
1. 已认证的公众号(未认证账号调用草稿接口会返回 48001)。
|
1. 已认证的公众号(未认证账号调用草稿接口会返回 48001)。
|
||||||
2. 环境变量 WECHAT_APP_ID、WECHAT_APP_SECRET(在 mp.weixin.qq.com 设置与开发→基本配置获取)。
|
2. 环境变量凭证(在 mp.weixin.qq.com 设置与开发→基本配置获取,只放 shell 环境变量,不进 git):
|
||||||
3. 调用方公网 IP 已加入公众号后台的 IP 白名单(家用宽带 IP 变化后需回后台更新)。
|
默认账号 WECHAT_APP_ID / WECHAT_APP_SECRET;
|
||||||
|
多账号按 --account <key> 读 WECHAT_<KEY>_APP_ID / WECHAT_<KEY>_APP_SECRET(key 转大写),
|
||||||
|
每个账号在各自后台配置,互不相通。
|
||||||
|
3. 调用方公网 IP 已加入**该账号**后台的 IP 白名单(家用宽带 IP 变化后需回后台更新)。
|
||||||
|
|
||||||
用法:
|
用法:
|
||||||
python3 publish_draft.py <文章目录> [--title 标题] [--digest 摘要] [--cover 封面图] \
|
python3 publish_draft.py <文章目录> [--title 标题] [--digest 摘要] [--cover 封面图] \
|
||||||
[--check] [--dry-run] [--author 作者]
|
[--account 账号key] [--check] [--dry-run] [--author 作者]
|
||||||
|
--account 多账号选择,读 WECHAT_<KEY>_APP_ID / WECHAT_<KEY>_APP_SECRET;缺省用默认凭证
|
||||||
--check 只验证凭证与 IP 白名单(调 token 接口),不写任何内容
|
--check 只验证凭证与 IP 白名单(调 token 接口),不写任何内容
|
||||||
--dry-run 不联网,打印将上传的图片与将提交的草稿参数
|
--dry-run 不联网,打印将上传的图片与将提交的草稿参数
|
||||||
退出码:0 成功;1 失败(错误码已翻译成人话);2 凭证缺失或参数不合法。
|
退出码:0 成功;1 失败(错误码已翻译成人话);2 凭证缺失或参数不合法。
|
||||||
@@ -135,6 +139,25 @@ def get_token(app_id, secret):
|
|||||||
return r["access_token"]
|
return r["access_token"]
|
||||||
|
|
||||||
|
|
||||||
|
def load_creds(account):
|
||||||
|
"""按账号取凭证:--account key 读 WECHAT_<KEY>_APP_ID/_SECRET,缺省读默认对。"""
|
||||||
|
if account:
|
||||||
|
key = re.sub(r"[^A-Z0-9]+", "_", account.upper()).strip("_")
|
||||||
|
env_id, env_secret = f"WECHAT_{key}_APP_ID", f"WECHAT_{key}_APP_SECRET"
|
||||||
|
else:
|
||||||
|
env_id, env_secret = "WECHAT_APP_ID", "WECHAT_APP_SECRET"
|
||||||
|
return os.environ.get(env_id, ""), os.environ.get(env_secret, ""), env_id, env_secret
|
||||||
|
|
||||||
|
|
||||||
|
def missing_creds(env_id, env_secret):
|
||||||
|
print(f"❌ 缺少环境变量 {env_id} / {env_secret}\n"
|
||||||
|
" 获取路径:mp.weixin.qq.com(该账号)→ 设置与开发 → 基本配置\n"
|
||||||
|
f" 设置方式:export {env_id}=xxx {env_secret}=yyy(建议写进 ~/.zshrc)\n"
|
||||||
|
" 多账号:每个号各配一对,同步时用 --account <key> 选择。别把密钥写进任何 git 仓库。",
|
||||||
|
file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ap = argparse.ArgumentParser(description="同步成稿到公众号草稿箱(不群发)")
|
ap = argparse.ArgumentParser(description="同步成稿到公众号草稿箱(不群发)")
|
||||||
ap.add_argument("dir", help="文章目录(含 article.html)")
|
ap.add_argument("dir", help="文章目录(含 article.html)")
|
||||||
@@ -142,24 +165,21 @@ def main():
|
|||||||
ap.add_argument("--digest", default="")
|
ap.add_argument("--digest", default="")
|
||||||
ap.add_argument("--author", default="")
|
ap.add_argument("--author", default="")
|
||||||
ap.add_argument("--cover", help="封面图路径(默认在文章目录找 cover.png/jpg/jpeg/webp)")
|
ap.add_argument("--cover", help="封面图路径(默认在文章目录找 cover.png/jpg/jpeg/webp)")
|
||||||
|
ap.add_argument("--account", default="", help="多账号选择:读 WECHAT_<KEY>_APP_ID/_SECRET;缺省用默认凭证")
|
||||||
ap.add_argument("--check", action="store_true", help="只验证凭证与 IP 白名单")
|
ap.add_argument("--check", action="store_true", help="只验证凭证与 IP 白名单")
|
||||||
ap.add_argument("--no-crop", action="store_true", help="不提交封面裁剪框参数")
|
ap.add_argument("--no-crop", action="store_true", help="不提交封面裁剪框参数")
|
||||||
ap.add_argument("--dry-run", action="store_true", help="不联网,打印执行计划")
|
ap.add_argument("--dry-run", action="store_true", help="不联网,打印执行计划")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
app_id = os.environ.get("WECHAT_APP_ID", "")
|
app_id, secret, env_id, env_secret = load_creds(args.account)
|
||||||
secret = os.environ.get("WECHAT_APP_SECRET", "")
|
acct_note = f"账号 {args.account}" if args.account else "默认账号"
|
||||||
|
|
||||||
# --check 只验凭证与 IP,不需要文章产物
|
# --check 只验凭证与 IP,不需要文章产物
|
||||||
if args.check:
|
if args.check:
|
||||||
if not (app_id and secret):
|
if not (app_id and secret):
|
||||||
print("❌ 缺少环境变量 WECHAT_APP_ID / WECHAT_APP_SECRET\n"
|
missing_creds(env_id, env_secret)
|
||||||
" 获取路径:mp.weixin.qq.com → 设置与开发 → 基本配置\n"
|
|
||||||
" 设置方式:export WECHAT_APP_ID=xxx WECHAT_APP_SECRET=yyy(建议写进 ~/.zshrc)\n"
|
|
||||||
" 别把密钥写进任何 git 仓库。", file=sys.stderr)
|
|
||||||
sys.exit(2)
|
|
||||||
get_token(app_id, secret) # 失败时内部已 die 并翻译错误码
|
get_token(app_id, secret) # 失败时内部已 die 并翻译错误码
|
||||||
print("✔ 凭证与 IP 白名单验证通过,可以同步草稿箱")
|
print(f"✔ {acct_note} 凭证与 IP 白名单验证通过,可以同步草稿箱")
|
||||||
return
|
return
|
||||||
|
|
||||||
# ---- 收集产物(dry-run 也走这段,但不联网) ----
|
# ---- 收集产物(dry-run 也走这段,但不联网) ----
|
||||||
@@ -196,11 +216,7 @@ def main():
|
|||||||
return
|
return
|
||||||
|
|
||||||
if not (app_id and secret):
|
if not (app_id and secret):
|
||||||
print("❌ 缺少环境变量 WECHAT_APP_ID / WECHAT_APP_SECRET\n"
|
missing_creds(env_id, env_secret)
|
||||||
" 获取路径:mp.weixin.qq.com → 设置与开发 → 基本配置\n"
|
|
||||||
" 设置方式:export WECHAT_APP_ID=xxx WECHAT_APP_SECRET=yyy(建议写进 ~/.zshrc)\n"
|
|
||||||
" 别把密钥写进任何 git 仓库。", file=sys.stderr)
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
if not args.title:
|
if not args.title:
|
||||||
die("正式同步必须提供 --title", 2)
|
die("正式同步必须提供 --title", 2)
|
||||||
|
|||||||
Reference in New Issue
Block a user