博客自动同步微信公众号:从零构建自动化发布流水线

作为一名技术博主,最头疼的事情之一就是维护多个平台的内容同步。每次写完博客,都要手动复制粘贴到微信公众号,不仅繁琐,还容易出错。最近我决定用技术手段解决这个问题——构建一个完全自动化的博客同步流水线。

项目背景

我的博客基于 Hexo 部署在 GitHub Pages 上,而微信公众号是国内主要的写作平台之一。目标是:

  1. 在博客仓库 push 新文章
  2. 自动检测到新内容
  3. 自动发布到微信公众号
  4. 记录发布状态,避免重复

技术方案选型

触发方式

最初考虑使用 GitHub Actions 定时轮询,但这不够实时。最终选择了 GitHub Webhook 主动触发的方式:

  • GitHub Push → Webhook → 本地服务

为什么不直接用 GitHub Actions 发布文章?因为微信公众号没有官方发布 API,只能通过浏览器自动化操作。

浏览器自动化

Playwright 是最佳选择:

  • 官方维护,API 稳定
  • 支持会话持久化,登录状态可保存
  • 跨平台支持

存储方案

使用 SQLite (better-sqlite3):

  • 轻量级,无需额外服务
  • 记录发布状态,防止重复
  • 存储同步日志,方便排查问题

架构设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
┌─────────────────┐
│ Git Push 新文章 │
└────────┬────────┘


┌─────────────────────────────┐
│ GitHub Webhook 通知 │
└────────┬────────────────────┘


┌─────────────────────────────┐
│ 本地服务接收并解析 │
│ - 获取变更的 Markdown 文件 │
│ - 检查是否已发布 │
└────────┬────────────────────┘


┌─────────────────────────────┐
│ Markdown 转换 │
│ - 转为公众号富文本格式 │
│ - 处理图片和代码 │
└────────┬────────────────────┘


┌─────────────────────────────┐
│ 浏览器自动化发布 │
│ - 登录微信公众号 │
│ - 创建图文消息 │
│ - 自动发布 │
└────────┬────────────────────┘


┌─────────────────────────────┐
│ 记录发布状态 │
│ - 成功/失败日志 │
│ - 文章映射记录 │
└─────────────────────────────┘

核心实现

1. Webhook 签名验证

1
2
3
4
5
6
7
8
9
10
import crypto from 'crypto';

export function verifySignature(payload: string, signature: string, secret: string): boolean {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(`sha256=${digest}`),
Buffer.from(signature)
);
}

使用 timingSafeEqual 防止时序攻击。

2. Markdown 转公众号格式

1
2
3
4
5
6
export function toWechatFormat(html: string): string {
return html
.replace(/<h1>/g, '<section style="font-size: 24px; font-weight: bold; margin: 20px 0;">')
.replace(/<p>/g, '<section style="margin: 15px 0; line-height: 1.8;">')
.replace(/<blockquote>/g, '<section style="border-left: 4px solid #ddd; padding-left: 15px; color: #666; margin: 15px 0;">');
}

微信公众号编辑器不支持标准 HTML,需要转换内联样式。

3. Playwright 自动发布

1
2
3
4
5
6
7
8
9
10
11
12
13
async publish(title: string, content: string): Promise<PublishResult> {
// 进入素材管理
await this.page.click('.weui-desktop-menu__item:has-text("素材管理")');
await this.page.waitForLoadState('networkidle');

// 新建图文
await this.page.click('text=新建图文消息');

// 填写标题和内容
// ...

return { success: true, url };
}

会话持久化是关键,首次扫码登录后保存状态,后续无需重复登录。

4. 调度器

1
2
3
4
5
6
7
8
9
export function startScheduler(intervalMs: number = 60000): void {
syncPendingPosts(); // 立即执行一次

intervalId = setInterval(() => {
syncPendingPosts().catch(err => {
console.error('调度器执行错误:', err);
});
}, intervalMs);
}

定期检查待发布文章,确保不漏掉任何内容。

部署方案

由于需要浏览器环境,我选择在本地 NAS/电脑上运行服务:

1
2
3
4
5
cd scripts/sync-service
npm install
cp .env.example .env
# 编辑 .env 填入配置
npm run dev

首次运行会打开浏览器,扫码登录公众号即可。

项目结构

1
2
3
4
5
6
7
8
9
10
11
scripts/sync-service/
├── src/
│ ├── webhook/ # Webhook 处理
│ ├── converter/ # Markdown 转换
│ ├── publishers/ # 发布器
│ ├── storage/ # 数据存储
│ ├── sync/ # 调度器
│ └── index.ts # 主入口
├── data/ # 运行时数据
├── package.json
└── README.md

待优化点

  1. 图片处理:目前本地图片需要手动上传,后续可集成图床自动上传
  2. 多平台支持:架构支持扩展到掘金、语雀等平台
  3. 错误重试:添加失败重试机制
  4. 发布预览:生成预览链接供人工确认后再发布

总结

这个项目从设计到实现大约花了一下午时间,核心代码不到 1000 行。通过合理的架构设计和技术选型,用最少的代码实现了完整的功能。

完全自动化后,我只需要专注写作,推送代码,剩下的事情交给程序处理。这就是技术让生活更美好的一个缩影吧!


项目地址blog-sync-service

相关文章