Skip to content

Commit f94b71d

Browse files
author
devin
committed
feat: 支持消息新字段atUserIds,修复新建连接失败问题issue#53,完善测试用例和文档
1 parent f054c49 commit f94b71d

File tree

4 files changed

+53
-47
lines changed

4 files changed

+53
-47
lines changed

README.rst

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99

1010
2、聚合Trello、JIRA等项目协调服务,实现项目信息同步;
1111

12-
3、机器人支持Webhook自定义接入,就可以实现更多可能性,例如:将运维报警、自动化测试结果报告、工作&生活日程安排(上班打卡、下班吃饭、健身、读书、生日、纪念日...)的提醒;
12+
3、机器人支持Webhook自定义接入,就可以实现更多可能性,例如:将运维报警、产品数据、自动化测试报告、工作&生活日程安排(上班打卡、下班吃饭、健身、读书、生日、纪念日...)的提醒;
1313

14-
目前自定义机器人支持文本(text)、链接(link)、markdown三种消息格式,五种消息类型,详细信息请参考\ `自定义机器人官方文档 <https://ding-doc.dingtalk.com/doc#/serverapi2/qf2nxq>`__
14+
目前自定义机器人支持文本(text)、链接(link)、markdown三种消息格式,五种消息类型,详细信息请参考\ `自定义机器人官方文档 <https://open.dingtalk.com/document/group/custom-robot-access>`__
1515

1616
二、安装使用
1717
============
@@ -46,12 +46,13 @@
4646
- 支持image表情消息;
4747
- 支持Markdown消息;
4848
- 支持ActionCard消息;
49-
- 支持消息发送失败时自动通知(默认fail_notice=False不通知,开发者可根据返回的消息发送结果自行判断处理)
50-
- 支持设置消息链接打开方式(默认pc_slide=False,跳转至浏览器打开,pc_slide=True,则在PC端侧边栏打开)
51-
- 支持钉钉官方消息发送频率限制限制:每个机器人每分钟最多发送20条;
49+
- 支持消息发送失败时自动通知(默认fail_notice=False不通知,开发者可直接根据返回的消息发送结果自行处理)
50+
- 支持设置消息链接打开方式(默认pc_slide=False跳转至浏览器打开;pc_slide=True则在PC端侧边栏打开)
51+
- 支持钉钉官方消息发送频率限制,即每个机器人每分钟最多发送20条,不用担心触发限流;
52+
- 支持新版钉钉机器人加密设置;
5253
- 支持Python2、Python3;
53-
- 支持钉钉企业内部机器人\ `自定义outgoing机器人消息发送 <https://ding-doc.dingtalk.com/doc#/serverapi2/elzz1p>`__;
54-
- 支持最新版钉钉机器人加密设置密钥验证;
54+
- 支持钉钉企业内部机器人\ `即outgoing机器人的消息发送 <https://open.dingtalk.com/document/group/enterprise-created-chatbot>`__;
55+
5556

5657
三、各消息类型使用示例
5758
======================
@@ -171,9 +172,10 @@
171172
四、常见注意事项
172173
===========================
173174

