企业微信/钉钉通知Hook

发送消息到企业微信或钉钉

一、企业微信/钉钉通知Hook的设计

在自动化工作流和CI/CD管线中,通知是不可或缺的一环。当构建完成、部署成功、出现错误或需要人工审核时,及时将状态推送到团队使用的即时通讯工具,可以大幅缩短响应时间、提升协作效率。

企业微信和钉钉是中国最主流的两款企业通讯工具,它们都提供了群机器人Webhook接口,允许外部系统通过HTTP POST请求向群聊推送消息。本案例将深入讲解如何在Claude Code和其他自动化场景中编写Hook脚本,将消息发送到企业微信或钉钉群。

适用场景: CI/CD构建通知、代码审查请求、错误告警、定时报告、部署状态通知、自定义事件监控

企业微信和钉钉的通知Hook本质上是一个Shell/Python/Node.js脚本,它通过curl或HTTP客户端向机器人Webhook地址发送POST请求,携带格式化后的消息内容(JSON格式),从而实现将事件信息推送到群聊中。

核心特点: 配置简单(仅需Webhook URL)、消息类型丰富(文本/Markdown/图文等)、支持@指定成员、安全可靠(HTTPS + 签名验证)

二、机器人Webhook配置

2.1 企业微信机器人配置

在企业微信群中添加机器人并获取Webhook地址:

步骤一:进入企业微信群 → 点击群设置 → 群机器人 → 添加机器人 步骤二:选择一个机器人或创建新机器人 → 输入机器人名称 步骤三:点击"添加" → 复制生成的Webhook URL 步骤四:保存URL,格式如下: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

2.2 钉钉机器人配置

在钉钉群中添加机器人并获取Webhook地址:

步骤一:进入钉钉群 → 群设置 → 智能群助手 → 添加机器人 步骤二:选择"自定义"机器人 → 设置机器人名称和头像 步骤三:配置安全设置(推荐使用"加签"模式) 步骤四:完成创建 → 复制Webhook地址和签名密钥 步骤五:保存URL和密钥,格式如下: Webhook URL: https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 签名密钥(Secret): SECxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
安全提示: 钉钉机器人的安全设置有三种方式:自定义关键词、加签、IP白名单。建议同时使用"加签"和"IP白名单"组合,确保最大安全性。企业微信也支持设置IP白名单。

2.3 Hook脚本基础:使用curl发送消息

无论是企业微信还是钉钉,发送消息的核心都是使用curl向Webhook URL发送POST请求。以下是基础用法:

#!/bin/bash # 企业微信:发送文本消息到群机器人 WEBHOOK_URL="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY" curl -s -X POST "$WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{ "msgtype": "text", "text": { "content": "Hello from Claude Code Hook!" } }'
#!/bin/bash # 钉钉:发送文本消息到群机器人(无加签模式) WEBHOOK_URL="https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN" curl -s -X POST "$WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{ "msgtype": "text", "text": { "content": "Hello from Claude Code Hook!" } }'

三、消息格式构建

3.1 企业微信消息类型

企业微信群机器人支持以下消息类型:

(1)文本消息(text)

{ "msgtype": "text", "text": { "content": "构建通知:前端项目构建完成\n项目:my-app\n分支:main\n状态:成功", "mentioned_list": ["@all"] } }

(2)Markdown消息

{ "msgtype": "markdown", "markdown": { "content": "# 构建通知\n## 项目:my-app\n> **状态**:✅ 成功\n> **分支**:main\n> **提交者**:张三\n> **时间**:2026-05-08 10:00:00\n---\n[查看构建详情](https://ci.example.com/build/123)" } }

(3)图文消息(news)

{ "msgtype": "news", "news": { "articles": [ { "title": "构建成功:my-app v2.3.1", "description": "前端项目构建完成,包含3个新功能和2个修复", "url": "https://ci.example.com/build/123", "picurl": "https://example.com/icon.png" } ] } }

3.2 钉钉消息类型

钉钉自定义机器人支持以下消息类型:

