openclaw 钉钉 Webhook 完全指南

📮 钉钉 Webhook 完全指南

整理者:✨ 小琳 | 更新于 2026-02-05

一、基础知识

Webhook vs 插件

方式优点缺点
OpenClaw 插件集成简单,双向通信只能回复,不能主动发
Webhook 机器人支持主动推送,格式丰富单向,需要自己处理签名

结论:需要主动推送消息时,用 Webhook。

消息格式支持

格式插件Webhook
纯文本
Markdown
链接卡片
按钮卡片
@ 用户

二、@ 用户功能

核心原理

两个地方必须同时设置:

  1. 消息内容中包含 @手机号@所有人
  2. JSON 的 at 字段中指定 atMobilesisAtAll

缺一不可!

JSON 示例

@ 所有人:

{
  "msgtype": "text",
  "text": {
    "content": "【紧急通知】@所有人 请立即查看"
  },
  "at": {
    "isAtAll": true
  }
}

@ 指定用户:

{
  "msgtype": "text",
  "text": {
    "content": "【任务分配】@13800138000 请跟进项目进度"
  },
  "at": {
    "atMobiles": ["13800138000"],
    "isAtAll": false
  }
}

@ 多个用户:

{
  "msgtype": "text",
  "text": {
    "content": "@13800138000 @13900139000 请查看"
  },
  "at": {
    "atMobiles": ["13800138000", "13900139000"],
    "isAtAll": false
  }
}


三、完整 Shell 脚本

支持 @ 用户的钉钉推送脚本:

#!/bin/bash
# dingtalk-notify.sh - 支持 @ 用户的钉钉推送

# 用法:
#   ./dingtalk-notify.sh "消息内容"              # 普通发送
#   ./dingtalk-notify.sh "消息内容" all          # @所有人
#   ./dingtalk-notify.sh "消息内容" lin          # @指定用户
#   ./dingtalk-notify.sh "消息内容" lin maple    # @多人

MESSAGE="$1"
shift

if [ -z "$MESSAGE" ]; then
  echo "用法: $0 \"消息内容\" [all|用户名...]"
  exit 1
fi

# ===== 配置区域 =====
WEBHOOK_BASE="你的Webhook地址"
SECRET="你的加签密钥"

# 用户手机号映射
declare -A USERS
USERS["lin"]="16670151072"
USERS["琳琳"]="16670151072"
USERS["maple"]="19976618156"
USERS["鸿枫"]="19976618156"
# ===== 配置结束 =====

# 生成时间戳和签名
timestamp=$(date +%s%3N)
string_to_sign="${timestamp}\n${SECRET}"
sign=$(echo -ne "${string_to_sign}" | openssl dgst -sha256 -hmac "${SECRET}" -binary | base64 | sed 's/+/%2B/g; s/\//%2F/g; s/=/%3D/g')

# 构造 @ 参数
IS_AT_ALL="false"
AT_MOBILES=""
AT_TEXT=""

for target in "$@"; do
  if [ "$target" = "all" ] || [ "$target" = "所有人" ]; then
    IS_AT_ALL="true"
    AT_TEXT="@所有人 "
  elif [ -n "${USERS[$target]}" ]; then
    phone="${USERS[$target]}"
    if [ -z "$AT_MOBILES" ]; then
      AT_MOBILES="\"$phone\""
    else
      AT_MOBILES="$AT_MOBILES, \"$phone\""
    fi
    AT_TEXT="${AT_TEXT}@${phone} "
  fi
done

# 构造完整消息
FULL_MESSAGE="${AT_TEXT}${MESSAGE}"

# 构造 JSON
if [ -n "$AT_MOBILES" ]; then
  JSON_BODY="{\"msgtype\":\"text\",\"text\":{\"content\":\"$FULL_MESSAGE\"},\"at\":{\"atMobiles\":[$AT_MOBILES],\"isAtAll\":$IS_AT_ALL}}"
else
  JSON_BODY="{\"msgtype\":\"text\",\"text\":{\"content\":\"$FULL_MESSAGE\"},\"at\":{\"isAtAll\":$IS_AT_ALL}}"
fi

# 发送请求
curl -s "${WEBHOOK_BASE}&timestamp=${timestamp}&sign=${sign}" \
  -H "Content-Type: application/json" \
  -d "$JSON_BODY"


四、Node.js 实现

const crypto = require('crypto');
const axios = require('axios');

// 配置
const WEBHOOK_BASE = '你的Webhook地址';
const SECRET = '你的加签密钥';

// 用户手机号映射
const USER_PHONES = {
  'lin': '16670151072',
  '琳琳': '16670151072',
  'maple': '19976618156',
  '鸿枫': '19976618156'
};

/**
 * 生成钉钉签名
 */
