​ 本文档对自定义钉钉机器人的流程与注意事项做一个简单介绍,没有知识点,作为一个日常笔记,储备一些小工具。

一、获取自定义机器人webhook

步骤一、打开机器人管理页面

  • 以PC端为例,打开PC端钉钉,点击头像,选择“机器人管理”。 钉钉预警机器人配置

步骤二、在机器人管理页面选择“自定义”机器人

  • 输入机器人名字并选择要发送消息的群,同时可以为机器人设置机器人头像。 钉钉预警机器人配置2

步骤三、完成必要的安全设置

  • 安全设置至少选择一种,勾选我已阅读并同意《自定义机器人服务及免责条款》,点击“完成”。安全设置目前有3种方式,设置说明见下文介绍。 *这种安全设置是后来添加的 之前是没有安全验证

钉钉预警机器人配置3

步骤四、复制出机器人的Webhook地址

  • 可用于向这个群发送消息,格式如下:
1
https://oapi.dingtalk.com/robot/send?access_token=XXXXXX

注意:请保管好此Webhook 地址,不要公布在外部网站上,泄露后有安全风险。

二、安全设置

安全设置目前有3种方式:

方式一、自定义关键词

最多可以设置10个关键词,消息中至少包含其中1个关键词才可以发送成功。

例如:添加了一个自定义关键词:监控报警

则这个机器人所发送的消息,必须包含 监控报警 这个词,才能发送成功。 钉钉预警机器人配置4

  • 代码实现 由于在自定义关键词上关键词是业务报警 所以发送的消息里一定要有这几个字才能发送成功
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from urllib import parse, request
import json
import time
import hmac
import hashlib
import base64
import urllib.parse

def ding_api(contents):
    url = "https://oapi.dingtalk.com/robot/send?access_token=67f7dbda6efff871c39005b0ee812d89d64887180b40d0dbdcb87daca2022817"

    data={
     "msgtype": "markdown",
     "markdown": {
         "title":"北京天气",
         "text": "#### 杭州天气 ####@17600XXXXX \n> 9度,西北风1级,空气良89,相对温度73%\n> ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n> ###### 10点20分发布 [天气](https://www.dingtalk.com) \n"
     },
      "at": {
          "atMobiles": [
              "17600XXXXX"
          ],
          "isAtAll": False
      }
 }

    postdata = json.dumps(data).encode('utf-8')

    req = request.Request(url=url, data=postdata)
    req.add_header('Content-Type', 'application/json')
    r = request.urlopen(req)
    r_data = r.read().decode('utf-8')
    return r_data


if __name__ == "__main__":
    a = ding_api("业务报警")
    print(a)

方式二、加签

  • 第一步,把timestamp+”\n"+密钥当做签名字符串,使用HmacSHA256算法计算签名,然后进行Base64 encode,最后再把签名参数再进行urlEncode,得到最终的签名(需要使用UTF-8字符集)。
参数 说明
timestamp 当前时间戳,单位是毫秒,与请求调用时间误差不能超过1小时
secret 密钥,机器人安全设置页面,加签一栏下面显示的SEC开头的字符串

签名计算代码示例(Python 3.8)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#python 3.8 
import time
import hmac
import hashlib
import base64
import urllib.parse

timestamp = str(round(time.time() * 1000))
secret = 'this is secret'
secret_enc = secret.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp, secret)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
print(timestamp)
print(sign)


  • 注意secret 钉钉预警机器人配置5
  • 第二步、把 timestamp和第一步得到的签名值拼接到URL中。
参数 说明
timestamp 第一步使用到的时间戳
sign 第一步得到的签名值
1
2
https://oapi.dingtalk.com/robot/send?access_token=XXXXXX&timestamp=XXX&sign=XXX

  • 代码实现 以markdown的形式发送消息
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from urllib import parse, request
import json
import time
import hmac
import hashlib
import base64
import urllib.parse

