下载地址
https://www.zhengdianxue.com/wp-content/uploads/skills/org-78cs9wim--wechat-mp-template-article-pj.zip将提示词发送给你的 AI 安装该 skills
请根据 https://skillhub.cn/install/skillhub.md,安装 @org-78cs9wim/wechat-mp-template-article-pj。公众号招聘图文「推草稿」全流程(单技能自包含)——含首次使用六步向导(扫码登录→录入凭证→检查/创建模板→确认规则→选发布方式→编辑保存草稿)、岗位表出长图/封面、正文按规范排版、套后台图文模板(多模板可选)只替换占位符、自检、推草稿箱(单篇/多图文,多公众号凭证自备)、回读核对;当用户要发招聘简章到公众号、推草稿、套模板排版发文时使用。
阅读全文 (展开查看完整说明)
概述
公众号招聘图文流水线(推草稿全流程)
从「招聘简章 + 岗位表」到公众号草稿箱的一条龙:
出素材(岗位表长图/封面/二维码) → 写正文(排版 helper) → 套后台模板(只换占位符)
→ 自检 → 推草稿(单篇 / 多图文) → 回读核对模板是 135 编辑器产出的 HTML(约 5~6 万字符),里面有大量固定模块(代投客服、备考资料、免责声明、公众号名片)和若干占位符。填充环节只替换占位符,其余原样保留——这是用户的核心要求,也是本技能存在的全部理由。
模板有多个(医护 / 事业单位 / …,见 templates/catalog.json):每次排版先选模板再动内容,命令与规则见「⓪ 选择文章模板」。
📖 有人(不是 AI)要按步骤操作时,直接看
references/manual.md—— 那是本技能的完整操作手册;
SKILL.md是给 AI 的规则与约束。🚀 第一次用(或换公众号) 先走下面的「首次使用:六步向导」,说明在
references/onboarding.md。
🚨 四条硬约束(先读,不可绕过)
1. 草稿箱对其他草稿「只读」
除本次运行自己新建的草稿外,绝不删除、绝不修改任何已有草稿——包括标题为空的「废草稿」。原因:草稿在微信侧是按组管理/删除的,误删会连带删掉同组的真实文章,不可逆。
- 需要清理草稿 → 请用户本人在公众号后台操作,脚本一律不代劳。
- 本技能的
push_article.py默认只做「新建」,永远碰不到别人的草稿。 - 想覆盖更新,只有一种合法情形:
media_id是本次运行自己新建的那条,且必须显式加
--confirm-own-draft,否则脚本直接拒绝执行。
2. 素材与模板按账号隔离,换号不许清掉别的账号的数据
封面图、正文图片上传后拿到的 media_id 属于该账号自己的素材库,只能在同账号使用。换一个公众号推送时,必须重新上传封面与正文图,不能复用另一个账号的 media_id(会报无效素材或图片不显示)。
同理,登录态与模板也是一个号一份,--account <号> 选择,互不覆盖:
| 东西 | 存哪 | 换号怎么办 |
|---|---|---|
| 后台登录态(抓模板用) | ~/.workbuddy/secrets/mp-wechat-backend/accounts/<号>/ | mp_login.py --account 新号 --login 另开一份 |
| 模板库(模板 HTML + catalog) | templates/<号>/ | fetch_templates.py --account 新号 --auto --catalog 另抓一份 |
| 推草稿凭证(AppID/AppSecret) | 技能根 accounts.json,一个号一个键 | 加一个键即可 |
🚫 换号不要用
--forget(它会把那个号的登录态删掉),更不要用--forget-all。
--forget只在确实要退出某个号时才用,而且带--account时只清那一个号。老机器上只有一份不带账号的登录态时,先
mp_login.py --migrate-legacy <号>认领一次即可。
3. 别整篇读成品 HTML、别再造一次性校验脚本(成本约束,2026-09-17 定)
本技能的耗时与消耗大头既不在脚本、也不在微信接口(实测:构建 1 篇 0.40s、--check 单篇 0.42s、生成封面 0.51s、推 8 篇多图文 17.0s、回读 8 篇 5.8s;整批机器只占 4.7%),而在模型轮数 × 累计上下文:
- 成品 HTML 一律不许整篇
Read—— 8 篇约 36 万字符,读一篇 ≈ 3 万 tokens。
查内容用 Grep 定位,判合规用 build_article.py --check / preflight.py。
- 推之前跑
preflight.py,不要再现场写_len.py/_chk_*.py这类一次性脚本
(上一批留下 56 个临时文件,约占全部工具往返的六成)。
- 改样式规则不要重建正文:
restyle.py对已生成 HTML 就地归一化,再按--index
逐篇 draft/update 原地重推。
- 海报型公告先回源、别直接切图(见硬约束 4)—— 上一批 25 次读图 / 2 万+ tokens
的最大单项消耗,就出在「没先试 WebFetch」上。
- 细节都在
references/:SKILL.md每次会话无条件注入,是常驻税 ——
按需读,别把参考文档全量拉进上下文。
4. 开工先给采集 txt 做体检 → 偏短 / 全是图 先批量回源(2026-09-17 定)
采集器对海报型公告(校招海报 / 宣传长图)和导航页只能抓到零星文字 ——414/417/418 正文分别只有 32 字(配 4/1/1 张图),419 更极端:正文 76 字里11/12 行是「首页 / 关于锦泰 / 新闻中心 …」导航。这时若直接切图读,417/418 两张 11000~13000px 海报要切 15 片逐片读,是本批最大的单项消耗(≈25 次读图 / 2 万+ tokens);而原文链接通常有纯文本正文,一条命令就能抓全(419 实测:本地抓一次拿到 3668 字完整正文 —— 18 人 / 8 个岗位 / 9月18日—10月25日 / 任职要求,模型侧零 token)。
动手写 batch_def.py 之前,先跑这两步(第一步只读不联网;第二步本地抓取,正文清洗后才进上下文,模型侧零消耗):
python scripts/scan_sources.py --dir "C:/Users/.../Downloads/articles_<批次>" # ① 判哪几篇要回源
python scripts/fetch_sources.py --dir "<采集目录>" --fetch # ② 批量抓 → _refetch/
python scripts/fetch_sources.py --dir "<采集目录>" --verify # ③ 校验抓齐没(0 才动笔)判定阈值(2026-09-17 三批 24 篇真实样本校准:正常篇 651~5408 字,异常篇 32~76 字):
| 判定 | 命中条件 | 怎么办 |
|---|---|---|
| FETCH(必先回源) | 正文 < 300 字;或图片行占比 ≥ 0.5;或导航行占比 ≥ 0.7 且正文 < 800 字;或正文是 TLS 证书错误页;或正文头部被截断(开头就有没配对的右引号/右括号) | 先 fetch_sources.py --fetch 抓原文(本地、零模型消耗);抓不到再 WebFetch 兜底;两者都拿不到文字才切图 |
| WATCH | 正文 < 600 字(未命中上面) | 顺手核对一眼,多半正常(如 409 湘潭 657 字) |
| OK | 其余 | 直接用 |
退出码 1 = 有篇要回源(不是报错,是「这一步还没做」的闸门)。
回源的四种结果,脚本直接给结论(落盘 <采集目录>/_refetch/<id>_<标题>.txt,头部与采集 txt 同格式,可直接读、也可再喂给 scan_sources.py):
| 结果 | 含义 | 接下来 |
|---|---|---|
| ✅ OK | 抓到纯文本正文(招聘关键词命中 ≥5 个) | 直接读这个 txt 写 batch_def.py |
| 🖼 POSTER | 原文本身只有海报图,回源救不了 | 走切图读图(pitfalls.md);图片 URL 已落 <id>.images.txt |
| 🧭 NAVTEXT | 字够多但没有正文(模板导航 / JS 异步加载) | WebFetch 兜底 → 正文写 raw/<id>.txt → --import-dir raw/ |
| 🚫 BLOCKED | 被反爬拦下 / 证书过期 / HTTP 错误 | 同上(证书过期的站点 requests 必报 SSLError,直接 WebFetch) |
⚠️ 「假 OK」是第三批才暴露的坑:
415北京体育大学 5408 字全是 TLS 证书链(官网站点证书过期,采集器抓到的是证书错误页),
422永修县 3962 字却是从「…公告”专题进入报名」开始的半句话——整段「一、招聘人数 / 报名时间」都不在。
两篇字数都远超 300,只看字数会全部放过去。现在
scan_sources.py会分别判
FETCH(原因写明「证书错误页」/「头部疑似被截断」)。处置:415 那种官网证书过期的,本地
requests一定SSLError,直接上WebFetch→ 正文写
raw/<id>.txt→--save <id> --raw raw/<id>.txt(--save会自己从采集txt 认标题,不会存成
<id>_<id>.txt)。
⚠️ 回源拿到正文后,按纯文本写
L.p/L.li—— 不要再把那张海报整张塞进正文当内容。海报只作素材图时才
L.img();正文靠图承载信息 = 读者搜不到、也读不了。切图读图法(确认原文本身就只有图时)见
references/pitfalls.md。
🚀 首次使用:六步向导(新环境/新账号先走这一遍)
日常出稿直接跳到「全流程」;但第一次用(或换公众号)时,前置条件有六件事要按顺序办,而且牵涉两套互不相干的凭证。整套流程做成了一个向导,每步一条命令、幂等、可随时体检:
⚠️ 别和下面的「全流程」混:这六步是首次使用的前置向导(登录/凭证/模板/规则/发布方式/
保存草稿),走一遍就不用再管;下面的
⓪~⑥是每次出稿都要走的内容流水线。
python scripts/mp_setup.py # 总览体检:六步各自状态 + 建议的下一步
python scripts/mp_setup.py --json s.json # 同上,落盘| 步 | 做什么 | 命令 | 通过标准 |
|---|---|---|---|
| ① | 登录公众号后台(扫码) | mp_setup.py --step 1 --account <号> | ✅ 登录态有效(失效才重新无头扫码) |
| ② | 录入公众号凭证 | mp_setup.py --step 2 | 写入 accounts.json;--verify 打印 ✅ 凭证可用 |
| ③ | 检查文章模板 | mp_setup.py --step 3 --account <号> | ✅ 有 N 份可直接用的模板 |
| ④ | 确认文章规则(可改) | mp_setup.py --step 4 --confirm | 记录规则指纹 |
| ⑤ | 选择发布方式 | mp_setup.py --step 5 | 富文本粘贴 / API 推草稿 |
| ⑥ | 开始编辑并保存草稿 | mp_setup.py --step 6 <push_article 参数> | 输出 MEDIA_ID=…,回读 0 问题 |
♻️ 重复测试向导:
mp_setup.py --reset只列出会清掉什么(不动手);
mp_setup.py --reset --yes先自动备份到~/.workbuddy/_backup/firstuse-reset-<时间戳>/再清空账号数据(凭证/登录态/模板库/规则确认记录),回到
NEXT=1的全新状态。只动技能自身状态,文章素材目录不受影响。
🛑 「是不是首次使用」只看技能自身状态,不看"命令能不能跑通":只要无参体检里有任何一步
不是 ✅(最典型的是技能被 firstrun-reset 清空,
accounts.json/templates/template_*.html只剩
*.example.*),就是首次使用,必须从体检给出的NEXT=那一步起、逐步走给用户看。禁止这条捷径:翻到
~/.workbuddy/_backup/firstrun-reset-*/→ 静默拷回凭证与模板 →直接从 ⑥ 推草稿。**恢复只能把 ①②③ 变回「就绪」,替不了 ④(规则确认)与 ⑤(发布方式选择)
这两步用户决策**,也替不了 ⑥ 前该给用户的说明。
恢复动作本身可以做,但要先告知、再继续:① 讲清"技能被 reset 了、我恢复了哪几个文件、
为什么";② 复检并把六步状态与
NEXT=念给用户;③ 从NEXT=继续走完剩余步骤。(用户 2026-09-14 明确要求:六步向导是首次使用的主路径,不许绕。)
📌 两种运行形态(用户 2026-09-14 定的规则):
① 检测到首次使用(六步体检有 ❌)→ 用户发的任何会话都按 ①→⑥ 一步一步引导
配置与使用,从
NEXT=那步起推进,每步给用户讲清"这步在做什么、要提供什么";② 六步全部 ✅ 之后→ 降级为日常形态:每次使用只做 cookie/token 过期检测
(脚本取用登录态时自动校验,失效才重新无头扫码),**不再重复走向导、
不再重复索要 ④⑤ 的确认**。只有
accounts.json/ 模板库 / 规则指纹被清掉时才回到形态 ①。
AI 执行这六步时的硬性要求:
- 先跑
mp_setup.py(不带参数)体检,读最后一行NEXT=<步骤号>,从该步开始做,
不要从第 1 步无脑重跑(每一步都幂等,重复跑不会出错,但会让用户重复输入)。
- 第 ① 步的二维码要在会话里展示:二维码写到
~/.workbuddy/secrets/mp-wechat-backend/login_qr.png,每 20 秒自动刷新。做法:把 --step 1 --login 放后台跑,等几秒后把该图片路径用文件展示能力给用户扫(--step 1 --qr 只打印路径)。登录态是否过期由脚本每次自动校验,不要自己去猜。
- 第 ② 步必须向用户逐项索要:公众号名称、
appid、appsecret、author四个字段
(author ≤8 字)。拿到后写进 accounts.json 并 --verify 验一次;脚本会做格式体检(appid 形如 wx+16 位、secret 32 位、author ≤8 字)。不要把 appsecret 回显到聊天里、不要写进其它文件。
- 第 ③ 步没有可用模板时,要告诉用户怎么建:
--step 3 --guide会打印完整指引
(7 个占位符 + 后台操作步骤),并给出示意图references/images/template-placeholders.png —— 把这张图展示给用户。建好后用新号 mp_login.py --login → fetch_templates.py --auto --catalog 抓回来,再 --step 3 复检。
- 第 ④ 步要把规则念给用户确认、并说明怎么改:规则文本是脚本实时读自
mp_template.STYLE 与 references/style-spec.md,不是死文本。用户要改就改那两处;确认状态按规则指纹记录(.setup_state.json),规则变动后 --step 4 会提示重新确认。
- 第 ⑤ 步要讲清两条发布路径:A 富文本粘贴(
article.html全选复制粘到后台正文区,
不需要凭证;但本地图片不会跟着过去,需先传素材库);B API 推草稿(推荐,图片自动上传)。
- 第 ⑥ 步 = 开始编辑并保存草稿:参数原样转发给
push_article.py,推完必须
用 inspect_draft.py 回读核对。
📖 六步的完整说明(含每步为什么、失败怎么办、换号怎么做)在
references/onboarding.md。
依赖
- Python 3,第三方包:
requests(抓模板用)、xlrd==1.2.0(读.xls岗位表,
2.x 不支持 BIFF)、openpyxl(读 .xlsx 岗位表)、qrcode、pillow、playwright(自动登录用,需已装 chromium:python -m playwright install chromium)。装在装了上述依赖的 Python 环境里,下文统一记作 python。
- 推送环节本技能自带(
scripts/mp_push.py),不需要任何其它技能。
凭证:换公众号必须配 AppID / AppSecret(各号一套)
推送走的是微信服务端 API(draft/add),调用方必须证明自己有权操作该公众号,所以凭证是必需的,而且每个公众号一套,不能通用:
| 字段 | 必填 | 说明 |
|---|---|---|
appid | ✅ | 公众号的 AppID(wx 开头) |
appsecret | ✅ | 该号的 AppSecret |
author | 可选 | 默认作者名(≤8 字,超长微信报 45110) |
写在技能根目录 accounts.json(模板见 templates/accounts.example.json):
{
"我的公众号": {"appid": "wx0000000000000000", "appsecret": "…", "author": "短名"},
"另一个号": {"appid": "wx…", "appsecret": "…", "author": "短名"}
}一个公众号 = 一个键,推送时 --account <键名> 选择。取用优先级:命令行 --appid/--appsecret > accounts.json > 交互式输入(交互补全会写回配置文件)。
- 凭证在哪看:mp.weixin.qq.com → 设置与开发 → 基本配置。
- 先自检凭证通不通(不推任何东西):
push_article.py --account 我的公众号 --selftest。 - 换账号做三件事(不需要
--forget,旧号的登录态与模板一个字节都不会动):
① 在 accounts.json 加一个键(填新号的 appid/appsecret);② 所有命令加 --account 新键名;③ 用新号重抓模板:mp_login.py --account 新号 --login(用新号的微信扫码,登录态落到 accounts/新号/)→ fetch_templates.py --account 新号 --auto --catalog(模板落到 templates/新号/)。不要把某个号的模板复制给另一个号。素材(正文图/封面)由脚本自动重传,不要手工塞别号的 mmbiz 图片地址。
- 别把
accounts.json提交到仓库或分享出去(等同密码)。
⚠️ 两套凭证别混:
accounts.json(AppID/AppSecret)用于推草稿;
references/manual.md「① 抓模板」用的后台扫码登录态(mp_login.py)用于抓模板。两者独立,抓模板不需要 AppSecret,推草稿也不需要扫码。
全流程
⓪ 选模板 先定用哪一份(模板有多个,不选就是错)
① 抓模板 fetch_templates.py --auto 登录已自动化(mp_login.py 持久化会话);
后台有新增/改动模板时才做
② 出素材 gen_assets.py 岗位表 → 长图;封面;二维码(mp_assets)
②' 写正文 mp_layout.py 正文/附件区排版 helper —— **别手写裸 HTML**
③ 填充 build_article.py 配置驱动,只替换占位符(占位符清单按模板实测)
④ 自检 build_article.py --check
⑤ 推草稿 push_article.py 单篇;--multi items.json 推多图文(本轮草稿才可覆盖更新)
⑥ 回读核对 inspect_draft.py 查服务器上的真实形态(多图文加 --all;别靠后台预览下结论)⓪ 选择文章模板(每次编辑第一步)
后台「图文模板」有多个,各自版式/适用场景不同,必须先选定再动内容 ——不选就报错,脚本不会替你默认某一份。所有模板登记在 templates/catalog.json,模板 HTML 与清单都在技能内(templates/),换机器/换目录也能直接用。
# 1) 看有哪些模板、哪些能直接用(指定账号就只看该号的模板库)
python scripts/build_article.py --list-templates --account 我的公众号
# 2) 选定一个(短名,即 catalog 里的 key)
python scripts/build_article.py --account 我的公众号 --template 医护 --config content.json模板库按账号分目录:templates/<账号>/catalog.json(老机器的旧格式扁平库templates/catalog.json 里记 account,属于哪个号就继续给那个号用,向后兼容)。不带 --account 时用旧格式扁平库。
也可以把选择记在配置里(同一个模板下多篇同批稿件时省事;命令行 --template 优先):
{ "template": "医护", "title": "……", "fields": { } }现有模板(我的公众号 号,2026-09-14 重抓;模板随账号走,换号要重新抓):
| key | 模板 | 模式 | 状态 | 默认报名标签 | 适用场景 |
|---|---|---|---|---|---|
医护 | 医护招聘 | placeholder 占位符式 | ✅ 可直接用 | 「报名邮箱:」 | 医院/卫生院/疾控等招聘(招聘类通用) |
事业单位 | 事业单位(公务员) | placeholder 占位符式 | ✅ 可直接用 | 「报名网站:」 | 事业单位/公务员招考 |
模版示例 | 135 通用示例 | sample 示例 | — 仅参考 | — | 排版参考,不是招聘模板,不要发稿 |
模式是脚本对模板文件实测判定的(不靠 catalog 声明),所以后台模板改版后会如实反映:
placeholder:模板里写着「XX占位符」式占位符 → 可直接用;占位符清单也从模板实测
(不再是写死的 7 个),换一份占位符式的新模板照样能跑。
skeleton/sample:选中即SystemExit报错并给出替代方案,不会产出缺正文的半成品。
⚠️ 模板的默认报名标签因模板而异(医护=报名邮箱:/ 事业单位=报名网站:)。
拿哪个值当「未改写」的判据,由
template_default_apply_label(模板原文)实测得出,不是一个全局常量 —— 用全局常量会让换模板后的漏填静默通过(本技能真实踩过)。
🔑 模板属于哪个公众号,就只能用那个号发:模板里的图片是那次抓取时该账号的素材、
公众号名片也指向该账号。换号推送请用新号重新抓一份
(
fetch_templates.py --account 新号 --auto --catalog),**不要跨号复用,也不要把别人的模板复制过来改**(名片会指向错的号)。
模板名支持三种写法:短名
医护/ 文件名template_医护.html/ 任意路径
D:/x/template_医护.html(模板还在别处时用)。
① 抓模板(后台有新增/改动模板时才做)
后台模板只能带登录态调后台接口拿。登录已自动化,不用再手工 Copy as cURL:
# 首次(某个号):扫码登录一次(弹出浏览器窗口,用哪个号扫码就登哪个号)
python scripts/mp_login.py --account 我的公众号 --login
# 之后:全程自动(复用登录态,失效才弹一次浏览器)
python scripts/fetch_templates.py --account 我的公众号 --auto --catalog # 抓取 + 登记进模板库
python scripts/fetch_templates.py --account 我的公众号 --auto --relogin --catalog
# 只想看现有模板库(不需要登录)
python scripts/fetch_templates.py --account 我的公众号 --list📌 抓模板要的是「后台登录态」,不是
appid/appsecret—— 本技能两种凭据各管一段,别混:
| 环节 | 走哪个接口 | 需要什么凭据 | 要扫码吗 |
|---|---|---|---|
抓后台图文模板 fetch_templates.py | 后台网页接口 mp.weixin.qq.com/cgi-bin/appmsgtemplate?action=list | mp 后台登录 Cookie + 后台 token(形如 1370142500,不是 access_token) | 首次要,之后 --auto 自动复用 |
| 上传图片 / 推草稿 / 回读草稿 | 开放平台 API(media/uploadimg、draft/add、draft/batchget) | 只需 appid + appsecret(换 access_token)+ IP 白名单 | 不要 |
--list / build_article.py --list-templates 看本地模板库 | 纯本地读 catalog.json | 什么都不需要 | 不要 |
- 抓模板必须带 Cookie:图文模板是 mp 后台的东西,开放平台 API 里没有这个接口。
但不等于每次都要扫码:登录态由 mp_login.py 落盘持久化,--auto 会先校验再复用,失效才弹一次二维码(--status / --sessions 可先看是否还有效)。
- 反过来推草稿不需要 Cookie(appsecret 换
access_token就够)。所以
「后台加了 IP 白名单就能推草稿」与「抓模板仍要登录态」是两件互不影响的事。
会话由 scripts/mp_login.py 负责,机制与用法:
| 命令 | 作用 |
|---|---|
mp_login.py --account X --login | 扫码登录(该号专属会话),把 Cookie + token 落盘持久化 |
mp_login.py --account X --status | 只检查该号登录是否仍有效(不开浏览器),有效 exit 0 / 失效 exit 1 |
mp_login.py --account X --ensure | 默认动作:该号有效就直接用,失效才弹浏览器 —— 脚本内部都走这个 |
mp_login.py --sessions | 列出本机所有账号的登录态 + 各自是否有效 |
mp_login.py --account X --print | 打印该号 cookie / token(调试、人工复制用) |
mp_login.py --account X --forget | 只清该号的会话与 profile(别的号不动) |
mp_login.py --forget-all | ⚠️ 清掉本机所有账号的登录态 |
mp_login.py --migrate-legacy X | 把旧格式(不带账号)那唯一一份登录态认领给 X,一次性 |
换号只加
--account新号,不--forget:登录态各存accounts/<号>/,互不覆盖。
- 登录态存
~/.workbuddy/secrets/mp-wechat-backend/(浏览器 profile +session.json),
不进技能目录、不进仓库;每次取用前都会调接口校验一次,失效自动要求重登。
- 想换公众号:
--forget后--login,用目标公众号对应的微信扫码。 - 二维码会自动存成
login_qr.png并每 20 秒刷新(浏览器窗口看不到时可扫这张图)。
产出 templates/template_<模板名>.html(原样保存,不做任何处理),--catalog 会同时登记 key / file / appmsgid / 实测模式 / 实测占位符 / 默认报名标签,之后 --list-templates 就能看到新模板。人工写的 scene / notes 不会被覆盖。
Cookie 等同密码。脚本只存本机;不要复制进仓库、不要贴到群里。
备选的手工方式(接口没变时仍可用):
--cookie-file cookie.txt --token 1370142500。
② 出素材 + 写正文(正文 HTML 的唯一写法)
第一步(2026-09-17 新增,硬约束 4):先给采集 txt 做体检,看哪几篇要回源 ——正文明显偏短(< 300 字)或几乎全是 ![图片N] 的,先 WebFetch 回源取纯文本,别直接切图读(那类篇目切 15 片读图是本技能最大的单项消耗):
python scripts/scan_sources.py --dir "<采集目录>" # 退出码 1 = 有篇要先回源
python scripts/fetch_sources.py --dir "<采集目录>" --fetch # 批量抓 → _refetch/(--verify 校验)正文与附件区不要手写裸 HTML —— 手写会把色值/字号写散,改一次配色要满仓库找。技能提供这几个脚本/模块,正好覆盖这一步:
| 模块 | 干什么 |
|---|---|
scripts/scan_sources.py | 开工体检(第一步):给采集 txt 量体量,标出「正文 < 300 字 / 全是图 / 抓到站内导航」的篇目 → 先回源再写正文(见硬约束 4) |
scripts/fetch_sources.py | 批量回源(第二步):--fetch 本地抓原文 → 清洗落盘 _refetch/;--save/--import-dir 收 WebFetch 兜底稿;--verify 校验(POSTER 篇不算未完成) |
scripts/mp_layout.py | 正文排版 helper:p/li/li2/h1/h2/tip/card/contact/sign/img/quote/table/wrap_body/no_attach;色值全部取自 mp_template.STYLE。contact() 对投诉/监督类行自动不标红不加粗(2026-09-17 规则) |
scripts/mp_assets.py + scripts/gen_assets.py | 图片素材:岗位表长图 / 900×383 封面 / 二维码 |
scripts/selftest_rules.py | 规则回归自测:改标红/联系方式规则后跑一遍,校验「contact 自动降级 → audit 兜底拦截 → 回读检测」三处一致(退出码可直接接冒烟) |
写正文(复制 templates/article_def.example.py 改):
import sys; sys.path.insert(0, "<技能目录>/scripts")
import mp_layout as L
blocks = [
# 默认【不要】加 big_title:公众号标题是独立字段,正文再顶个大标题属重复。
# 2026-09-15 用户明确要求去掉(038 孟超肝胆医院样本)。确需时再加。
L.p("决定公开招聘公立医院编制员额备案管理人员**46名**。"), # **文字** 自动标红加粗
L.h1("一、招聘计划"),
L.li(L.hl("1.报名方式:统一在网上进行报名") + "(不设现场报名)。"), # A 类:整条标红
L.h2("(二)网上缴费"),
L.p("须于" + L.hl("2026年9月14日9:00 至 9月22日12:00") + "期间缴纳"
+ L.hl("100元考试费") + "。"), # B 类:只红关键值
L.tip("总成绩 = 笔试成绩 × 60% + 面试成绩 × 40%"),
L.img("jobs_1.png"), # -> images/jobs_1.png
L.contact([("咨询电话:", "0856—6623162")]),
L.sign(["XXX招聘工作小组办公室", "2026年9月10日"]),
]
L.build_body_file("body.html", blocks) # content.json 里用 "body_file": "body.html"正文末尾不写「来源:xxx」行 —— 信息来源统一由导读卡片的「信息来源」字段承载,
正文再写一遍与落款重复。自检会把「来源:」行报成问题(
mp_layout不提供该 helper)。
出图素材(岗位表长图 / 封面 / 二维码)—— 先定 spec,再一条命令:
python scripts/gen_assets.py --peek "附件1:…岗位表.xls" --header-row 3 # ① 看清列索引
python scripts/gen_assets.py --init assets.json # ② 生成 spec 样板
python scripts/gen_assets.py --spec assets.json # ③ 出图到 images/{
"source": {"file": "附件1:…岗位表.xls", "sheet": 0, "header_row": 3, "data_start": 4,
"columns": {"no": 0, "unit": 1, "code": 4, "name": 3, "num": 6}},
"tables": [{"title": "附件1:…(1/2 岗位·人数)", "out": "images/jobs_1.png", "font": 20,
"columns": [{"header": "序号", "key": "no", "width": 56},
{"header": "招聘单位", "key": "unit", "width": 168}]}],
"cover": {"line1": "江口县人民医院", "line2": "招聘46名", "sub": "9月14日—9月20日",
"out": "images/cover.png"},
"qr": {"url": "http://官网公告页", "out": "images/qr.png"}
}source.columns是「字段名 → 列索引(0-based)」,必须显式给(自动猜列会猜错)。tables[].columns[].key必须来自source.columns;width是像素,整表合计 ≈750 为宜。- 岗位表常有「顶部 2~3 行大标题 + 底部合计行」:用
header_row/data_start定位,
首列为空的行会被自动跳过(就是合计行)。
- 表格标题比表体宽时自动降字号;单元格自动折行。
.xls用 xlrd 1.2.0,.xlsx/.xlsm用 openpyxl,.csv直接读。
⚠️
columns的列索引是「位置」不是「名字」,写反了不会报错但数据全错位(2026-09-15 实测):本次 CSV 列序是
0序号 1招聘单位 2岗位名称 3岗位代码 4招聘人数,误写成
{"code": 2, "name": 3}后 —— 岗位名列渲染出「01」(岗位代码),且长图被拉到 1854px 高(错位单元格文字超长触发疯狂折行)。自检不报错,只有肉眼看图才发现。
配好 spec 后先跑一次出图并肉眼看:正常长图高度 ≈600px(10 行),
若出现异常高度 / 单元格竖排单字,九成是
columns对错了列。用
--peek看真实列号,或写个小脚本打印_to_dicts结果自查:raw, header = A.read_table(...) # ← 注意必须两个变量接 print(A._to_dicts(raw, columns)[0]) # 每个字段的值是否符合预期
⚠️
read_table()返回(rows, header)元组,必须两个变量接。写成
rows = read_table(...)会让rows变成元组,再传给_to_dicts()时索引整体错位(表现为每个字段都拿到整行内容),且不抛异常。
③ 填充
写一份 content.json(照抄 templates/content.example.json),先在其中写明用哪份模板(template 字段),或命令行用 --template 指定。字段值里用 **文字** 包住的内容会自动渲染成标红加粗 #FF0000——但只对正文内容用,信息导读的 5 个字段(来源/单位/人数/方式/时间)一律写纯文本,保持模板默认样式:
{
"template": "医护",
"title": "【江口】招46人!江口县公立医院招聘(9月14日-9月20日报名)",
"fields": {
"信息来源占位符": "江口县人民政府门户网",
"单位名字占位符": "江口县人民医院、江口县中医医院",
"报考人数占位符": "46名",
"报名方式占位符": "网上报名(唯一渠道)",
"报名时间占位符": "2026年9月14日9:00 — 9月20日17:00",
"正文占位符": "<section>……公告正文,重点处可用 **文字** 标红……</section>",
"附件占位符": "<p>……岗位表长图……</p>"
},
"body_file": "body.example.html"
}⚠️ 导读卡字段硬规则(2026-09-14 / 2026-09-16 用户要求),构建时有护栏告警:
报考人数占位符只写人数——如「46名」;括号补充一律不要(分单位/分岗位人数等细节正文里已有);原文没写明招聘人数 → 写「若干名」。
报名方式占位符一律把真实值写出来,不再写「(见正文)」(2026-09-16 用户三轮迭代后的终态,本批起生效):
实际报名方式 值写法 网站 / 网上报名 报名网址:https://ly.hnfnu.edu.cn/hrzpw/#/index(网址写全)邮箱报名 报名邮箱:yjji@sthj.shanghai.gov.cn多个邮箱 报名邮箱:caocsum@126.com、dy1888@csu.edu.cn(顿号「、」分隔)现场报名 现场报名(就这四个字,不带括号、不带地址)电话报名 电话报名(就这四个字,不带联系人姓名/手机号——号码放正文的联系方式行,2026-09-17 用户要求)组合方式 现场报名 / 报名邮箱:510129371@qq.com(用「 / 」连接)护栏
warn_card_field_rules()逐条查:含「见正文」/ 现场带括号 / 网站无网址 /邮箱无地址 / 多邮箱没用顿号 / 电话报名里掺了联系人姓名或手机号 → 告警。
⚠️ 不要反过来以「值太长」为由改回「(见正文)」 —— 值长度完全不受限
(2026-09-16 用户明确「不用检测移动端」,按手机视口/字间距估算宽度的那套判定
已从技能中彻底删除,别再去按宽度估算值框大小限制字段)。
护栏只保留内容类规则:报考人数括号、报名方式写全值、报名时间写法。
用户要的就是完整网址、完整邮箱进卡片(多邮箱也要列全),机构名也可以写全称。
报名时间占位符不写「报名」二字、不用中文括号(2026-09-16 用户要求):已知起止 → 「2026年9月15日 — 12月31日」;不知道开始日期 → 只写
「2026年12月31日截止」(参照「湖南省电子信息产业研究院」样本)。
起始是相对日期(「即日起」「自公告发布之日起」)→ 只写截止日期
「2026年9月30日截止」,别写「自公告发布之日起至2026年9月30日」。
原文按相对期限发布的(如上海市环境监测中心「自本公告发布之日起7个工作日内」)
照写,不要自己换算成具体日期(猜错日期比相对期限更糟)。
文章标题里的括注同理:写「(12月31日截止)」「(9月15日—12月31日)」,
不要再写「(9月15日—12月31日报名)」。
apply_label(模板固定「※如何报名※」模块那行)与导读卡口径完全一致:网站写「报名网址:<网址>」、邮箱写「报名邮箱:<地址>」(多邮箱顿号分隔)、
现场写「现场报名」。值长度不限。
📎 附件区:只认附件短链(2026-09-16 用户定稿)
判据是采集器给的附件短链存不存在,不是原文有没有写「附件1《岗位表》」:
| 情形 | 写法 | 结果 |
|---|---|---|
| 有短链(content.txt 有「附件短链:」) | 正文照常写 "attach": [L.no_attach()] 即可(短链由脚本挂) | 附件区 = 「附件:🔗 合集名」一行;描述文字与单独的「附件」标题自动删掉(岗位表长图保留) |
| 没有短链 | "attach": [L.no_attach()] | 判定为没有附件:附件区(「附件」标题、文字指引)与模板固定的「(若附件预览失败…)」提示整块删掉 |
⚠️ 「没有短链 = 没有附件」(2026-09-16 用户要求,贴图上海市环境监测中心样本):
用户看到稿子里还留着「附件 / 附件1《招聘简章》…请通过官网原文下载。/(若附件预览失败…)」
这一块,明确要求「如果没有附件短链则直接不再需要这块」「**附件检测也只检测附件短链
是否存在,不存在则判定没有附件**」。
所以
L.no_attach("附件1《岗位表》…")那种「按原文写文字指引」的写法已废弃(写了也会被清掉)—— 一律只看采集器给没给短链,不再按原文判断有没有附件。
有短链时:短链本身就是「点进原文看附件合集」的入口,再留描述行与「附件」小标题
就是重复信息。
build_article.build()在插短链前调mp_template.drop_attach_desc()自动清理(日志打印
[3a] 附件短链已挂载 -> 去掉附件文字描述/「附件」标题 N 处…):
- 只删纯文字块;带
<img>(岗位表长图)或<a>(短链本身)的块一律保留;- 「附件」标题既支持
<p>形式,也支持<section>/<h1..h6>包一层(整块纯文字才算,避免误删包着正文的大
<section>);
- 幂等,删干净后再跑不会重复删;
- 想保留时:
"attach_keep_desc": true保留描述行、"attach_keep_heading": true保留标题。
实现:
mp_layout.no_attach()返回""(不要写凑数说明文字);build_article.build()里无短链时清空附件区文字,并调
mp_template.drop_attach_hint()删掉模板固定的「(若附件预览失败,可发送至微信查看)」(日志 `[4a] 无附件短链(判定为没有附件)
-> 附件区整块不出现…`)。
ℹ️ 还有一条反向提醒:无短链但正文仍引用「附件1《…》」(如上海环境监测中心样本)
会打印
ℹ️ 正文仍引用附件名…附件区已整块删掉、读者没有入口—— 大概率是采集器漏给短链,确认后用
attach_link手工补。这是提示不是错误,正文引用附件名本身不用删。
正文太长时用 body_file / attach_file 指向独立文件(会覆盖 fields 里对应的值)——技能已附样板 templates/body.example.html 与 templates/attach.example.html,直接照抄改名即可;文件名写错时脚本会明确报错并给出两种改法,不会抛裸异常。
⚠️ apply_label:报名方式标签,必改
模板「※如何报名※」模块顶部有一行红字下划线加粗的标签,默认值由模板设计者写死、且因模板而异(医护=「报名邮箱:」/ 事业单位=「报名网站:」)。它必须按本次招聘的真实报名方式改写,否则会和正文、信息导读卡里的「报名方式」自相矛盾:
| 实际报名方式 | apply_label 写法 |
|---|---|
| 邮箱报名 | "报名邮箱:yjji@sthj.shanghai.gov.cn" |
| 多个邮箱 | "报名邮箱:caocsum@126.com、dy1888@csu.edu.cn"(顿号分隔) |
| 网站 / 网上报名 | "报名网址:https://ly.hnfnu.edu.cn/hrzpw/#/index"(网址写全) |
| 现场报名 | "现场报名"(就四个字,不带括号、不带地址) |
| 组合方式 | "现场报名 / 报名邮箱:510129371@qq.com" |
⚠️ 2026-09-16 迭代终态:三种方式全部写真实值,稿子里不再出现「(见正文)」。
沿革:「邮箱报名(hsgfrlzy@hnxxjt.com.cn)」中文括号式 → 「报名邮箱:<地址>」
→ 网站/现场也写全值(用户第三条指令)。
也可写成结构化形式,由脚本拼「<方式>:<值>」:
"apply_label": { "kind": "报名网址", "value": "https://ly.hnfnu.edu.cn/hrzpw/#/index" }
"apply_label": { "kind": "现场报名" }只换文字,外层红色+下划线样式原样保留(那是模板设计,不要动)。漏改会被自检拦下——audit 拿该模板实测的默认标签比对(如事业单位是「报名网站:」),命中即报问题,不会静默发出去;标签元素整个丢失也会报(build 日志里会打印模板默认值)。
🔗 content_txt:附件短链,采集器给了就必须挂
采集器(配套小程序)会在 content.txt 末尾追加一段「附件短链:」,里面是一个微信原生小程序文字链 a 标签:
# 附件短链:
<a class="weapp_text_link js_weapp_entry" data-miniprogram-appid="wx1234567890abcdef"
data-miniprogram-path="pages/previewfile/index?id=…" data-miniprogram-applink
data-miniprogram-nickname="小程序昵称" href="" data-miniprogram-type="text">附件合集名</a>把它挂到正文附件区最后一行(正文 → 附件标题/岗位表图片 → 「附件:🔗 合集名」),微信里就渲染成图例那种「附件:🔗 附件合集名」的可点击链接:
{
"content_txt": "<公告目录>/content.txt",
"attach_position": "end", // 默认 end=附件区最后一行;"start"=首行(旧行为)
"attach_name": "可选:覆盖短链显示文字"
}- 配了
content_txt,脚本自动提取并插入附件区最后一行;重复执行幂等,不会插两次。 - 也可以直接用
attach_link塞完整a标签(优先于content_txt)。 attach_name只改显示文字,class/data-miniprogram-*属性一个不动。- 采集器原文里没有这一段 → 什么都不做,附件区照旧(不是错误)。
⚠️ 与公众号名片同理:
class="weapp_text_link js_weapp_entry"和
data-miniprogram-appid/path/type少一个,微信就不再当小程序链处理(变成死链或纯文字)。所以本技能原样搬运,绝不重建 a 标签;自检会拦下缺属性的短链。
短链文字与本次公告对不上时用
attach_name改写,不要手工改 a 标签本体。
⚠️ 后台网页预览看不到蓝字+小程序图标 ≠ 没挂上。小程序渲染依赖微信客户端,
PC/浏览器环境不跑这套逻辑,只会渲染成普通文字。要判定是否真的挂上:
回读草稿查
weapp_text_link属性(draft/get后回读),或在后台点「预览」发到手机微信看。
python scripts/build_article.py --template 医护 --config content.json输出 article.html(正文,含 <div id="article">)与 preview.html(本地预览),并打印自检报告,第一行会写明用的是哪份模板([0] 模板: 医护(医护招聘) · 模式=placeholder),方便追溯。占位符对不上会直接报错,不会静默漏填。
④ 自检
单篇:
python scripts/build_article.py --check article.html --config content.json一批(推荐):preflight.py —— 推草稿前的总闸门。一次跑完「标题/摘要长度 + 封面存在 +逐篇正文自检 + 原文链接是否官网」,一篇一行出汇总表,退出码 1 = 不许推(可直接接冒烟):
python scripts/preflight.py --multi items.json --account 我的公众号⚠️ 摘要上限 120 字(建议 ≤115),超了
draft/add回45004且整条多图文一起失败——不是只丢摘要。
preflight.py与build_article.py --check --digest "…" --title "…"都会在推送前拦下。
必须全部通过再推。自检覆盖两类:
- 结构:占位符残留 0、所有图片都有
src、名片data-id完好、
附件短链属性齐全(appid/path/type 少一个即报问题)、本地图能在 HTML 所在目录解析到(解析不到 → 推送后微信直接丢图)
- 配色(见
references/style-spec.md):禁用色#E60012不出现、
粗体色只有规范内的黑/装饰绿、信息导读卡片内没有标红、正文里没有「来源:」行、投诉/监督类联系方式没有被标红加粗(应按规范走普通正文色);同时打印全部标红清单与「投诉/监督行 N 行」供人工核对
带上
--config才能定位信息导读卡片区,卡片样式检查才生效——建议每次都带。⚠️
--check也必须带--account <号>:不带就退回旧的扁平模板库去找模板,换号后的模板(在
templates/<号>/)找不到会**跳过「报名方式标签是否已改写」与「占位符残留」两项校验**,然后照样打印
✅ 自检通过(2026-09-15 实测:输出里只有一行「无法解析模板 …,跳过默认报名标签/占位符残留校验」的小字提示)。看到这行小字就说明
自检没覆盖全,补上
--account重跑。
⑤ 推草稿
单篇:
python scripts/push_article.py --account 我的公众号 --html article.html
--title "……" --digest "……" --cover images/cover.png
--source-url "http://官网公告页" --check多篇合成一条多图文(头条 + 次条,一次 draft/add,最多 8 篇)。批量生成直接用骨架 templates/batch_def.example.py —— 复制到项目目录,改三处(TEMPLATE / SAMPLE_ROOT / ARTICLES+ORDER),一条命令就出正文 + 封面 + 套模板 + 写 items.json(全篇自检不通过会标 ⚠️ 并留 build.log):
python batch_def.py # 全量;调试单篇用 --only 001(不会重写 items.json)逐篇生成 article_template.html(同一批用同一份选定模板)后,items.json 是一个按「头条→次条」排序的数组:
[
{"html": "…/01/article_template.html", "title": "头条标题",
"digest": "…", "author": "短名", "cover": "…/01/cover.png",
"source_url": "https://官网原文"},
{"html": "…/02/article_template.html", "title": "次条标题", "digest": "…"}
]python scripts/push_article.py --account 我的公众号 --multi items.json⚠️ 一条多图文必须是一次请求带 articles 数组;连发两次单篇得到的是
两条独立单图文草稿,之后合成不了。数组顺序就是头条→次条顺序。
只改样式规则(标红 / 不标红 / 配色)时不要重建正文:
python scripts/restyle.py --multi items.json --apply对已生成的 HTML 就地归一化,再按
--index逐篇draft/update原地更新草稿。为一条规则重建 8 篇正文≈ 多花 3.5 分钟 + 十几次工具往返(2026-09-17 实测)。
⑥ 回读核对(推完必跑)
python scripts/inspect_draft.py --account 我的公众号 --media-id <media_id>
python scripts/inspect_draft.py --account 我的公众号 --media-id <media_id> --all # 多图文逐篇+汇总表它直接调 draft/get 打印服务器上的真实形态:标题/作者/原文链接、图片张数(服务端统一存成 data-src,属正常)、名片 data-id、附件短链属性是否齐全、占位符残留、标红处数、报名方式标签是否已改写。加 --dump draft.html 可把正文存下来逐字比对,--json r.json 可把结果落盘。--all 会逐篇核对并打印「问题篇数 / 总篇数」汇总表。
排版参数与配色规范(速查)
完整规范见
references/style-spec.md(唯一事实来源) —— 这里只列必须立刻记住的;层级说明、生成写法、自检签名、导读卡「值」的写法规则都在那份文档里。
代码里的色值只在
mp_template.STYLE定义一次,任何脚本/文档都不要另抄一份。
段落:默认字体 16px / 两端对齐 / 行高 1.75 / 字距 0.5px / 段距 margin:10px 0 /边距 10~15px / 图片居中且 max-width:100%。
三层配色(互不越界):① 信息导读卡片字段值 #333333(模板默认,不加粗不改色);② 正文标题(h1/h2)纯黑 #000000、不加粗、不加竖条;③ 正文重点信息纯红 #FF0000 + 加粗。禁用色 #E60012(出现即报错);装饰绿 #0FBD8C / #1C4738 只用于卡片底与模板固定模块。
几条硬规则:
- 联系方式(
mp_layout.contact()):整条加粗标红、不加卡片底色与竖条;
但「投诉 / 监督类」不标红不加粗 —— 举报/投诉/监督/纪检/审计/信访/反映 打头的行由 contact() 自动降级(关键字见 mp_layout.PLAIN_LABEL_RE),手工 hl() 包它会被自检拦下(mp_template.PLAIN_CONTACT_RE,两处关键字表必须同步改)。
- 正文末尾不写「来源:」行(信息来源由导读卡片的「信息来源」字段承载)。
- 正文顶部不加重复大标题(标题由公众号「标题」字段承载,
L.big_title默认别用)。 - ⚠️ 动标题排版/大标题,必须同步
mp_template.TITLE_SIGNATURES/BIG_TITLE_RE,
否则对应检查静默失效。
标红判定(最容易搞错,按句式区分)
- A 条目式「编号 + 标签:值」→ 整条标红(编号、标签、值、句末句号一起进红色):
hl("3.报名网址:https://bm.bjpass.com/front/site/jkrsnq")—— ❌ 标签留黑只红值,与模板示例不符。条目后紧跟的补充说明保持黑色:li(hl("1.报名方式:统一在网上进行报名") + "(不设现场报名)。")
- B 叙述句 → 只红关键值:
p("…须于" + hl("2026年9月14日9:00 至 9月22日12:00") + "期间…缴纳" + hl("100元考试费") + "。")
- C 关键数字 / 政策亮点 → 只红该短语:
hl("46名")、hl("实行同工同酬")
拿不准 A 还是 B:看能否只留标签而句子仍然通顺——能,就是 A。
该红的项:人数、报名方式/时间/网址、资格初审时间、缴费时间与金额、准考证打印时间、
笔试时间、咨询电话、政策亮点。全篇 10~15 处为宜,不要成段染红。
禁止标红清单与完整判定规则见
references/style-spec.md二~四。
发布前校验清单
- [ ] 开工时跑过
scan_sources.py+fetch_sources.py --verify,退出码 0(见硬约束 4) - [ ]
python scripts/preflight.py --multi items.json --account <号>退出码 0 - [ ] 模板已选定:
--template <名字>或配置里template字段;同一批多篇用同一份模板
(混用两份模板会让版式不统一);--list-templates 里状态为 ✅ 的才能用
- [ ] 标题:省份用
【地区】关键信息!单位名招聘(报名时间);直辖市用【单位性质】… - [ ] 标签:从 国央企 / 事业单位 / 名企招聘 / 教师招聘 / 医护招聘 / 银行招聘 中选 1 个
- [ ] 原文链接:必须官网,不得用公考雷达等第三方链接
- [ ] 引流:文中二维码不得指向竞品
- [ ] 附件短链:
content.txt里有「附件短链:」就必须挂(配content_txt),
挂在附件区最后一行(attach_position 默认 end);文字与本次公告对不上用 attach_name 改写;仅当短链指向竞品、或确与本次公告无关时才删
- [ ] 排序:首篇应为省会国企;考公考编期可让事业编顶头条
- [ ] 报名方式标签:
apply_label已按实际方式(网站/邮箱/现场)改写,非该模板的默认值
(医护=「报名邮箱:」/ 事业单位=「报名网站:」;build 日志会打印模板默认值)
- [ ] 附件区:只认附件短链(2026-09-16)—— 有短链 → 附件区只剩「附件:🔗 合集名」
一行(描述与「附件」标题自动删);无短链 → 附件区与「(若附件预览失败…)」提示整块不出现(正文统一写 L.no_attach(),不要写文字指引)
- [ ] 草稿箱:本轮只新建/只动自己新建的那条(见硬约束 1)
参考文档(按需读,不必全读)
给人看的完整手册:references/manual.md(一页速览 / 七步详解 / 配置速查 / 素材与排版 / 批量多图文 / 排错表 / 实战示例);新环境、新账号另见 references/onboarding.md(六步向导 + 换公众号只动三处)。
| 什么时候读 | 读哪份 |
|---|---|
| 报错、结果反常、怀疑走错路 | references/pitfalls.md(常见误判大全:40001 / 40007 / 45004 / 47001 / 登录态 / 附件短链点不动…) |
| 改代码、动模板结构、追问「为什么这样实现」 | references/internals.md(关键实现说明 26 条,含占位符、名片保护、附件判定、账号隔离) |
| 配色 / 标红该不该红 / 标题签名 / 导读卡「值」怎么写 | references/style-spec.md(唯一事实来源) |
| 占位符含义、模板库与三种模式、模板模块地图 | references/template-placeholders.md |
| 模板占位符示意图(让用户建模板时展示) | references/images/template-placeholders.png |
脚本(都在 scripts/):
| 脚本 | 作用 |
|---|---|
mp_setup.py | 首用向导 / 环境体检:--step 1..6 逐步执行,裸跑 = 六步总览并给 NEXT= |
build_article.py | 套模板出正文 + 自检:--config 构建、--check 只检(可带 --digest/--title 验长度)、--list-templates |
scan_sources.py | 开工第一步:给采集 txt 做体检,标出「正文太短 / 全是图 / 抓到导航」的篇目 → 先回源;退出码 1 = 有篇要回源 |
fetch_sources.py | 开工第二步(批量回源):--fetch 本地抓原文 → 清洗落盘 _refetch/(零模型消耗);--verify 校验;POSTER 篇给结论并把图片 URL 落盘 |
preflight.py | 推草稿前一键预检:标题/摘要长度 + 封面 + 逐篇正文自检 + 原文链接官网性;退出码 1 = 不许推 |
restyle.py | 对已生成 HTML 就地归一化样式规则(标红/降级),改样式不必重建正文;幂等 |
push_article.py | 推草稿入口(护栏:推送前自检 + 不许误改别人草稿;--index 原地改多图文中的一篇) |
inspect_draft.py | 草稿回读核对(属性级 + --all 逐篇汇总表),推完必跑 |
mp_push.py | 推送核心:凭证解析 / token 缓存 / 传图 / HTML 清洗 / draft/add|update(单篇与多图文都在这) |
mp_layout.py | 正文排版 helper(p/li/h1/h2/tip/card/contact/sign/img/wrap_body/no_attach)——正文 HTML 的唯一写法 |
mp_assets.py + gen_assets.py | 图片素材:岗位表长图 / 900×383 封面 / 二维码(--spec --init --peek) |
mp_login.py | 后台登录态(按账号隔离;扫码一次自动复用)--login/--status/--ensure/--forget |
fetch_templates.py | 抓取后台模板(--auto --catalog);同名模板不会互相覆盖 |
selftest_rules.py | 规则回归自测(改正则 / 改规则后跑一遍,退出码可接冒烟) |
数据与样板:accounts.json(凭证,别提交仓库;样板 templates/accounts.example.json)· templates/<账号>/catalog.json(模板库清单,--list-templates 读它)· templates/template_*.html (模板本体,随技能走)· templates/article_def.example.py / batch_def.example.py / assets.example.json / content.example.json / body.example.html / attach.example.html(复制到项目目录改,batch_def 骨架改三处即可跑一批多图文)。