function generateSign(secret, timestamp) {
  const stringToSign = `${timestamp}\n${secret}`;
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(stringToSign);
  return encodeURIComponent(hmac.digest('base64'));
}

/**
 * 解析 @ 目标
 * @param {string|string[]} targets - 'all' | 'lin' | ['lin', 'maple']
 */
function parseAtTargets(targets) {
  const result = { atMobiles: [], isAtAll: false, atText: '' };
  if (!targets) return result;

  const list = Array.isArray(targets) ? targets : [targets];
  for (const t of list) {
    if (t === 'all') {
      result.isAtAll = true;
      result.atText = '@所有人 ';
    } else if (USER_PHONES[t]) {
      result.atMobiles.push(USER_PHONES[t]);
      result.atText += `@${USER_PHONES[t]} `;
    }
  }
  return result;
}

/**
 * 发送消息
 * @param {string} content - 消息内容
 * @param {string|string[]} atTargets - @ 目标
 */
async function sendText(content, atTargets = null) {
  const timestamp = Date.now();
  const sign = generateSign(SECRET, timestamp);
  const url = `${WEBHOOK_BASE}&timestamp=${timestamp}&sign=${sign}`;

  const { atMobiles, isAtAll, atText } = parseAtTargets(atTargets);

  const body = {
    msgtype: 'text',
    text: { content: `${atText}${content}` },
    at: { atMobiles, isAtAll }
  };

  const res = await axios.post(url, body);
  return res.data;
}

// 使用示例
sendText('测试消息');                    // 普通发送
sendText('紧急通知', 'all');             // @所有人
sendText('请查看', 'maple');             // @指定用户
sendText('请查看', ['lin', 'maple']);    // @多人


五、Python 实现

import requests
import json
import time
import hmac
import hashlib
import base64
import urllib.parse

# 配置
WEBHOOK_BASE = "你的Webhook地址"
SECRET = "你的加签密钥"

# 用户手机号映射
USER_PHONES = {
    "lin": "16670151072",
    "琳琳": "16670151072",
    "maple": "19976618156",
    "鸿枫": "19976618156"
}

def generate_sign(secret, timestamp):
    """生成钉钉签名"""
    string_to_sign = f"{timestamp}\n{secret}"
    hmac_code = hmac.new(
        secret.encode('utf-8'),
        string_to_sign.encode('utf-8'),
        digestmod=hashlib.sha256
    ).digest()
    sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
    return sign

def send_text(content, at_targets=None):
    """
    发送消息
    at_targets: 'all' | 'lin' | ['lin', 'maple']
    """
    timestamp = str(int(time.time() * 1000))
    sign = generate_sign(SECRET, timestamp)
    url = f"{WEBHOOK_BASE}&timestamp={timestamp}&sign={sign}"

    # 解析 @ 目标
    at_mobiles = []
    is_at_all = False
    at_text = ""

    if at_targets:
        targets = at_targets if isinstance(at_targets, list) else [at_targets]
        for t in targets:
            if t == "all":
                is_at_all = True
                at_text = "@所有人 "
            elif t in USER_PHONES:
                at_mobiles.append(USER_PHONES[t])
                at_text += f"@{USER_PHONES[t]} "

    data = {
        "msgtype": "text",
        "text": {"content": f"{at_text}{content}"},
        "at": {"atMobiles": at_mobiles, "isAtAll": is_at_all}
    }

    response = requests.post(url, json=data)
    return response.json()

# 使用示例
send_text("测试消息")                     # 普通发送
send_text("紧急通知", "all")              # @所有人
send_text("请查看", "maple")              # @指定用户
send_text("请查看", ["lin", "maple"])     # @多人


六、避坑指南

1. 自定义关键词

钉钉要求 Webhook 机器人必须设置「自定义关键词」或「加签」。

  • 如果用关键词:确保消息内容包含设定的关键词
  • 推荐用加签:更灵活,不限制消息内容

2. 手机号必须准确

  • atMobiles 里的手机号必须是用户在钉钉绑定的手机号
  • 用户必须在群内,否则 @ 不生效

3. @ 的两个条件缺一不可

❌ 只在 content 里写 @手机号 → 不生效
❌ 只在 at.atMobiles 里填手机号 → 不生效
✅ 两个地方都写 → 生效

4. 避免滥用 @所有人

isAtAll 会打扰所有群成员,仅在紧急情况使用。

5. 发送频率限制

钉钉限制:每分钟最多 20 条消息。建议加发送间隔(1秒)。


七、Markdown 格式 @ 用户

{
  "msgtype": "markdown",
  "markdown": {
    "title": "任务提醒",
    "text": "### 任务提醒\n\n@13800138000 请在下班前完成以下任务:\n\n- [ ] 代码审查\n- [ ] 更新文档"
  },
  "at": {
    "atMobiles": ["13800138000"]
  }
}