(1)文本消息(text)

{ "msgtype": "text", "text": { "content": "部署通知:后端服务部署完成\n服务:api-gateway\n环境:生产\n版本:v3.2.0" }, "at": { "atMobiles": ["13800138000"], "isAtAll": false } }

(2)Markdown消息

{ "msgtype": "markdown", "markdown": { "title": "代码审查请求", "text": "## 代码审查请求\n\n**PR标题**:修复用户登录超时问题\n**提交者**:李四\n**分支**:fix/login-timeout → main\n**描述**:优化了Session过期策略,增加自动刷新机制\n---\n[查看PR详情](https://git.example.com/pr/456)" }, "at": { "atMobiles": ["13900139000"], "isAtAll": true } }

(3)链接消息(link)

{ "msgtype": "link", "link": { "text": "生产环境CPU使用率持续超过90%,请及时关注和处理", "title": "【告警】生产环境CPU负载过高", "picUrl": "https://example.com/alert.png", "messageUrl": "https://monitor.example.com/alert/789" } }

(4)ActionCard消息

{ "msgtype": "actionCard", "actionCard": { "title": "构建失败 - 需要关注", "text": "## 构建失败\n\n**项目**:my-app\n**分支**:develop\n**阶段**:单元测试\n**失败原因**:测试用例 TestLogin 超时\n---\n请点击下方按钮查看详情", "btnOrientation": "1", "singleTitle": "查看详情", "singleURL": "https://ci.example.com/build/124" } }

3.3 @指定成员的通知方式

在企业微信和钉钉中,可以通过消息中的特定字段实现@指定成员:

# 企业微信:使用 mentioned_list 字段 "mentioned_list": ["wangqing", "@all"] # "@all" 表示@所有人 # "wangqing" 表示@指定成员(企业微信UserID) # 钉钉:使用 at 字段 "at": { "atMobiles": ["13800138000", "13900139000"], "isAtAll": false } # atMobiles 数组指定要@的手机号 # isAtAll 设置为 true 表示@所有人
提示: 在企业微信中,mentioned_list使用成员的UserID(非手机号或姓名),需要提前在企业微信后台获取。钉钉则使用手机号来@成员。使用时请注意添加对应权限。

3.4 Markdown内容格式化对照

企业微信和钉钉的Markdown语法略有不同,以下是常用格式对照:

格式企业微信钉钉
标题# H1 / ## H2# H1 / ## H2
加粗**文本****文本**
斜体*文本**文本*
链接[文本](URL)[文本](URL)
图片不支持![alt](URL)
有序列表1. 项目1. 项目
无序列表- 项目- 项目
引用> 引用内容> 引用内容
分割线------
代码块```code``````code```

四、安全设置

4.1 钉钉加签模式(密钥签名)

钉钉推荐使用"加签"安全方式。发送消息时,需要在请求中附加签名参数。签名算法如下:

签名算法: 1. 将 timestamp + "\n" + secret 拼接成字符串 2. 使用 HMAC-SHA256 算法计算签名 3. 将签名结果进行 Base64 编码 其中: - timestamp = 当前时间戳(毫秒) - secret = 机器人安全设置中的签名密钥

带有签名的Shell脚本实现:

#!/bin/bash # 钉钉加签模式消息发送脚本 WEBHOOK_URL="https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN" SECRET="SECxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # 生成时间戳(毫秒)和签名 timestamp=$(date +%s%3N) sign=$(echo -n "${timestamp}\n${SECRET}" | \ openssl dgst -sha256 -hmac "$SECRET" -binary | \ base64) # 完整的请求URL(含签名参数) FULL_URL="${WEBHOOK_URL}×tamp=${timestamp}&sign=${sign}" # 发送消息 curl -s -X POST "$FULL_URL" \ -H "Content-Type: application/json" \ -d '{ "msgtype": "markdown", "markdown": { "title": "部署通知", "text": "## 部署成功\\n\\n项目:api-gateway\\n环境:生产\\n状态:✅ 成功" } }'

