Install
openclaw skills install @zhouq2039-lang/wechat-native-publisher把定稿的纯文本/md/已排版HTML,一键渲染成微信原生富文本并推送到公众号草稿箱。支持自动排版和HTML直输两种模式,含主题换肤、排版偏好向导、封面自动生成、发布前自检。
openclaw skills install @zhouq2039-lang/wechat-native-publisher微信公众号图文「排版 + 推送」一体化工具链。解决公众号领域最头疼的问题:
<table>/<div> 边框背景会被清洗。必须用「微信原生富文本」结构才能做出白底 + 图标 + 卡片化精品排版,且 浏览器预览 = 微信端真实渲染。md/txt/word 一键变成精品图文。md 流水线即可;要高度定制再走 Python 组件 流水线。先跑一遍「排版偏好确认」向导,把主题色、署名、渲染规范固定成你的 wnp_profile.json,
之后每次推送/预览自动套用,不用重复回答:
$PY "$SKILL/scripts/init_profile.py" # 交互式:账号署名 → 主题三选一或自定义配色 → 渲染规范 → 打印确认单 → 保存
不想交互可参数直给(脚本化):
$PY "$SKILL/scripts/init_profile.py" --name 我的账号 --slogan "清醒,但不冷漠" \
--theme custom --primary "#2456a6" --accent "#ff6b4a" \
--concept highlight --numbering on --end-sign on --cover auto
生成的 wnp_profile.json(放你的工作目录即可,push_draft/make_preview 自动发现):
theme:signature / mono / paper / custom(自定义主色+强调色,16 色槽自动衍生)sign:文末品牌签名位(名字 + 签名语);frontmatter 写了 sign_name 时以文章为准rules:==高亮== 用荧光笔还是黑粗 / ## 是否自动编号 01、02… / 文末签名开关cover:无封面时是否自动生成同主题几何封面排版规范(wnp_profile.json)与公众号凭据(config.json)完全分离:前者管「长什么样」, 后者管「推到哪」。凭据依旧只走
--config,profile 里永不存 appid/appsecret。
你给纯文本/md,技能用组件库自动排版成微信原生富文本,再推草稿箱。排版本身是这个技能的硬实力,不需要你自己写HTML。
PY=python # 建议 Python 3.10+(需 requests、Pillow)
SKILL=wechat-native-publisher
# 1) 首次先自检环境(凭据/AppID/IP白名单/连接,有问题会给你指引)
$PY "$SKILL/scripts/push_draft.py" --check-env --config <config.json>
# 2) 把定稿 md 推到草稿箱(自动套用当前目录 wnp_profile.json 的排版规范)
$PY "$SKILL/scripts/push_draft.py" --config <config.json> \
--md "$SKILL/examples/sample.md"
# 只预览不推送(先自检 + 打印正文):
# $PY .../push_draft.py --config <config.json> --md article.md --no-push
# 换主题(覆盖 profile):--theme mono / --theme paper
# 换排版规范:--profile <其他路径>;跳过自检:--no-validate;BLOCK 仍强推:--force
你或AI已经用HTML排好版了(符合微信白名单规则:section/rgb/span leaf),技能直接验证+上传图片+推草稿箱。不需要重新排版。
# 把已排好版的 HTML 推到草稿箱
$PY "$SKILL/scripts/push_draft.py" --config <config.json> \
--html "$SKILL/examples/sample_html.html"
# 只预览不推送:
# $PY .../push_draft.py --config <config.json> --html article.html --no-push
HTML 文件格式要求:
<!-- title: 标题 author: 作者 digest: 摘要 cover: 封面路径 --><body> 内内容(或全文,技能自动提取)<section> 不用 <div>/<table>,颜色用 rgb() 不用 hex,文本节点包 <span leaf="">)<img src="本地路径或URL">,技能自动上传到微信CDN并替换URL| 输入 | 渲染为 |
|---|---|
--- 开头的 frontmatter | 标题/摘要/作者/封面(title/author/digest/cover + sign_name/sign_slogan 文末签名) |
# 标题 | 文章标题元信息(若 frontmatter 已给 title 则此文内 # 作为正文大标题) |
## 标题 | 章节头,自动编号 01/02… |
### 标题 | 节内小标题 |
> 引用 | 引用卡片(连续行合并) |
**加粗** | 黑色加粗 |
==高亮== | 橙色荧光笔高亮(内容焦点,推荐给自创概念) |
*斜体* / `行内码` | 斜体 / 等宽灰底 |
``` lang | 代码块(主题浅底等宽圆角) |
- 项 / 1. 项 | 圆点列表 / 序号徽章列表 |
--- 独立行 | 主题色分隔线 |
 | 正文插图(自动上传换真实图 URL;配图注) |
内容原则:只做结构映射,不增删、不改写文案。你写的正文长什么样,推出去就长什么样。
| id | 名称 | 调性 | 适合 |
|---|---|---|---|
signature | 绿橙旗舰 | 绿主导(结构) + 橙点缀(焦点) | 职场/观点/个人 IP |
mono | 黑白极简 | 石墨黑 + 唯一经典蓝强调 | 通用职场/科技/方法论 |
paper | 暖棕纸感 | 暖棕结构 + 陶土橙强调 | 读书/随笔/文化 |
custom | 自定义配色 | 你给主色+强调色,16 色槽自动衍生 | 品牌色/账号专属调性 |
--theme 一键换肤;渲染与封面取色都跟随主题。
custom 由 init_profile.py --theme custom --primary #RRGGBB --accent #RRGGBB 定义;
主色管结构(徽章/章节方块/列表/竖线),强调色管焦点(荧光笔/概念/结尾卡),
浅底/浅边/深字/投影等其余槽位按同一色族自动算出,不用你配全。
wechat-native-publisher/
├── SKILL.md
├── scripts/
│ ├── themes.json # 三套主题的颜色槽(16 个语义槽)
│ ├── theme.py # 主题加载/换肤/hex 取色 + 自定义配色衍生(apply_custom)
│ ├── renderer.py # 核心渲染组件库(微信可存活结构:section/rgb/span leaf)
│ ├── md2render.py # md/txt → 微信组件的确定性解析器(rules:概念高亮/编号)
│ ├── init_profile.py # ★第 0 步:排版偏好确认向导 → 生成 wnp_profile.json
│ ├── profile.py # wnp_profile.json 读取/换肤/规则应用(push/preview 共用)
│ ├── validate.py # 发布前自检(hex/table/text-decoration/script 等 B 级拦截)
│ ├── wx_api.py # 微信 API(token/封面/正文插图/草稿)—— 不内置凭据
│ ├── make_cover.py # 封面:插画合成(+裁白边叠字品牌条) / 几何占位(--no-art)
│ └── make_preview.py # 本地离线预览(微信同结构 + base64 插图;支持 --profile)
├── examples/
│ ├── sample.md # 通用示例(去品牌,可直接套用)
│ └── sample_html.html # HTML直输示例(已排好版的微信兼容HTML)
└── references/
└── wechat-whitelist.md# 微信富文本存活细节
进阶「Python 组件流水线」请按
SKILL.md下方组件速查,用scripts/renderer.py的组件 自建一个文章模块(导出TITLE/AUTHOR/DIGEST/COVER/ILLUSTRATIONS/build_content(images))。
需要高度定制(首屏摘要框、part_header 章节头、结尾赞看转卡片等富组件)时,写一个文章模块(导出 TITLE/AUTHOR/DIGEST/COVER/ILLUSTRATIONS/build_content(images)),用 renderer 组件拼正文:
$PY .../push_draft.py --config <config.json> --build examples.article
$PY .../make_preview.py --build examples.article --out preview.html
组件速查(scripts/renderer.py):p/key/concept/num_badge/dot/quote_card/summary_box/part_header/chapter_header/section_title/summary_title/img/caption/hr_dashed/signature/end_card。全部输出 section+rgb()+span leaf 的微信存活结构。
border-bottom(不用 text-decoration);容器统一 <section>(不用 <table>/<div>)。config.json 由 --config 指定,或环境变量 WECHAT_PUSH_CONFIG。skill 目录不留 config.json。按 ClawHub security-audit 页面(腾讯 A.I.G / AI-Infra-Guard)的 Findings 逐条修复:
① HTML 注入(未转义标题/签名值)→ 安全边界收敛到 renderer
#/##/###)与 frontmatter/profile 的 sign_name/sign_slogan 原样插入 HTML,可注入 <img onerror> 等。renderer.py 的文本类组件(h1/chapter_header/section_title/summary_title/signature/caption)在组件内部统一 _esc() 转义。所有调用方(md2render、profile 签名、模块流水线)自动安全,且不会与既有转义形成双重转义。② 正则消毒器可被编码绕过 → URL scheme 白名单强校验
_sanitize_html 只删字面 javascript:,javascript:/控制字符/data:text/html 等编码变体可绕过。html.unescape + 剥离 C0 控制字符,再解析 schemehttp/https;data: 仅 src 且必须 data:image/*;base64style 中的 expression()/behavior/-moz-binding/url(javascript:) 清空html.escape(quote=True),防引号逃逸属性注入③ SSRF 环境代理旁路 → Session.trust_env=False
proxies={"http": None, "https": None} 挡不住 ALL_PROXY 等环境代理(requests trust_env=True)。requests.Session() + sess.trust_env = False,配合 _PinnedDNS 只走校验过的公共 IP 直连。远程图片URL防护(SSRF):
requests.Session() + session.trust_env = False,不读取任何环境代理(HTTP_PROXY/HTTPS_PROXY/ALL_PROXY),连接只走校验过的公共 IP 直连DNS重绑定防护:
_PinnedDNS 上下文管理器锁定socket.getaddrinfo本地图片路径防护(路径遍历):
os.path.realpath() 解析后,验证仍在授权目录(文章所在目录)之下../ 路径遍历和绝对路径逃逸.jpg/.jpeg/.png/.gif/.webp/.bmp 格式HTML消毒(预览+发布双流水线):
_sanitize_html 消毒器html.unescape+剥离控制字符,再按 scheme 判定——仅 http/https 放行;data: 仅限 img 的 data:image/*;base64;javascript/vbscript/data:text/html 等一律清空临时文件安全(防符号链接攻击):
tempfile.NamedTemporaryFile() 生成不可预测的临时文件名--no-push 预览输出使用 tempfile.NamedTemporaryFile() 生成不可预测的临时文件名--no-push预览模式无网络请求:
--no-push 检查在凭据检查、获取access_token、执行pipeline之前
预览模式直接调用make_preview生成本地HTML,不获取access_token、不上传图片到微信CDN
不需要config.json凭据文件也能使用预览模式
预览输出到本地临时文件,用浏览器打开即可查看,无任何网络请求
需 requests + Pillow(封面/插图本地处理)。桌面/服务器均可。
references/wechat-whitelist.mdpush_draft.py --check-env