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 的通知去触发。
相关推荐
BigTopOne5 小时前
【WebRtc】DTLS-SRTP 加密完整流程详解
前端
陈随易5 小时前
pm2 替代品,nodejs&bun 线上部署必备工具
前端·后端·程序员
单线程_015 小时前
从案例分析 Vue3 Tokenizer 源码一
前端·javascript·vue.js
BigTopOne5 小时前
【WebRtc】-ICE Candidate 与 STUN/TURN 原理详解
前端
l1258655 小时前
# LangGraph Memory机制深度解析:短期记忆与长期记忆的工程实践
前端·人工智能·python·langchain·bootstrap
丘山一郎6 小时前
Spring 中的IOC控制反转 和DI依赖注入
java·后端·spring
码视野6 小时前
基于 Spring Boot + Vue3 的【大学英语四六级 (CET-4/6) 作文智能评分与句式润色系统】设计与实现(含PRD/三端高保真源码/大屏)
java·前端·人工智能·spring boot·后端·vue3
MetaLite6 小时前
Spring-AOP自调用为什么失效-AopContext真能解决吗
java·后端·spring
大模型码小白6 小时前
AI 对话流性能调优:万级消息的虚拟滚动落地
java·大数据·前端·javascript·人工智能·算法·机器学习
MC皮蛋侠客7 小时前
Tauri 2.x 系列(五):调用系统 API——官方插件、Rust crate 与原生能力
开发语言·后端·rust