4.2 企业微信安全配置

企业微信机器人支持IP白名单配置:

# 企业微信后台 → 机器人详情 → 安全设置 # 可配置允许调用Webhook的IP地址列表 # 仅白名单内的IP发起的请求才会被处理 示例: 允许的IP:192.168.1.0/24, 10.0.0.1 或使用:0.0.0.0/0(不限制IP,不推荐)

4.3 通用安全最佳实践

1. 密钥管理 - 不要将Webhook URL和Secret硬编码在代码中 - 使用环境变量或密钥管理服务(如Vault) - 示例:export WEBHOOK_KEY="xxxxx" 2. 敏感信息过滤 - 消息内容不要包含密码、密钥、Token等敏感字段 - 对日志中的敏感信息进行脱敏处理 - 示例:将 password=123456 → password=****** 3. 传输安全 - 始终使用HTTPS(企业微信和钉钉强制HTTPS) - 验证SSL证书有效性 - 设置合理的请求超时(建议5-10秒) 4. 访问控制 - 配置Webhook IP白名单 - 定期轮换访问密钥 - 监控异常调用频率
企业微信 vs 钉钉安全机制对比:
企业微信:IP白名单(可选)、HTTPS传输
钉钉:自定义关键词验证、加签(HMAC-SHA256签名)、IP白名单(可选)
建议:钉钉务必启用加签模式;企业微信务必配置IP白名单

五、多场景消息模板

5.1 构建成功/失败消息模板

Shell脚本(适用于CI/CD):

#!/bin/bash # CI/CD构建通知脚本 WEBHOOK_URL="$WEBHOOK_URL" PROJECT_NAME="${CI_PROJECT_NAME:-unknown}" BRANCH="${CI_COMMIT_BRANCH:-unknown}" BUILD_STATUS="${1:-unknown}" COMMITTER="${CI_COMMIT_AUTHOR:-unknown}" COMMIT_MSG="${CI_COMMIT_MESSAGE:-unknown}" BUILD_URL="${CI_JOB_URL:-unknown}" # 根据状态设置图标和颜色 if [ "$BUILD_STATUS" = "success" ]; then STATUS_ICON="✅" STATUS_TEXT="成功" COLOR="00C853" elif [ "$BUILD_STATUS" = "failed" ]; then STATUS_ICON="❌" STATUS_TEXT="失败" COLOR="FF0000" else STATUS_ICON="⚠️" STATUS_TEXT="其他" COLOR="FFA500" fi MESSAGE=$(cat <

5.2 代码审查请求消息模板

#!/bin/bash # PR/MR审查通知脚本 PR_TITLE="${1}" PR_AUTHOR="${2}" PR_URL="${3}" PR_BRANCH="${4}" PR_DESC="${5}" REVIEWERS="${6}" MESSAGE="## 👀 代码审查请求 **PR标题**:${PR_TITLE} **提交者**:${PR_AUTHOR} **分支**:${PR_BRANCH} **描述**: > ${PR_DESC} --- [查看PR详情](${PR_URL})" MESSAGE_ESCAPED=$(echo "$MESSAGE" | sed 's/$/\\n/g' | tr -d '\n') curl -s -X POST "$WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{ "msgtype": "markdown", "markdown": { "title": "代码审查请求", "text": "'"${MESSAGE_ESCAPED}"'" }, "at": { "atMobiles": ["'"${REVIEWERS}"'"] } }'

5.3 错误告警消息模板

