Skip to content

通过 RPC 推送告警到钉钉

1. 场景说明

ThinkLink(TKL)平台的 RPC 支持调用外部 HTTP 接口(见 RPC 模型 §1.3.2 类型四),可以把设备告警实时推送到钉钉自定义机器人。本文介绍如何使用钉钉加签机制安全地推送告警消息。

2. 准备工作

2.1 创建钉钉自定义机器人

  1. 在钉钉群中点击 群设置 → 智能群助手 → 添加机器人 → 自定义
  2. 安全设置至少勾选一项,强烈推荐选择 "加签" 模式(不要只用关键词或 IP 白名单)
  3. 复制生成的 Webhook 地址加签密钥 (Secret,以 SEC 开头)
Webhook: https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxx
Secret:  SECxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

2.2 妥善保管 access_token 与 Secret(必读)

⚠️ 安全提示access_token 是机器人的发送凭证,Secret 是签名密钥。两者任一泄漏都会带来安全风险 —— 攻击者可以伪造来自机器人的消息。

请遵守以下规则:

  • 不要access_tokenSecret 硬编码进脚本、配置文件、截图
  • 推荐做法:把两者保存在设备(或资产)的 server_attrs 中,脚本里通过 device.server_attrs.dingtalk_token.dingtalk_secret 引用
  • 始终启用加签机制:钉钉的"自定义关键词"和"IP 地址段"两种安全模式很容易被绕过,加签是唯一难以被伪造的方式
  • 建议至少 每季度更换一次 机器人凭证(重新生成机器人即可)
  • 严格控制群成员,并定期清理不再需要的机器人
  • 发现凭证泄漏时立即在群机器人设置中删除并重建

3. 在 RPC 中调用钉钉

3.1 在设备属性中保存凭证

运维管理 → 设备管理 → 设备详情 → 属性 标签下,为 server_attrs 添加:

json
{
    "dingtalk_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "dingtalk_secret": "SECxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

3.2 RPC 脚本:带加签的 Markdown 消息

javascript
function rpc_script({ device, params, alarms, logger }) {
    let token  = device.server_attrs?.dingtalk_token;
    let secret = device.server_attrs?.dingtalk_secret;
    if (!token || !secret) {
        logger.error("dingtalk credentials not configured", { eui: device.eui });
        return null;
    }

    // 计算钉钉加签:timestamp + "\n" + secret,用 HmacSHA256 摘要后做 base64 + URL encode
    let timestamp = Date.now();
    let stringToSign = `${timestamp}\n${secret}`;

    let crypto = require("crypto");
    let sign = encodeURIComponent(
        crypto.createHmac("sha256", secret).update(stringToSign).digest("base64")
    );

    let url = `https://oapi.dingtalk.com/robot/send?access_token=${token}` +
              `&timestamp=${timestamp}&sign=${sign}`;

    let title = `设备告警 - ${device.name}`;
    let text = [
        `### ${title}`,
        ``,
        `- **设备 EUI**:\`${device.eui}\``,
        `- **告警等级**:**${(params.level || "").toUpperCase()}**`,
        `- **告警内容**:${params.desc || "无"}`,
        `- **时间**:${new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" })}`
    ].join("\n");

    return [
        {
            sleepTimeMs: 0,
            type: "axios",
            dnMsg: {
                method: "POST",
                url: url,
                headers: { "Content-Type": "application/json" },
                data: {
                    msgtype: "markdown",
                    markdown: { title: title, text: text },
                    at: {
                        atMobiles: params.at_mobiles || [],
                        isAtAll: !!params.at_all
                    }
                },
                timeout: 5000
            }
        }
    ];
}

💡 钉钉加签要求 timestamp 与服务器时间偏差不超过 1 小时,签名需对当前时间戳重新计算 —— 不能预先固定。

3.3 RPC 脚本:发送纯文本消息

javascript
function rpc_script({ device, params, alarms, logger }) {
    let token  = device.server_attrs?.dingtalk_token;
    let secret = device.server_attrs?.dingtalk_secret;
    if (!token || !secret) return null;

    let timestamp = Date.now();
    let crypto = require("crypto");
    let sign = encodeURIComponent(
        crypto.createHmac("sha256", secret)
              .update(`${timestamp}\n${secret}`).digest("base64")
    );

    return [
        {
            sleepTimeMs: 0,
            type: "axios",
            dnMsg: {
                method: "POST",
                url: `https://oapi.dingtalk.com/robot/send?access_token=${token}&timestamp=${timestamp}&sign=${sign}`,
                headers: { "Content-Type": "application/json" },
                data: {
                    msgtype: "text",
                    text: { content: params.content || `${device.name} 告警通知` }
                },
                timeout: 5000
            }
        }
    ];
}

4. 配置 RPC 参数

字段标识类型别名说明
contentstring消息内容纯文本模式使用
levelstring告警等级low / mid / high / urgent
descstring告警描述Markdown 模式下的详情
at_mobilesobject@ 手机号列表数组,例如 ["13800000000"]
at_allboolean是否 @ 所有人慎用,避免打扰

5. 集成到告警流程

参考 企业微信 AN 中的触发联动示例,把 method 改成本 RPC 的 Method 名即可。

6. 故障排查

现象可能原因
钉钉返回 errcode: 310000 sign not matchtimestamp 或 secret 不匹配;签名编码方式错误(应为 base64 后 URL encode)
钉钉返回 errcode: 130101关键词模式下消息内容不包含设定的关键词
完全收不到access_token 错误;机器人被群管理员禁用;服务器到 oapi.dingtalk.com 网络不通
频率限制单个机器人每分钟 20 条消息,超过会被限流

7. 注意事项

  • 加签机制要求每条消息单独签名,不能复用旧签名
  • 消息内容不要包含完整客户隐私数据、内部 IP 地址、Token 等敏感信息
  • @ 手机号必须是钉钉群内成员的注册手机号,否则不会触发 @ 提醒
  • 重要告警建议同时配置多个通道(如钉钉 + 邮件),避免单一通道异常漏报