def get_sss():
    timestamp = str(round(time.time() * 1000))
    secret = 'SEC051d514ef45a08a377d606ef5c470a801df0ae1a9d7a2d248f2bfb166ce3de633b'
    secret_enc = secret.encode('utf-8')
    string_to_sign = '{}\n{}'.format(timestamp, secret)
    string_to_sign_enc = string_to_sign.encode('utf-8')
    hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
    sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
    print(timestamp)
    print(sign)
    return timestamp,sign
def ding_api(contents):
    timestamp,sign = get_sss()
    url = "https://oapi.dingtalk.com/robot/send?access_token=67f7dbda6efff871c39005b0ee812d89d64887180b40d0dbdcb87daca2022817&timestamp="+timestamp+"&sign="+sign

    data={
     "msgtype": "markdown",
     "markdown": {
         "title":"北京天气",
         "text": "#### 杭州天气 ####@17600XXXXX \n> 9度,西北风1级,空气良89,相对温度73%\n> ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n> ###### 10点20分发布 [天气](https://www.dingtalk.com) \n"
     },
      "at": {
          "atMobiles": [
              "17600XXXXX"
          ],
          "isAtAll": False
      }
 }

    postdata = json.dumps(data).encode('utf-8')

    req = request.Request(url=url, data=postdata)
    req.add_header('Content-Type', 'application/json')
    r = request.urlopen(req)
    r_data = r.read().decode('utf-8')
    return r_data


if __name__ == "__main__":
    a = ding_api("北京天气")
    print(a)

方式三、IP地址(段)

设定后,只有来自IP地址范围内的请求才会被正常处理。支持两种设置方式:IP、IP段,暂不支持IPv6地址白名单,格式如下:

钉钉预警机器人配置6 代码实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from urllib import parse, request
import json
import time
import hmac
import hashlib
import base64
import urllib.parse

def ding_api(contents):
    url = "https://oapi.dingtalk.com/robot/send?access_token=67f7dbda6efff871c39005b0ee812d89d64887180b40d0dbdcb87daca2022817"

    data={
     "msgtype": "markdown",
     "markdown": {
         "title":"北京天气",
         "text": "#### 北京天气 ####@17600XXXXX \n> 9度,西北风1级,空气良89,相对温度73%\n> ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n> ###### 10点20分发布 [天气](https://www.dingtalk.com) \n"
     },
      "at": {
          "atMobiles": [
              "17600XXXXX"
          ],
          "isAtAll": False
      }
 }

    postdata = json.dumps(data).encode('utf-8')

    req = request.Request(url=url, data=postdata)
    req.add_header('Content-Type', 'application/json')
    r = request.urlopen(req)
    r_data = r.read().decode('utf-8')
    return r_data


if __name__ == "__main__":
    a = ding_api("业务报警")
    print(a)

  • 注意: 1.个人感觉使用第二种与第三种方式可以实现配置化钉钉预警的实现,安全性也更好; 2.安全设置的上述三种方式,需要至少设置其中一种,以进行安全保护。校验不通过的消息将会发送失败,错误如下:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 消息内容中不包含任何关键词
{
  "errcode":310000,
  "errmsg":"keywords not in content"
}

// timestamp 无效
{
  "errcode":310000,
  "errmsg":"invalid timestamp"
}

// 签名不匹配
{
  "errcode":310000,
  "errmsg":"sign not match"
}

// IP地址不在白名单
{
  "errcode":310000,
  "errmsg":"ip X.X.X.X not in whitelist"
}
  • 使用 (1)获取到Webhook地址后,用户可以向这个地址发起HTTP POST 请求,即可实现给该钉钉群发送消息。注意,发起POST请求时,必须将字符集编码设置成UTF-8。

(2)当前自定义机器人支持文本 (text)、链接 (link)、markdown(markdown)、ActionCard、FeedCard消息类型,大家可以根据自己的使用场景选择合适的消息类型,达到最好的展示样式。

(3)自定义机器人发送消息时,可以通过手机号码指定“被@人列表”。在“被@人列表”里面的人员收到该消息时,会有@消息提醒(免打扰会话仍然通知提醒,首屏出现“有人@你”)。

(4)当前机器人尚不支持应答机制 (该机制指的是群里成员在聊天@机器人的时候,钉钉回调指定的服务地址,即Outgoing机器人)。