掌握这些,钉钉机器人就能玩出花了! 🚀

本文由 mdnice 多平台发布

Read more

前端环境配置(nvm、nodejs、npm)

前端环境配置(nvm、nodejs、npm)

一、安装nvm 1. 下载vnm url: https://nvm.uihtm.com/doc/download-nvm.html 2. 解压文件后双击exe文件进行安装 3. 选择nvm的安装地址,我是安装在D:\App\nvm 4. 选择nodejs的安装地址,我是安装在C:\Program Files\nodejs 5. 点击next 一直点击 完成安装; 6. 找到nvm的settings.txt文件打开后: 给该文件添加这两行命令: node_mirror: https://npmmirror.com/mirrors/node/ npm_mirror: https://npmmirror.com/mirrors/npm/ 二、环境变量配置 1.

【DeepSeek R1部署至RK3588】RKLLM转换→板端部署→局域网web浏览

【DeepSeek R1部署至RK3588】RKLLM转换→板端部署→局域网web浏览

本文为DeepSeek R1 7B 以qwen为底座的LLM在瑞芯微RK3588 SoC上的完整部署流程,记录从开发板驱动适配烧录开始,到最终的开发板终端访问模型和局域网web访问模型的完整流程,有不足之处希望大家共同讨论。 文章目录 * 一、项目背景介绍 * 二、所需工具介绍 * 1.硬件工具 * 1.X86 PC虚拟机Ubuntu20.04 * 2. 准备NPU驱动为0.9.8的RK3588开发板 * 2.软件工具 * 三、获取.safetensors模型权重 * 四、safetensors转RKLLM * 1.转换环境搭建 * 2.模型转换 * 五、RKLLM模型板端部署及推理 * 六、集成开源gradio工具实现web访问 一、项目背景介绍 先来介绍下项目背景吧,目前有一个空闲的firefly出厂的搭载瑞芯微RK3588 SoC的arm64开发板,样式如图所示: 博主之前主要进行CV领域的模型的RK开发板部署,对于LLM和VLM的接触并不算多,但现在大模型是趋势所向,并且瑞芯微及时的完成了针对各开源

想做多语言项目?试试Hunyuan-MT-7B-WEBUI快速部署方案

想做多语言项目?试试Hunyuan-MT-7B-WEBUI快速部署方案 你有没有遇到过这样的情况:手头有个跨境项目,要同时处理日语产品说明、西班牙语用户反馈、维吾尔语政策文件,甚至还有藏文古籍数字化需求——可翻来翻去,不是翻译质量差强人意,就是部署起来像在解一道高数题?在线工具不敢传敏感数据,本地跑模型又卡在CUDA版本、依赖冲突、显存爆炸上……最后只能靠人工硬啃,进度一拖再拖。 Hunyuan-MT-7B-WEBUI 就是为这种真实困境而生的。它不讲大道理,不堆参数,不做“实验室里的冠军”,而是把腾讯混元团队打磨出的最强开源翻译模型,连同网页界面、一键脚本、预装环境,全打包进一个镜像里。你不需要懂Transformer结构,不用查PyTorch兼容表,甚至不用打开终端敲命令——点一下,等两分钟,就能在浏览器里开始翻译38种语言。 这不是又一个“需要调参、需要写代码、需要配环境”的AI工具。这是你今天下午就能用上的多语言工作台。 1. 为什么这款翻译镜像值得你立刻试试? 1.1 它真能覆盖你没想过的语言 很多翻译模型标榜“支持多语言”,但实际打开列表一看:英、法、

前端实现Word文档在线编辑与导出:基于mammoth.js与Blob对象的完整解决方案

如何在浏览器中直接编辑Word文档并导出?本文将深入探索一种基于mammoth.js和Blob对象的完整技术方案。 在当今的Web应用开发中,实现文档的在线编辑与导出已成为常见需求。无论是企业内部系统、教育平台还是项目管理工具,都迫切需要让用户能够在浏览器中直接编辑Word文档,而无需安装桌面软件。本文将详细介绍如何利用mammoth.js和Blob对象实现这一功能,并对比其他可行方案。 一、为什么选择mammoth.js与Blob方案? 在Web前端实现Word文档处理,主要有三种主流方案:浏览器原生Blob导出、mammoth.js专业转换和基于模板的docxtemplater方案。它们各有优劣,适用于不同场景。 mammoth.js的核心优势在于它能将.docx文档转换为语义化的HTML,而非简单复制视觉样式。这意味着它生成的HTML结构清晰、易于维护和样式定制。配合Blob对象,我们可以轻松将编辑后的内容重新导出为Word文档。 与直接使用Microsoft Office Online或Google Docs嵌入相比,mammoth.js方案不依赖外部服务,能更好地