174-
- 1、at_mobiles列表上的手机号默认自动添加到消息文本末尾,可将参数改为is_auto_at=False取消自动化添加,在消息文本自定义@的位置,支持同时@多个手机号,以便突出对应的人去关注对应的内容;
175-
- 2、图片链接是Http,在网页版钉钉无法正常显示,在客户端则可以,需要更改为使用Https;
176-
- 3、消息链接打开方式可以在初始化机器人时设置(默认pc_slide=False,跳转至浏览器打开,pc_slide=True,则在PC端侧边栏打开);
175+
- 1、at_mobiles列表上的手机号默认自动添加到消息文本末尾,才有@的效果,如需自定义在消息文本@的位置,可将参数is_auto_at设置为False即可,同时支持@多个手机号;
176+
- 2、如果钉钉机器人消息中的图片链接使用Http协议,可以在钉钉客户端正常加载显示,但无法在钉钉网页版显示,需要更改为Https协议;
177+
- 3、钉钉机器人消息中的链接默认跳转至浏览器打开(即:pc_slide=False),如果需要直接在客户端侧边栏打开,设置pc_slide为True即可;
178+
- 4、当前钉钉自定义机器人官方尚不支持应答机制(即在群里成员在聊天@机器人),此功能需要使用钉钉企业内部开发机器人实现(即Outgoing机器人;
177179

178180

179181

dingtalkchatbot/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
__title__ = 'DingtalkChatbot'
22
__description__ = '一个钉钉自定义机器人消息的Python封装库'
33
__url__ = 'https://github.com/zhuifengshen/DingtalkChatbot'
4-
__version__ = '1.5.3'
4+
__version__ = '1.5.7'
55
__author__ = 'devin'
66
__author_email__ = '1324556701@qq.com'
77
__license__ = 'MIT'

dingtalkchatbot/chatbot.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python
22
# _*_ coding:utf-8 _*_
33
# create time: 07/01/2018 11:35
4-
__author__ = 'Devin -- http://zhangchuzhao.site'
4+
__author__ = 'Devin - https://zhuifengshen.github.io'
55

66
import re
77
import sys
@@ -63,7 +63,7 @@ def __init__(self, webhook, secret=None, pc_slide=False, fail_notice=False):
6363
:param fail_notice: 消息发送失败提醒,默认为False不提醒,开发者可以根据返回的消息发送结果自行判断和处理
6464
"""
6565
super(DingtalkChatbot, self).__init__()
66-
self.headers = {'Content-Type': 'application/json; charset=utf-8'}
66+
self.headers = {'Content-Type': 'application/json; charset=utf-8', 'Connection': 'close'} # fix issue #53
6767
self.queue = queue.Queue(20) # 钉钉官方限流每分钟发送20条信息
6868
self.webhook = webhook
6969
self.secret = secret
@@ -112,9 +112,9 @@ def send_text(self, msg, is_at_all=False, at_mobiles=[], at_dingtalk_ids=[], is_
112112
text类型
113113
:param msg: 消息内容
114114
:param is_at_all: @所有人时:true,否则为false(可选)
115-
:param at_mobiles: 被@人的手机号(注意:可以在msg内容里自定义@手机号的位置,也支持同时@多个手机号,可选)
116-
:param at_dingtalk_ids: 被@人的dingtalkId(可选)
117-
:param is_auto_at: 是否自动在msg内容末尾添加@手机号,默认自动添加,可设置为False取消(可选)
115+
:param at_mobiles: 被@用户的手机号
116+
:param at_dingtalk_ids: 被@用户的UserId(企业内部机器人可用,可选)
117+
:param is_auto_at: 是否自动在msg内容末尾添加@手机号,默认自动添加,也可设置为False,然后自行在msg内容中自定义@手机号的位置,才有@效果,支持同时@多个手机号(可选)
118118
:return: 返回消息发送结果
119119
"""
120120
data = {"msgtype": "text", "at": {}}
@@ -136,14 +136,14 @@ def send_text(self, msg, is_at_all=False, at_mobiles=[], at_dingtalk_ids=[], is_
136136

137137
if at_dingtalk_ids:
138138
at_dingtalk_ids = list(map(str, at_dingtalk_ids))
139-
data["at"]["atDingtalkIds"] = at_dingtalk_ids
139+
data["at"]["atUserIds"] = at_dingtalk_ids
140140

141141
logging.debug('text类型:%s' % data)
142142
return self.post(data)
143143

144144
def send_image(self, pic_url):
145145
"""
146-
image类型(表情)
146+
image类型
147147
:param pic_url: 图片链接
148148
:return: 返回消息发送结果
149149
"""
@@ -192,9 +192,9 @@ def send_markdown(self, title, text, is_at_all=False, at_mobiles=[], at_dingtalk
192192
:param title: 首屏会话透出的展示内容
193193
:param text: markdown格式的消息内容
194194
:param is_at_all: @所有人时:true,否则为:false(可选)
195-
:param at_mobiles: 被@人的手机号(默认自动添加在text内容末尾,可取消自动化添加改为自定义设置,可选)
196-
:param at_dingtalk_ids: 被@人的dingtalkId(可选)
197-
:param is_auto_at: 是否自动在text内容末尾添加@手机号,默认自动添加,可设置为False取消(可选)
195+
:param at_mobiles: 被@人的手机号
196+
:param at_dingtalk_ids: 被@用户的UserId(企业内部机器人可用,可选)
197+
:param is_auto_at: 是否自动在text内容末尾添加@手机号,默认自动添加,也可设置为False,然后自行在text内容中自定义@手机号的位置,才有@效果,支持同时@多个手机号(可选)
198198
:return: 返回消息发送结果
199199
"""
200200
if all(map(is_not_null_and_blank_str, [title, text])):
@@ -220,7 +220,7 @@ def send_markdown(self, title, text, is_at_all=False, at_mobiles=[], at_dingtalk
220220

221221
if at_dingtalk_ids:
222222
at_dingtalk_ids = list(map(str, at_dingtalk_ids))
223-
data["at"]["atDingtalkIds"] = at_dingtalk_ids
223+
data["at"]["atUserIds"] = at_dingtalk_ids
224224

225225
logging.debug("markdown类型:%s" % data)
226226
return self.post(data)
@@ -326,8 +326,8 @@ def post(self, data):
326326
error_data = {
327327
"msgtype": "text",
328328
"text": {
329-
"content": "[注意-自动通知]钉钉机器人消息发送失败,时间:%s,原因:%s,请及时跟进,谢谢!" % (
330-
time_now, result['errmsg'] if result.get('errmsg', False) else '未知异常')
329+
"content": "[异常通知]钉钉机器人消息发送失败,失败时间:%s,失败原因:%s,要发送的消息:%s,请及时跟进,谢谢!" % (
330+
time_now, result['errmsg'] if result.get('errmsg', False) else '未知异常', post_data)
331331
},
332332
"at": {
333333
"isAtAll": False
@@ -361,7 +361,7 @@ def __init__(self, title, text, btns, btn_orientation=0, hide_avatar=0):
361361
if isinstance(btn, CardItem):
362362
btn_list.append(btn.get_data())
363363
if btn_list:
364-
btns = btn_list # 兼容:1、传入CardItem示例列表;2、传入数据字典列表
364+
btns = btn_list # 兼容:1、传入CardItem列表;2、传入数据字典列表
365365
self.btns = btns
366366

367367
def get_data(self):
@@ -404,7 +404,7 @@ def get_data(self):
404404

405405
class FeedLink(object):
406406
"""
407-
FeedCard类型单条消息格式
407+
FeedCard类型单条消息格式(已废弃,直接使用 CardItem 即可)
408408
"""
409409
def __init__(self, title, message_url, pic_url):
410410
"""

dingtalkchatbot/chatbot_test.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,20 @@
55

66
from dingtalkchatbot.chatbot import DingtalkChatbot, is_not_null_and_blank_str, ActionCard, FeedLink, CardItem
77

8-
__author__ = 'Devin -- http://zhangchuzhao.site'
9-
8+
__author__ = 'Devin - https://zhuifengshen.github.io'
109

1110
class TestDingtalkChatbot(unittest.TestCase):
1211
"""DingtalkChatbot 测试用例"""
1312

1413
@classmethod
1514
def setUpClass(cls):
15+
# 1.无加签
1616
cls.webhook = 'https://oapi.dingtalk.com/robot/send?access_token=77eb420ff2761ad516d974e1428c3e198b84faabc9c9ef8e86b2c71ac60bd0ea'
1717
cls.xiaoding = DingtalkChatbot(cls.webhook)
18+
# 2.有加签
19+
#cls.webhook = 'https://oapi.dingtalk.com/robot/send?access_token=fab4f070e0214d2e3f7429acd18bc38848cc7043f9191ed1f96fa090ab25b943'
20+
#cls.xiaoding = DingtalkChatbot(cls.webhook, secret='SEC225443235b43d49959eaca83b15b5b93ec747d662ad347a2b3483a7e67d8b96b')
21+
1822

1923
def test_is_not_null_and_blank_str(self):
2024
"""测试字符串不为空函数"""
@@ -31,12 +35,12 @@ def test_send_text(self):
3135

3236
def test_send_image(self):
3337
"""测试发送表情图片消息函数"""
34-
result = self.xiaoding.send_image(pic_url='http://uc-test-manage-00.umlife.net/jenkins/pic/flake8.png')
38+
result = self.xiaoding.send_image(pic_url='http://www.sinaimg.cn/dy/slidenews/5_img/2013_28/453_28488_469248.jpg')
3539
self.assertEqual(result['errcode'], 0)
3640

3741
def test_send_link(self):
3842
"""测试发送链接消息函数"""
39-
result = self.xiaoding.send_link(title='万万没想到,某小璐竟然...', text='故事是这样子的...', message_url='http://www.kwongwah.com.my/?p=454748', pic_url='https://pbs.twimg.com/media/CEwj7EDWgAE5eIF.jpg')
43+
result = self.xiaoding.send_link(title='万万没想到,某小璐竟然...', text='故事是这样子的...', message_url='https://open.dingtalk.com/document/group/custom-robot-access', pic_url='http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png')
4044
self.assertEqual(result['errcode'], 0)
4145

4246
def test_send_markdown(self):
@@ -49,28 +53,28 @@ def test_send_markdown(self):
4953
self.assertEqual(result['errcode'], 0)
5054

5155
def test_send_actioncard(self):
52-
"""测试发送整体跳转ActionCard消息功能(CardItem新API)"""
53-
btns1 = [CardItem(title="查看详情", url="https://www.dingtalk.com/")]
56+
"""1.测试发送整体跳转ActionCard消息功能(基于CardItem新API)"""
57+
btns1 = [CardItem(title="查看详情", url="https://open.dingtalk.com/document/group/custom-robot-access")]
5458
actioncard1 = ActionCard(title='万万没想到,竟然...',
55-
text='![markdown](http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png) \n### 故事是这样子的...',
59+
text='![markdown](http://www.sinaimg.cn/dy/slidenews/5_img/2013_28/453_28488_469248.jpg) \n### 故事是这样子的...',
5660
btns=btns1,
5761
btn_orientation=1,
5862
hide_avatar=1)
5963
result = self.xiaoding.send_action_card(actioncard1)
6064
self.assertEqual(result['errcode'], 0)
6165

62-
"""测试发送单独跳转ActionCard消息功能"""
63-
btns2 = [CardItem(title="支持", url="https://www.dingtalk.com/"), CardItem(title="反对", url="http://www.back china.com/news/2018/01/11/537468.html")]
66+
"""2.测试发送单独跳转ActionCard消息功能(基于CardItem新API)"""
67+
btns2 = [CardItem(title="支持", url="https://www.dingtalk.com/"), CardItem(title="反对", url="https://www.baidu.com/")]
6468
actioncard2 = ActionCard(title='万万没想到,竟然...',
65-
text='![markdown](http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png) \n### 故事是这样子的...',
69+
text='![markdown](http://www.sinaimg.cn/dy/slidenews/5_img/2013_28/453_28488_469248.jpg) \n### 故事是这样子的...',
6670
btns=btns2,
6771
btn_orientation=1,
6872
hide_avatar=1)
6973
result = self.xiaoding.send_action_card(actioncard2)
7074
self.assertEqual(result['errcode'], 0)
7175

7276
def test_send_actioncard_old_api(self):
73-
"""测试发送整体跳转ActionCard消息功能(数据列表btns旧API)"""
77+
""" 1.测试发送整体跳转ActionCard消息功能(基于字典旧API)"""
7478
btns1 = [{"title": "查看详情", "actionURL": "https://www.dingtalk.com/"}]
7579
actioncard1 = ActionCard(title='万万没想到,竟然...',
7680
text='![markdown](http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png) \n### 故事是这样子的...',
@@ -80,9 +84,9 @@ def test_send_actioncard_old_api(self):
8084
result = self.xiaoding.send_action_card(actioncard1)
8185
self.assertEqual(result['errcode'], 0)
8286

83-
"""测试发送单独跳转ActionCard消息功能"""
87+
"""2.测试发送单独跳转ActionCard消息功能(基于字典旧API)"""
8488
btns2 = [{"title": "支持", "actionURL": "https://www.dingtalk.com/"},
85-
{"title": "反对", "actionURL": "http://www.back china.com/news/2018/01/11/537468.html"}]
89+
{"title": "反对", "actionURL": "https://www.baidu.com/"}]
8690
actioncard2 = ActionCard(title='万万没想到,竟然...',
8791
text='![markdown](http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png) \n### 故事是这样子的...',
8892
btns=btns2,
@@ -92,19 +96,19 @@ def test_send_actioncard_old_api(self):
9296
self.assertEqual(result['errcode'], 0)
9397

9498
def test_send_feedcard(self):
95-
"""测试发送FeedCard类型消息功能(CardItem新API)"""
96-
carditem1 = CardItem(title="氧气美女", url="https://www.dingtalk.com/", pic_url="http://pic1.win4000.com/wallpaper/2020-03-11/5e68b0557f3a6.jpg")
97-
carditem2 = CardItem(title="氧眼美女", url="https://www.dingtalk.com/", pic_url="http://pic1.win4000.com/wallpaper/2020-03-11/5e68b0557f3a6.jpg")
98-
carditem3 = CardItem(title="氧神美女", url="https://www.dingtalk.com/", pic_url="http://pic1.win4000.com/wallpaper/2020-03-11/5e68b0557f3a6.jpg")
99+
"""测试发送FeedCard类型消息功能(基于CardItem新API)"""
100+
carditem1 = CardItem(title="氧气美女", url="https://www.dingtalk.com/", pic_url="http://www.sinaimg.cn/dy/slidenews/5_img/2013_28/453_28488_469248.jpg")
101+
carditem2 = CardItem(title="氧眼美女", url="https://www.dingtalk.com/", pic_url="http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png")
102+
carditem3 = CardItem(title="氧神美女", url="https://www.dingtalk.com/", pic_url="http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png")
99103
cards = [carditem1, carditem2, carditem3]
100104
result = self.xiaoding.send_feed_card(cards)
101105
self.assertEqual(result['errcode'], 0)
102106

103107
def test_send_feedcard_old_api(self):
104-
"""测试发送FeedCard类型消息功能(FeedLink旧API)"""
105-
feedlink1 = FeedLink(title="氧气美女", message_url="https://www.dingtalk.com/", pic_url="http://pic1.win4000.com/wallpaper/2020-03-11/5e68b0557f3a6.jpg")
106-
feedlink2 = FeedLink(title="氧眼美女", message_url="https://www.dingtalk.com/", pic_url="http://pic1.win4000.com/wallpaper/2020-03-11/5e68b0557f3a6.jpg")
107-
feedlink3 = FeedLink(title="氧神美女", message_url="https://www.dingtalk.com/", pic_url="http://pic1.win4000.com/wallpaper/2020-03-11/5e68b0557f3a6.jpg")
108+
"""测试发送FeedCard类型消息功能(基于FeedLink旧API)"""
109+
feedlink1 = FeedLink(title="氧气美女", message_url="https://www.dingtalk.com/", pic_url="http://www.sinaimg.cn/dy/slidenews/5_img/2013_28/453_28488_469248.jpg")
110+
feedlink2 = FeedLink(title="氧眼美女", message_url="https://www.dingtalk.com/", pic_url="http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png")
111+
feedlink3 = FeedLink(title="氧神美女", message_url="https://www.dingtalk.com/", pic_url="http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png")
108112
links = [feedlink1, feedlink2, feedlink3]
109113
result = self.xiaoding.send_feed_card(links)
110114
self.assertEqual(result['errcode'], 0)

0 commit comments

Comments
 (0)