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 的通知去触发。
相关推荐
前端Hardy1 小时前
前端日历组件,要变天了?Schedule-X v4.6 彻底杀疯了
前端·javascript·后端
Oo_行者_oO1 小时前
微服务 Feign 从“万能公共服务”到“业务客户端”
后端·架构
wei_shuo1 小时前
别再踩坑了!KingbaseES 存储过程与触发器开发避坑实录
后端
元宝骑士1 小时前
MySQL 实战:跨表排序 + 指定类型置顶四种写法
后端·mysql
微扬嘴角1 小时前
React快速入门
前端·react.js·前端框架
ZC跨境爬虫2 小时前
跟着 MDN 学CSS day_40:(Flexbox实战技能测试)
前端·css·ui·html·tensorflow
ZC跨境爬虫2 小时前
跟着 MDN 学CSS day_36:(float、clear与BFC深度解析)
前端·javascript·css·ui·交互
ConardLi2 小时前
啊?我刚开源的 Skills 已经 7K Star 了?!
前端·人工智能·后端
糯米团子7492 小时前
javascript高频知识点
开发语言·前端·javascript