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 的通知去触发。
相关推荐
葡萄城技术团队3 小时前
1000 人协同压测的环境瓶颈排查:CPU 争用与 Wi\-Fi
前端
学长毕设4 小时前
基于springboot的食品仓库管理系统
java·spring boot·后端
excel4 小时前
Marked 与 Markdown 有什么区别?该怎么选?
前端
滴滴答答哒5 小时前
ASP.NET Core + FaceRecognitionDotNet 人脸识别实战:DI 单例、特征向量持久化与 1:N 比对
后端·asp.net
Da Da 泓5 小时前
五子棋 Web 端测试用例报告
前端·网络·websocket·网络协议·测试用例·项目
zxcvb1536 小时前
远程查看本地文档:一条轻量 Web 文件服务的工程化链路
前端·远程查看文档
niucloud-admin6 小时前
NIUCLOUD PHP SAAS上门家政使用文档:城市管理
java·前端·php
北岛贰6 小时前
啵啵音乐迎来全新升级!
前端
captain3767 小时前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis
hkNaruto8 小时前
【AI】基于大模型的AI游戏地图生成、控制方案:AI Native Game Harness 迁移操作手册
前端·人工智能·游戏