跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
编程语言Node.js

钉钉 Webhook 机器人配置与 @ 用户实现指南

介绍钉钉 Webhook 机器人的使用方法,对比了 Webhook 与插件的区别。重点讲解了@用户的实现原理(需同时设置消息内容与 JSON 字段),提供了 Shell、Node.js 和 Python 三种语言的完整推送脚本示例。此外还涵盖了自定义关键词、加签、手机号准确性及发送频率限制等避坑指南,帮助开发者安全高效地集成钉钉通知功能。

鲜活发布于 2026/4/6更新于 2026/7/2551 浏览

钉钉 Webhook 机器人配置与 @ 用户实现指南

一、基础知识

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

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

消息格式支持
格式插件Webhook
纯文本✅✅
Markdown✅✅
链接卡片❌✅
按钮卡片❌✅
@ 用户❌✅

二、@ 用户功能

核心原理

两个地方必须同时设置:

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

缺一不可!

JSON 示例

@ 所有人:

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

}
}

@ 指定用户:

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

@ 多个用户:

{
  "msgtype": "text",
  "text": {
    "content": "@13800000000 @13900000000 请查看"
  },
  "at": {
    "atMobiles": ["13800000000", "13900000000"],
    "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"]="13800000000"
USERS["琳琳"]="13800000000"
USERS["maple"]="13900000000"
USERS["鸿枫"]="13900000000"
# ===== 配置结束 =====

# 生成时间戳和签名
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': '13800000000',
  '琳琳': '13800000000',
  'maple': '13900000000',
  '鸿枫': '13900000000'
};

/**
 * 生成钉钉签名
 */
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": "13800000000",
    "琳琳": "13800000000",
    "maple": "13900000000",
    "鸿枫": "13900000000"
}

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@13800000000 请在下班前完成以下任务:\n\n- [ ] 代码审查\n- [ ] 更新文档"
  },
  "at": {
    "atMobiles": ["13800000000"]
  }
}

目录

  1. 钉钉 Webhook 机器人配置与 @ 用户实现指南
  2. 一、基础知识
  3. Webhook vs 插件
  4. 消息格式支持
  5. 二、@ 用户功能
  6. 核心原理
  7. JSON 示例
  8. 三、完整 Shell 脚本
  9. dingtalk-notify.sh - 支持 @ 用户的钉钉推送
  10. 用法:
  11. ./dingtalk-notify.sh "消息内容" # 普通发送
  12. ./dingtalk-notify.sh "消息内容" all # @所有人
  13. ./dingtalk-notify.sh "消息内容" lin # @指定用户
  14. ./dingtalk-notify.sh "消息内容" lin maple # @多人
  15. ===== 配置区域 =====
  16. 用户手机号映射
  17. ===== 配置结束 =====
  18. 生成时间戳和签名
  19. 构造 @ 参数
  20. 构造完整消息
  21. 构造 JSON
  22. 发送请求
  23. 四、Node.js 实现
  24. 五、Python 实现
  25. 配置
  26. 用户手机号映射
  27. 使用示例
  28. 六、避坑指南
  29. 1. 自定义关键词
  30. 2. 手机号必须准确
  31. 3. @ 的两个条件缺一不可
  32. 4. 避免滥用 @所有人
  33. 5. 发送频率限制
  34. 七、Markdown 格式 @ 用户
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Happy Coder:Claude Code 的移动端与 Web 客户端
  • Python SQLAlchemy ORM 数据库操作指南
  • Python 基于 Transformer 的时序数据建模与实现详解
  • Open3D与C++的3D点云处理环境配置与编译
  • OpenClaw 安装部署与渠道接入指南
  • Agent Native 取代 Copilot,定义下一代 AI 系统
  • Visual Studio 2026 新特性详解:AI 驱动开发体验升级
  • Embedding 模型的选择和微调
  • Python 2026 发展局势:AI 时代的通用基础设施语言
  • Visual Studio 常用快捷键与效率技巧
  • 工控上位机开发为何首选 C#?核心优势与实战模板
  • mdev 与 udev:嵌入式及桌面 Linux 设备管理对比
  • 2024 年大模型驱动的数字员工 3.0 建设应用白皮书核心观点解读
  • LLaMA 2/3、Qwen 与 DeepSeek 开源大模型技术对比分析
  • ROS2 无人机自主智能技术解析与落地指南
  • Superpowers 与 gstack:AI 编程 Agent 技能与角色架构对比
  • MyBatisPlus 与 Thymeleaf 全栈分页整合实战
  • DeepSeek 时代:前端开发的变革与实践
  • MyBatisPlus 与 Thymeleaf 全栈分页方案实现
  • 垄断时代,开源让程序员过的更好还是更坏?

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online