#!/bin/bash # 错误告警通知脚本(适用于监控告警系统) ALERT_NAME="${1}" ALERT_SEVERITY="${2:-critical}" ALERT_SERVICE="${3}" ALERT_DETAIL="${4}" ALERT_TIME="${5:-$(date '+%Y-%m-%d %H:%M:%S')}" # 严重级别映射 case "$ALERT_SEVERITY" in critical) LEVEL_ICON="🔴"; LEVEL_TEXT="致命";; warning) LEVEL_ICON="🟡"; LEVEL_TEXT="警告";; info) LEVEL_ICON="🔵"; LEVEL_TEXT="提示";; *) LEVEL_ICON="⚪"; LEVEL_TEXT="未知";; esac MESSAGE="## ${LEVEL_ICON} ${LEVEL_TEXT}告警 **告警名称**:${ALERT_NAME} **服务名称**:${ALERT_SERVICE} **严重级别**:${LEVEL_TEXT} **发生时间**:${ALERT_TIME} **告警详情**: > ${ALERT_DETAIL} 请相关人员立即排查处理!" MESSAGE_ESCAPED=$(echo "$MESSAGE" | sed 's/$/\\n/g' | tr -d '\n') curl -s -X POST "$WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{ "msgtype": "markdown", "markdown": { "title": "【'"${LEVEL_TEXT}"'告警】'"${ALERT_NAME}"'", "text": "'"${MESSAGE_ESCAPED}"'" }, "at": { "isAtAll": true } }'
注意: 在告警消息中通常使用 @所有人(isAtAll: true)来确保相关人员第一时间获知,但请控制告警频率,避免对团队成员造成过度打扰。

5.4 定时报告消息模板

#!/bin/bash # 每日构建/部署状态报告 DATE=$(date '+%Y-%m-%d') SUCCESS_COUNT="${1:-10}" FAIL_COUNT="${2:-2}" TOTAL_COUNT=$((SUCCESS_COUNT + FAIL_COUNT)) SUCCESS_RATE=$(echo "scale=2; $SUCCESS_COUNT * 100 / $TOTAL_COUNT" | bc) MESSAGE="## 📊 每日构建报告 - ${DATE} ### 概览 | 指标 | 数值 | |------|------| | 总构建数 | ${TOTAL_COUNT} | | 成功 | ${SUCCESS_COUNT} | | 失败 | ${FAIL_COUNT} | | 成功率 | ${SUCCESS_RATE}% | ### 失败构建列表 ${4:-"(暂无失败构建)"} ### 备注 ${5:-"构建状态正常"}" MESSAGE_ESCAPED=$(echo "$MESSAGE" | sed 's/$/\\n/g' | tr -d '\n') curl -s -X POST "$WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{ "msgtype": "markdown", "markdown": { "title": "每日构建报告 '"${DATE}"'", "text": "'"${MESSAGE_ESCAPED}"'" } }'

5.5 多环境消息分发

在实际项目中,通常需要根据环境(开发/测试/生产)将消息发送到不同的群。可以通过配置多个Webhook URL实现:

#!/bin/bash # 多环境通知分发脚本 # 各环境Webhook配置 declare -A WEBHOOKS WEBHOOKS["dev"]="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=DEV_KEY" WEBHOOKS["test"]="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=TEST_KEY" WEBHOOKS["prod"]="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=PROD_KEY" ENV="${1:-dev}" MESSAGE_CONTENT="${2}" # 验证环境 if [ -z "${WEBHOOKS[$ENV]}" ]; then echo "错误:未知环境 '${ENV}'" echo "可用环境:${!WEBHOOKS[*]}" exit 1 fi WEBHOOK_URL="${WEBHOOKS[$ENV]}" # 发送通知 curl -s -X POST "$WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{ "msgtype": "markdown", "markdown": { "title": "【'"${ENV}"'环境】部署通知", "text": "## 部署通知\n\n**环境**:'"${ENV}"'\n**消息**:'"${MESSAGE_CONTENT}"'\n**时间**:'"$(date '+%Y-%m-%d %H:%M:%S')"'" } }' echo "通知已发送到 ${ENV} 环境群聊"
实践建议: 将Webhook URL存储在环境变量或密钥管理服务中,不要在代码仓库中明文保存。在生产环境中,建议使用CI/CD提供的Secret变量功能(如GitLab CI Variables、GitHub Secrets)来管理Webhook地址。

六、完整Hook脚本案例

下面是一个完整的通用通知Hook脚本,支持企业微信和钉钉双平台,包含完整的签名、重试和错误处理逻辑:

