epic-ding-message 0.0.1
Use DingDing Api send Msg to DingDing Group

自定义机器人开发

更新时间：2020-10-15 17:40:42

## 获取自定义机器人webhook

步骤一，打开机器人管理页面。以PC端为例，打开PC端钉钉，点击头像，选择“机器人管理”。

![image.png](https://static-aliyun-doc.oss-cn-hangzhou.aliyuncs.com/assets/img/zh-CN/3824199951/p131222.png)



步骤二，在机器人管理页面选择“自定义”机器人，输入机器人名字并选择要发送消息的群，同时可以为机器人设置机器人头像。



![屏幕快照 2019-10-25 下午1.50.45.png](https://static-aliyun-doc.oss-cn-hangzhou.aliyuncs.com/assets/img/zh-CN/4824199951/p131223.png)



步骤三，完成必要的安全设置（至少选择一种），勾选 我已阅读并同意《自定义机器人服务及免责条款》，点击“完成”。安全设置目前有3种方式，设置说明见下文介绍。

![image.png](https://static-aliyun-doc.oss-cn-hangzhou.aliyuncs.com/assets/img/zh-CN/4824199951/p131224.png)



步骤四，完成安全设置后，复制出机器人的Webhook地址，可用于向这个群发送消息，格式如下：



```javascript
https://oapi.dingtalk.com/robot/send?access_token=XXXXXX
```



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

## 安全设置

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

**（1）方式一，自定义关键词**

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

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

则这个机器人所发送的消息，必须包含 监控报警 这个词，才能发送成功。



**（2）方式二，加签名**

第一步，把timestamp+"\n"+密钥当做签名字符串，使用HmacSHA256算法计算签名，然后进行Base64 encode，最后再把签名参数再进行urlEncode，得到最终的签名（需要使用UTF-8字符集）。



| **参数**  | **说明**                                                    |
| --------- | ----------------------------------------------------------- |
| timestamp | 当前时间戳，单位是毫秒，与请求调用时间误差不能超过1小时     |
| secret    | 密钥，机器人安全设置页面，加签一栏下面显示的SEC开头的字符串 |

签名计算代码示例（Python）

```javascript
#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)
```

```javascript
#python 2.7
import time
import hmac
import hashlib
import base64
import urllib

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

第二步，把 timestamp和第一步得到的签名值拼接到URL中。

| **参数**  | **说明**             |
| --------- | -------------------- |
| timestamp | 第一步使用到的时间戳 |
| sign      | 第一步得到的签名值   |

```javascript
https://oapi.dingtalk.com/robot/send?access_token=XXXXXX&timestamp=XXX&sign=XXX
```

**（3）方式三，IP地址（段）**

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

![image.png](https://static-aliyun-doc.oss-cn-hangzhou.aliyuncs.com/assets/img/zh-CN/4824199951/p131225.png)

**注意：安全设置的上述三种方式，需要****至少****设置其中一种，以进行安全保护**。校验不通过的消息将会发送失败，错误如下：

```javascript
// 消息内容中不包含任何关键词
{
  "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机器人)。

**SDK ：**

可以下载[SDK](https://open-doc.dingtalk.com/microapp/faquestions/vzbp02)，简化调用方式。

**消息发送频率限制：**

每个机器人每分钟最多发送20条。消息发送太频繁会严重影响群成员的使用体验，大量发消息的场景 (譬如系统监控报警) 可以将这些信息进行整合，通过markdown消息以摘要的形式发送到群里。



## 测试自定义机器人

> 通过下面方法，可以快速验证自定义机器人是否可以正常工作：

使用命令行工具curl。

为避免出错，将以下命令逐行复制到命令行，需要将xxxxxxxx替换为真实access_token；若测试出错，请检查复制的命令是否和测试命令一致，多特殊字符会报错。

```javascript
curl 'https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxx' \
   -H 'Content-Type: application/json' \
   -d '{"msgtype": "text","text": {"content": "我就是我, 是不一样的烟火"}}'
```





## 安装及使用

```bash
pip install epic-ding-message -i https://pypi.org/simple/
```

```python
from EpicDingMessage.message import EpicDingMessage
ding_msg = EpicDingMessage("https://oapi.dingtalk.com/robot/send?access_token=xxxxx") # your webhook
# 提供一下几个方法，不同方法的具体参数请参考 【消息类型及数据格式】
ding_msg.send_text("【测试】我艾特你了吗？")
ding_msg.send_link(...)
ding_msg.send_action_card_single(...)
ding_msg.send_action_card_multi(...)
ding_msg.send_feed_card(...)
```



## 消息类型及数据格式

## text类型

```javascript
{
    "msgtype": "text",
    "text": {
        "content": "我就是我, 是不一样的烟火@156xxxx8827"
    },
    "at": {
        "atMobiles": [
            "156xxxx8827",
            "189xxxx8325"
        ],
        "isAtAll": false
    }
}
```

| **参数**  | **参数类型** | **必须** | **说明**                                    |
| --------- | ------------ | -------- | ------------------------------------------- |
| msgtype   | String       | 是       | 消息类型，此时固定为：text                  |
| content   | String       | 是       | 消息内容                                    |
| atMobiles | Array        | 否       | 被@人的手机号（在content里添加@人的手机号） |
| isAtAll   | Boolean      | 否       | 是否@所有人                                 |



![img](https://static-aliyun-doc.oss-cn-hangzhou.aliyuncs.com/assets/img/zh-CN/4824199951/p131226.png)

## link类型

```javascript
{
    "msgtype": "link",
    "link": {
        "text": "这个即将发布的新版本，创始人xx称它为红树林。而在此之前，每当面临重大升级，产品经理们都会取一个应景的代号，这一次，为什么是红树林",
        "title": "时代的火车向前开",
        "picUrl": "",
        "messageUrl": "https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI"
    }
}
```

| **参数**   | **参数类型** | **必须** | **说明**                       |
| ---------- | ------------ | -------- | ------------------------------ |
| msgtype    | String       | 是       | 消息类型，此时固定为：link     |
| title      | String       | 是       | 消息标题                       |
| text       | String       | 是       | 消息内容。如果太长只会部分展示 |
| messageUrl | String       | 是       | 点击消息跳转的URL              |
| picUrl     | String       | 否       | 图片URL                        |



![image.png](https://static-aliyun-doc.oss-cn-hangzhou.aliyuncs.com/assets/img/zh-CN/4824199951/p131227.png)

## markdown类型

略过，本模块不推荐使用markdown。

## 整体跳转ActionCard类型

```javascript
{
    "actionCard": {
        "title": "乔布斯 20 年前想打造一间苹果咖啡厅，而它正是 Apple Store 的前身",
        "text": "![screenshot](https://gw.alicdn.com/tfs/TB1ut3xxbsrBKNjSZFpXXcXhFXa-846-786.png)
 ### 乔布斯 20 年前想打造的苹果咖啡厅
 Apple Store 的设计正从原来满满的科技感走向生活化，而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划",
        "btnOrientation": "0",
        "singleTitle" : "阅读全文",
        "singleURL" : "https://www.dingtalk.com/"
    },
    "msgtype": "actionCard"
}
```

| **参数**       | **类型** | **必须** | **说明**                                        |
| -------------- | -------- | -------- | ----------------------------------------------- |
| msgtype        | String   | 是       | 此消息类型为固定actionCard                      |
| title          | String   | 是       | 首屏会话透出的展示内容                          |
| text           | String   | 是       | markdown格式的消息                              |
| singleTitle    | String   | 是       | 单个按钮的标题。(设置此项和singleURL后btns无效) |
| singleURL      | String   | 是       | 点击singleTitle按钮触发的URL                    |
| btnOrientation | String   | 否       | 0-按钮竖直排列，1-按钮横向排列                  |

通过整体跳转ActionCard类型消息发出的消息样式如下：

![img](https://static-aliyun-doc.oss-cn-hangzhou.aliyuncs.com/assets/img/zh-CN/4824199951/p131229.png)![img](https://static-aliyun-doc.oss-cn-hangzhou.aliyuncs.com/assets/img/zh-CN/5824199951/p131230.png)

## 独立跳转ActionCard类型

```javascript
{
    "actionCard": {
        "title": "乔布斯 20 年前想打造一间苹果咖啡厅，而它正是 Apple Store 的前身",
        "text": "![screenshot](https://gw.alicdn.com/tfs/TB1ut3xxbsrBKNjSZFpXXcXhFXa-846-786.png)
 ### 乔布斯 20 年前想打造的苹果咖啡厅
 Apple Store 的设计正从原来满满的科技感走向生活化，而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划",
        "btnOrientation": "0",
        "btns": [
            {
                "title": "内容不错",
                "actionURL": "https://www.dingtalk.com/"
            },
            {
                "title": "不感兴趣",
                "actionURL": "https://www.dingtalk.com/"
            }
        ]
    },
    "msgtype": "actionCard"
}
```

| **参数**       | **类型** | **必须** | **说明**                       |
| -------------- | -------- | -------- | ------------------------------ |
| msgtype        | String   | 是       | 此消息类型为固定actionCard     |
| title          | String   | 是       | 首屏会话透出的展示内容         |
| text           | String   | 是       | markdown格式的消息             |
| btns           | Array    | 是       | 按钮                           |
| └title         | String   | 是       | 按钮标题                       |
| └actionURL     | String   | 是       | 点击按钮触发的URL              |
| btnOrientation | String   | 否       | 0-按钮竖直排列，1-按钮横向排列 |

通过独立跳转ActionCard类型消息发出的消息样式如下：

![image.png](https://static-aliyun-doc.oss-cn-hangzhou.aliyuncs.com/assets/img/zh-CN/5824199951/p131231.png)

## FeedCard类型

```javascript
{
    "feedCard": {
        "links": [
            {
                "title": "时代的火车向前开",
                "messageURL": "https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI",
                "picURL": "https://gw.alicdn.com/tfs/TB1ayl9mpYqK1RjSZLeXXbXppXa-170-62.png"
            },
            {
                "title": "时代的火车向前开2",
                "messageURL": "https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI",
                "picURL": "https://gw.alicdn.com/tfs/TB1ayl9mpYqK1RjSZLeXXbXppXa-170-62.png"
            }
        ]
    },
    "msgtype": "feedCard"
}
```

| **参数**   | **类型** | **必须** | **说明**                 |
| ---------- | -------- | -------- | ------------------------ |
| msgtype    | String   | 是       | 此消息类型为固定feedCard |
| title      | String   | 是       | 单条信息文本             |
| messageURL | String   | 是       | 点击单条信息到跳转链接   |
| picURL     | String   | 是       | 单条信息后面图片的URL    |

通过FeedCard类型消息发出的消息样式如下：

![img](https://static-aliyun-doc.oss-cn-hangzhou.aliyuncs.com/assets/img/zh-CN/5824199951/p131232.png)