暂未播放
0:00
0:00

如何搭建企业微信机器人,用机器人发博文、动态

1870 字
9 分钟
如何搭建企业微信机器人,用机器人发博文、动态

没服务器又想随手发博客,这篇记录我搭的企业微信机器人:给机器人发一条消息,几分钟后它就变成博客上的一篇文章,全程不碰后台。

整体是怎么跑起来的#

先看整条链路。从你发消息到文章上线,中间一共四跳:

企业微信(收消息 + 被动回复)
│ 解密后的 XML
EdgeOne 边缘函数(解密 / 验签 / 加密回包)
│ /internal 转发(hybrid 模式)
Cloudflare Worker(攒草稿 → 拼 markdown → 写 GitHub)
│ commit 一个 .md 文件
GitHub(Actions 自动构建部署博客)

最绕的是「被动回复」。企业微信要求你在收到回调后 5 秒内把回复写进响应体,而且必须是加密的 XML。真实的写博客动作(调 GitHub API、等部署)绝对不能在 5 秒里同步做完,否则超时企微就当你没回。

我的做法是:边缘函数收到消息,先解密、立刻拼一句「已记录」加密回包返回(满足 5 秒),再在后台把内容丢给 Worker 去写 GitHub。回不回消息和写不写文章是两条线,互不阻塞。

Warning

EdgeOne 边缘函数,http 响应返回之后,普通 fetch 会被运行时强制终止。 向后端 Cloudflare Worker 转发任务,必须使用 event.waitUntil(fetch(…)) 包裹转发逻辑。 如果不包,企微能收到 “已记录” 回复,但后续写 GitHub 逻辑直接不执行,很难排查。


踩坑实录#

坑一:以为是源站被墙,其实是加密算法写错了#

最早我全放在 Cloudflare 上,企业微信回调死活访问不通。当时直觉是「CF 源站在国内被掐了」,于是把入口迁到 EdgeOne(国内 CDN,理论上更稳)。

结果迁过来还是不通。最后一行行查,发现是我自己写的 AES 加解密有 bug——企微回调用的是 AES-256-CBC + PKCS7,密文前面还要拼 16 字节随机串和 4 字节消息长度,回包还要按它的格式再加密一遍。只要哪一步字节对不上,企微就直接当你非法请求,表现就是「回调不通」。

修好之后我才确认:Cloudflare 源站本来是通的,当初根本不用换入口。为了不依赖任何第三方库(CF / EO 的运行时都不一定让你装包),我直接用了纯 JS 实现,核心是拿 Web Crypto 的 AES-ECB 当 AES-256-CBC 的轮函数,自己拼 CBC 链和 PKCS7 填充:

// // Web Crypto 不直接提供 AES‑256‑CBC,基于原生 AES‑ECB 手动实现 CBC 模式,无第三方依赖
async function aes256cbc(key32, iv16, data, decrypt) {
const wkey = await crypto.subtle.importKey("raw", key32, "AES-ECB", false, ["encrypt", "decrypt"]);
const pad = (b) => { const n = 32 - (b.length % 32); const p = new Uint8Array(n).fill(n); const o = new Uint8Array(b.length + n); o.set(b); o.set(p, b.length); return o; };
const unpad = (b) => b.subarray(0, b.length - b[b.length - 1]);
let prev = iv16, out = new Uint8Array(decrypt ? data : pad(data));
const step = decrypt ? data.length / 16 : out.length / 16;
for (let i = 0; i < step; i++) {
const blk = out.subarray(i * 16, i * 16 + 16);
const d = new Uint8Array(await crypto.subtle[decrypt ? "decrypt" : "encrypt"]({ name: "AES-ECB" }, wkey, blk));
for (let j = 0; j < 16; j++) d[j] ^= prev[j];
if (decrypt) prev = blk.slice(); else { prev = d.slice(); out.set(d, i * 16); }
}
return decrypt ? unpad(out) : out;
}

这段在 Cloudflare Worker 和 EdgeOne 边缘函数里各放了一份,逻辑完全一样,企微都能正确解开。也是因为它没有 npm 依赖,后面做纯 CF / 纯 EO 才能直接复制过去用。

坑二:回包里多写一个 AgentID,企微直接静默丢弃#

这个坑最麻烦,因为它不报错。

被动回复的 XML 结构企微是有标准 schema 的,里面没有 AgentID 这个字段。我当时顺手把 agentid 也塞进了回包(想着反正带上呗),结果企微收下后什么都不回、什么都不报错,表现就是「收不到任何回复」。我对着日志看了半天,加密对了、验签对了、回包也返回 200 了,就是没动静。

