Cloudflare 通知转发钉钉机器人

今天想着把 Cloudflare 账单通知打开,然后把通知推到钉钉群里方便查看。

结果发现邮件通知正常收到了,钉钉那边却一点反应都没有。

排查之后发现是 Cloudflare 发出的 Webhook payload 和钉钉机器人要求的格式对不上。

于是搓了个 Cloudflare Worker 做中间层转换,把 Cloudflare 的 payload 转换成钉钉能识别的格式。

1. 前置条件

  • 一个 Cloudflare 账号
  • 一个钉钉群,已创建自定义机器人,安全设置选择加签模式

问题分析:为什么直接填 Webhook 没反应

Cloudflare 发送的 payload 格式:官方文档

json 复制代码
{
  "name": "string",
  "text": "string",
  "data": {},
  "ts": 1136214245
}

钉钉机器人文本格式:官方文档

css 复制代码
{
  "msgtype": "text",
  "text": {
    "content": "xxxx"
  }
}

2. 创建 Worker

在 Cloudflare 控制台进入 Workers & Pages ,选择 Create Worker

  1. 选择 "Hello World" Worker
  2. 给 Worker 起个名字,比如 dingtalk-notify
  3. 点击 部署
  4. 部署完成后点击编辑代码

把默认代码全部删掉,替换为下面这段:

javascript 复制代码
// 生成钉钉加签
async function generateSign(timestamp, secret) {
  const encoder = new TextEncoder();
  const keyData = encoder.encode(secret);
  const message = encoder.encode(`${timestamp}\n${secret}`);

  const cryptoKey = await crypto.subtle.importKey(
    "raw",
    keyData,
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign"]
  );

  const signature = await crypto.subtle.sign("HMAC", cryptoKey, message);
  const sign = btoa(String.fromCharCode(...new Uint8Array(signature)));
  return encodeURIComponent(sign);
}

export default {
  async fetch(request, env, ctx) {
    if (request.method !== "POST") {
      return new Response(
        "This Worker only accepts POST requests from Cloudflare notifications.",
        { status: 405, headers: { "Content-Type": "text/plain" } }
      );
    }

    try {
      const text = await request.text();
      if (!text) {
        return new Response(JSON.stringify({ error: "Empty request body" }), {
          status: 400,
          headers: { "Content-Type": "application/json" },
        });
      }

      const cfPayload = JSON.parse(text);
      const title = cfPayload.name || "Cloudflare 通知";
      const content = cfPayload.text || "";

      const dingtalkBody = {
        msgtype: "text",
        text: { content: `${title}\n${content}` },
      };

      const timestamp = Date.now();
      const sign = await generateSign(timestamp, env.DINGTALK_SECRET);

      const webhookUrl = new URL(env.DINGTALK_WEBHOOK_URL);
      webhookUrl.searchParams.set("timestamp", timestamp);
      webhookUrl.searchParams.set("sign", sign);

      const res = await fetch(webhookUrl.toString(), {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(dingtalkBody),
      });

      const result = await res.text();
      return new Response(result, { status: res.status });
    } catch (err) {
      return new Response(JSON.stringify({ error: err.message }), {
        status: 500,
        headers: { "Content-Type": "application/json" },
      });
    }
  },
};

改完后点击 部署

3. 设置环境变量

在 Worker 页面,点击顶部 设置 标签,然后点击左侧 变量和机密

添加两个变量:

变量名 类型
DINGTALK_WEBHOOK_URL 你的钉钉 Webhook 地址,如 https://oapi.dingtalk.com/robot/send?access_token=xxx 密钥
DINGTALK_SECRET 钉钉机器人后台加签 SECxxxxxx 密钥

4. 修改 Cloudflare 通知目标

  1. 回到 Cloudflare 控制台的通知 Webhook 页面 目的地 -----> 创建
  2. 把 Webhook URL 改成你的 Worker 地址(格式类似 https://dingtalk-notify.xxx.workers.dev
  3. Secret 留空
  4. 点击 保存并测试

此时钉钉群里会收到一条测试通知。

5. 效果

配置完成后,Cloudflare 的通知就会通过 Worker 转发到钉钉群了

常见问题

  • 钉钉机器人后台的安全设置必须是加签模式 (不是关键词模式),SECxxxxxx 才能生效。如果是关键词模式,要么改成加签,要么把 Worker 代码改成不带加签的版本。
  • 浏览器访问 Worker 地址返回 405? 这是正常的,Worker 只接受 POST 请求,需要用 Cloudflare 的通知去触发。
相关推荐
এ慕ོ冬℘゜几秒前
深度解析 JavaScript:赋予 Web 灵魂的“全栈语言”
开发语言·前端·javascript
iCOD3R2 分钟前
Skill - 3秒生成精美GitHub主页
前端·程序员·ai编程
爱勇宝9 分钟前
《道德经》第5章:生产环境不相信眼泪
前端·后端·架构
掘金者阿豪13 分钟前
LEFT JOIN 凭空消失的背后,是优化器偷偷帮你做了决定
前端·后端
HackTwoHub21 分钟前
AntiDebug Mcp、AI逆向绕过前端,Hook 加密接口,抓取 Vue 路由漏洞,接入 MCP 让 AI 自动化挖掘前端漏洞
前端·vue.js·人工智能·安全·web安全·网络安全·系统安全
Mr_凌宇23 分钟前
AI 应用工程进阶扩展学习笔记 二
前端·面试
阿维的博客日记24 分钟前
spring里面的@RequestParam什么意思
java·后端·spring
金金金__31 分钟前
腾讯云COS接入配置
后端
Hyyy36 分钟前
Git Worktree 完全讲解
前端·后端·面试
葡萄城技术团队1 小时前
AI 不会淘汰电子表格,反而会让电子表格成为 AI 操作系统
前端·人工智能