Hexo 文章一键发布到微信公众号

博客写完,还要打开微信公众号后台,把 Markdown 粘进网页版排版工具,选主题、挑代码高亮,再把十几张图片一张张传上去,最后设封面、填原文链接……一篇文章光搬运就得半小时,搬完常常还发现图串了、代码块样式乱了。

后来我把这一整套流程压成了一条命令:

这条命令会:

  1. 自动挑出 source/_posts 下最近改动的那篇文章——未提交的改动优先,没有就回溯到最近一次改过文章的提交;
  2. 补齐 front-matter 里缺的默认值:封面、作者、原文链接、留言开关;
  3. 把正文里的站内路径 /gallery/... 换成磁盘绝对路径,并把微信不支持的 webp 转成 png / jpg;
  4. 通过 stdin 把处理好的内容交给文颜,由它渲染排版、上传图片、创建草稿。

选型

我要的功能很明确:读 Markdown → 渲染成公众号兼容的 HTML → 批量上传图片 → 创建草稿。这类工具其实不少,但绝大多数是网页版,用完还是得手动复制粘贴,没解决”搬运”这件事。

最后选了命令行工具文颜(wenyan-cli)(npm 包名是 @wenyan-md/cli):本地渲染、支持自定义主题和代码高亮、能调微信素材接口上传图片并建草稿,而且支持从 stdin 读取内容,天生适合做脚本的下游。

安装

下文统一用 wenyan 来调用它。

先拿到 AppID 和 AppSecret

两个前提:

  • 管理员身份:必须用该公众号绑定的管理员微信扫码。
  • 账号类型:订阅号、服务号都行。未认证的个人订阅号也能拿到凭据。

打开 微信开发者平台。

AppID 有两个入口,看到的都是同一个值:

  • 微信开发者平台:「我的业务」→「公众号」→「基础信息」,明文显示,可一键复制;
  • 公众平台后台:mp.weixin.qq.com → 设置与开发 → 账号设置 → 注册信息。

AppSecret 走这条路:

  1. 打开 https://developers.weixin.qq.com/platform,管理员微信扫码登录;
  2. 顶部「我的业务与服务」→ 点「公众号」进详情页;
  3. 切到「基础信息」Tab,下滑找到「开发密钥」;
  4. AppSecret 旁点「启用」(首次)或「重置」,再次扫码验证,弹出完整密钥。

三个必须记住的点:

  • 只显示这一次。平台不保存明文,关掉弹窗就再也看不到,只能重置。
  • 重置会让旧密钥立即失效。如果已经有脚本在用,重置前先想清楚。

配 IP 白名单

就在同一页、AppSecret 正下方有个「API IP 白名单」,点「编辑」填入服务器或本机的出口公网 IP(多个用换行或逗号分隔)。旧入口在 mp 后台「设置与开发 → 安全中心 → IP 白名单」,两处改的是同一份名单。

不知道自己 IP?访问 https://myip.ipip.net 看一眼。

坑点:

  • 家用宽带 IP 会变,隔段时间可能又要重配;
  • 不配会报 40164 invalid ip xxx not in whitelist;获取 token 时被拦则报 61004。

把凭据交给 wenyan

文颜提供了交互式命令:

它会依次问 AppID、AppSecret(输入不回显),再问一个可选的别名(用来简化 AppID 的显示),然后写进配置文件。想知道文件到底存在哪,执行:

注意 文颜官网 上提供的命令 wenyan credential add 是错的。

Windows 下默认是 %APPDATA%\wenyan-md\credential.json。

找出”最该发布的那篇文章”

我不想每次都手动指定文件,于是定了两条规则:

  1. 工作区里有未提交的 source/_posts/**/*.md,说明刚写完一篇,取其中磁盘修改时间最新的;
  2. 没有未提交的,就交给 git,找最近一次改动过 source/_posts 下 md 的提交,再从涉及的文件里取最新的。

两个细节:git status --porcelain 每行文件名前有 3 个字符的状态位,得 slice(3) 去掉;重命名会输出 old -> new,要取箭头右边的;git 默认会转义非 ASCII 文件名,加 -c core.quotePath=false 才对。

用 stdin 喂进去,不落盘

文颜支持管道,可以通过脚本直接将文章传给文颜:

用 process.execPath + CLI 的入口文件路径来调,而不是直接敲 wenyan,这样不依赖 PATH,也不会因为不同 shell 找错二进制。默认参数写死在脚本里,命令行传的额外参数追加在后面,想换主题直接覆盖:

补齐 front-matter 默认值

公众号草稿强制要求封面,另外我还想带上作者、原文链接和留言开关。这些在每个 md 里抄一遍太啰嗦,让脚本补:

原则是只补缺失的字段、已有的不覆盖,而且只改内存里的字符串,原始文件一个字节都不动。source_url 由文章相对 source/_posts 的路径拼出来,正好等于 Hexo 生成的永久链接。

站内绝对路径要换掉

正文里的图片写的是 /gallery/2026/xxx.webp 这种站内绝对路径——浏览器认,但文颜是把它当本地文件解析的,直接报找不到。所以发布前统一换成磁盘绝对路径:

正则只匹配 ![](...)、src="..."、front-matter 列表项和 cover: 这几种位置,免得误伤代码块——我的文章里就写过 media_folder: "source/gallery/2023" 这种配置片段。

微信不收 webp

第一次真跑就翻车了:

微信素材库不支持 webp,而我的图床里存的基本都是 webp。那就用 sharp 在发布前转一道:有透明通道的转 png,其余转 jpg(无损的 PNG 体积往往大不少)。

顺序很重要——先把 /gallery/ 换成绝对路径,再扫 .webp 转换,不然扫不到文件。

转换结果按「路径 + mtime」缓存到系统临时目录,同一张图不会每次发布都重转;文颜自己也有一份上传缓存,同一张图不会重复上传。

踩坑记录

  • AppID:文颜只有显式传 --app-id 时才去读凭据文件,不传就直接报”未提供 AppID”。所以得先从 credential.json 里把公众号 id 取出来传进去。
  • 封面:草稿强制要有封面,正文一张图都没有时会直接失败(”你必须指定一张封面图或者在正文中至少出现一张图片”)。默认封面就是为此准备的。

最后

现在写文章 → commit,然后

草稿箱里就躺着排好版、图片传好的草稿,剩下的只有人工复核和点群发。自动群发我没做——草稿和群发之间,我还是想留一步人工确认。

参考

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论