最后发现是那个多余的 AgentID 字段让企微判定回包非法,整条静默丢掉。把回包 XML 里的 AgentID 删掉、重部署,机器人就能收到消息了。

// 回包内层 XML:千万别带 AgentID
const xml = `<xml>
<Encrypt><![CDATA[${encrypt}]]></Encrypt>
<MsgSignature>${sig}</MsgSignature>
<TimeStamp>${ts}</TimeStamp>
<Nonce>${nonce}</Nonce>
</xml>`;

机器人到底能干什么#

指令集现在是这样的,发文字给它就会攒成一个草稿,指令控制行为:

指令作用
/done/发布把当前草稿发布成文章或动态
/取消 /清空 /cancel丢弃草稿,下次从头写
/动态切到动态模式(短内容,文件名用时间戳)
/文章切到文章模式(文件名用标题)
/tags设置文章标签,如 /tags: 折腾, 教程
/状态看看当前草稿攒了多少、什么模式
/预览返回将要发布的 markdown 原文,不发
/修改把草稿全文甩回给你,下次回复整体替换重算
/帮助打印上面这一张表

文字里以「键: 值」开头的行会自动识别成文章属性,中英文键名都认(标题: / tags: 都行),剩下的当正文。几个常用的点:

  • 文件名:动态固定 2026-08-14-020613.md;文章用 FILENAME_FORMAT 开关——title(默认)就是纯标题 标题.mddate 就是 2026-08-14-标题.md
  • 自定义链接:写 slug: 05,发布后返回 https://你的域名/posts/05;不设 slug 就退回文件名链接。
  • 动态专属:还能写 location: 广西pinned: true,直接进 frontmatter。
  • 正文插链接:标准 Markdown [文字](url) 就行,链接行不会被误吞。
  • 封面:明确写 封面:https://... 才用;不写就 image: api(随机封面),发的图只进正文不抢封面。

标记(@提及 / #话题标签)目前不可用,正文直接写文字即可。


三种部署模式#

用环境变量 DEPLOY_MODE 切换角色。现在三种模式随时换:

  1. hybrid(默认)

    EdgeOne 当入口解密回包,真正的写博客交给 Cloudflare Worker 的 /internal。国内延迟最低,两套东西各司其职。

  2. eo(纯 EdgeOne)

    EdgeOne 自己解密、自己 dispatch、自己写 GitHub,草稿存 EdgeOne 的 KV(绑定名 DRAFTS),Worker 彻底退休。入口本来就在 EO、解密回包也已跑通,只补「写 GitHub + KV 存草稿」两步,是改动最小的一个。

    Tip

    EdgeOne 的 KV 存储仅规则引擎形态边缘函数支持,普通 JS 边缘函数无法绑定 KV;切换为 eo 模式部署前先确认你的边缘函数类型。

    代码已存档在仓库 edge-functions/wxbot.js(与图床反代 index.js 分立),部署时作为企微入口边缘函数发布即可。

  3. cf(纯 Cloudflare)

    反过来,回调直接打 Cloudflare Worker,Worker 自己解密 + 回包 + 写 GitHub。需要把 EO 那份纯 JS AES 加密回包逻辑搬进 Worker(已经搬了,且跑了加解密往返测试,企微能正确解开)。

切换就是改环境变量,业务逻辑同源、指令集完全一致:

# EdgeOne 侧(index.js 的环境变量)
DEPLOY_MODE = "hybrid" # hybrid / eo
# Cloudflare 侧(wrangler.toml 的 [vars])
DEPLOY_MODE = "hybrid" # hybrid / cf

切到 cf 时,企业微信回调的入口域名得从 EdgeOne 换成 Cloudflare 的 Worker 域名——所以「换模式」不只是改个 env,还牵动域名指向,部署时要一起改。

纯 EO 那个 KV 绑定我在 EdgeOne 规则引擎形态里实测过能跑;万一你控制台那边 KV 绑定名不是 DRAFTS,回去补一下绑定就行,代码不用动。


支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
如何搭建企业微信机器人,用机器人发博文、动态
https://x1anyu.cn/posts/6/
作者
羡鱼
发布于
2026-08-15
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
羡鱼
临渊空慕水中鱼, 不如携风自渡河.
分类
标签
最新动态
站点统计
文章
14
分类
7
标签
35
总字数
19,969
运行时长
0
最后活动
0 天前
文章目录