#!/bin/bash # ===================================================== # 通用通知Hook脚本 # 支持:企业微信 / 钉钉 # 功能:构建/部署/告警通知 # 使用:./notify.sh # platform: wechat | dingtalk # type: build | deploy | alert # message: 通知内容(JSON格式或纯文本) # ===================================================== set -euo pipefail PLATFORM="${1:-wechat}" NOTIFY_TYPE="${2:-build}" MESSAGE="${3:-无消息内容}" # 配置(建议从环境变量读取) WECHAT_WEBHOOK="${WECHAT_WEBHOOK_URL:-}" DINGTALK_WEBHOOK="${DINGTALK_WEBHOOK_URL:-}" DINGTALK_SECRET="${DINGTALK_SECRET:-}" MAX_RETRIES=3 TIMEOUT=10 # 日志函数 log() { echo "[$(date '+%H:%M:%S')] $*"; } # 钉钉签名函数 dingtalk_sign() { local timestamp=$(date +%s%3N) local sign=$(echo -n "${timestamp}\n${DINGTALK_SECRET}" | \ openssl dgst -sha256 -hmac "$DINGTALK_SECRET" -binary | base64) echo "${DINGTALK_WEBHOOK}×tamp=${timestamp}&sign=${sign}" } # 发送函数 send_notification() { local url="$1" local payload="$2" local retry=0 while [ $retry -lt $MAX_RETRIES ]; do response=$(curl -s -X POST "$url" \ -H "Content-Type: application/json" \ -d "$payload" \ --max-time $TIMEOUT 2>&1) || true if echo "$response" | grep -q '"errcode":0\|"errcode":200'; then log "通知发送成功" return 0 fi retry=$((retry + 1)) log "发送失败(尝试 ${retry}/${MAX_RETRIES}):${response}" [ $retry -lt $MAX_RETRIES ] && sleep 2 done log "发送失败,已重试 ${MAX_RETRIES} 次" return 1 } # 构建消息体 build_payload() { local title="" local content="" local at_all="false" case "$NOTIFY_TYPE" in build) title="构建通知" content="## 构建通知\n\n${MESSAGE}" ;; deploy) title="部署通知" content="## 部署通知\n\n${MESSAGE}" at_all="true" ;; alert) title="【告警通知】" content="## 🚨 告警通知\n\n${MESSAGE}" at_all="true" ;; esac if [ "$PLATFORM" = "wechat" ]; then cat <

核心要点总结:

1. 企业微信和钉钉群机器人都通过Webhook接收POST请求,使用JSON格式传递消息

2. 支持的消息类型:企业微信(text/markdown/news),钉钉(text/markdown/link/ActionCard)

3. @指定成员:企业微信使用UserID,钉钉使用手机号

4. 钉钉加签模式使用HMAC-SHA256算法对timestamp+secret进行签名

5. 安全最佳实践:使用环境变量管理密钥、配置IP白名单、敏感信息脱敏

6. 多环境场景下,为不同环境分配不同的Webhook URL,发送到不同的群聊

七、进一步思考

1. 如何将通知Hook集成到GitLab CI / GitHub Actions / Jenkins 等CI/CD平台中? - 在流水线配置中直接调用通知脚本,将构建参数作为环境变量传入 - 使用各CI/CD平台的Webhook功能,将事件直接转发到群机器人 2. 如何实现告警降噪和聚合? - 相同类型的告警在短时间内只发送一次(使用缓存或时间窗口) - 将多个告警聚合成一条摘要消息 - 设置告警静默期,避免重复告警 3. 如何扩展支持更多IM平台? - 抽象通知接口,支持企业微信、钉钉、飞书、Slack等 - 使用适配器模式,不同平台实现各自的格式化逻辑 - 维护统一的内部消息格式,在不同平台间转换 4. 如何监控通知发送的健康状态? - 记录每次发送的成功/失败日志 - 设置通知发送成功率的监控指标 - 当通知发送失败时,通过备用通道(如短信)通知管理员