Skip to content

Commit

Permalink
feat: 支持消息新字段atUserIds,修复新建连接失败问题issue#53,完善测试用例和文档
Browse files Browse the repository at this point in the history
  • Loading branch information
devin committed Dec 7, 2022
1 parent f054c49 commit f94b71d
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 47 deletions.
22 changes: 12 additions & 10 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

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

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

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

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


三、各消息类型使用示例
======================
Expand Down Expand Up @@ -171,9 +172,10 @@
四、常见注意事项
===========================

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



Expand Down
2 changes: 1 addition & 1 deletion dingtalkchatbot/__about__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
__title__ = 'DingtalkChatbot'
__description__ = '一个钉钉自定义机器人消息的Python封装库'
__url__ = 'https://github.com/zhuifengshen/DingtalkChatbot'
__version__ = '1.5.3'
__version__ = '1.5.7'
__author__ = 'devin'
__author_email__ = '[email protected]'
__license__ = 'MIT'
Expand Down
30 changes: 15 additions & 15 deletions dingtalkchatbot/chatbot.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
# create time: 07/01/2018 11:35
__author__ = 'Devin -- http://zhangchuzhao.site'
__author__ = 'Devin - https://zhuifengshen.github.io'

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

if at_dingtalk_ids:
at_dingtalk_ids = list(map(str, at_dingtalk_ids))
data["at"]["atDingtalkIds"] = at_dingtalk_ids
data["at"]["atUserIds"] = at_dingtalk_ids

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

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

if at_dingtalk_ids:
at_dingtalk_ids = list(map(str, at_dingtalk_ids))
data["at"]["atDingtalkIds"] = at_dingtalk_ids
data["at"]["atUserIds"] = at_dingtalk_ids

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

def get_data(self):
Expand Down Expand Up @@ -404,7 +404,7 @@ def get_data(self):

class FeedLink(object):
"""
FeedCard类型单条消息格式
FeedCard类型单条消息格式(已废弃,直接使用 CardItem 即可)
"""
def __init__(self, title, message_url, pic_url):
"""
Expand Down
46 changes: 25 additions & 21 deletions dingtalkchatbot/chatbot_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@

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

__author__ = 'Devin -- http://zhangchuzhao.site'

__author__ = 'Devin - https://zhuifengshen.github.io'

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

@classmethod
def setUpClass(cls):
# 1.无加签
cls.webhook = 'https://oapi.dingtalk.com/robot/send?access_token=77eb420ff2761ad516d974e1428c3e198b84faabc9c9ef8e86b2c71ac60bd0ea'
cls.xiaoding = DingtalkChatbot(cls.webhook)
# 2.有加签
#cls.webhook = 'https://oapi.dingtalk.com/robot/send?access_token=fab4f070e0214d2e3f7429acd18bc38848cc7043f9191ed1f96fa090ab25b943'
#cls.xiaoding = DingtalkChatbot(cls.webhook, secret='SEC225443235b43d49959eaca83b15b5b93ec747d662ad347a2b3483a7e67d8b96b')


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

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

def test_send_link(self):
"""测试发送链接消息函数"""
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')
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')
self.assertEqual(result['errcode'], 0)

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

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

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

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

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

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

def test_send_feedcard_old_api(self):
"""测试发送FeedCard类型消息功能(FeedLink旧API)"""
feedlink1 = FeedLink(title="氧气美女", message_url="https://www.dingtalk.com/", pic_url="http://pic1.win4000.com/wallpaper/2020-03-11/5e68b0557f3a6.jpg")
feedlink2 = FeedLink(title="氧眼美女", message_url="https://www.dingtalk.com/", pic_url="http://pic1.win4000.com/wallpaper/2020-03-11/5e68b0557f3a6.jpg")
feedlink3 = FeedLink(title="氧神美女", message_url="https://www.dingtalk.com/", pic_url="http://pic1.win4000.com/wallpaper/2020-03-11/5e68b0557f3a6.jpg")
"""测试发送FeedCard类型消息功能(基于FeedLink旧API)"""
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")
feedlink2 = FeedLink(title="氧眼美女", message_url="https://www.dingtalk.com/", pic_url="http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png")
feedlink3 = FeedLink(title="氧神美女", message_url="https://www.dingtalk.com/", pic_url="http://www.songshan.es/wp-content/uploads/2016/01/Yin-Yang.png")
links = [feedlink1, feedlink2, feedlink3]
result = self.xiaoding.send_feed_card(links)
self.assertEqual(result['errcode'], 0)
Expand Down

0 comments on commit f94b71d

Please sign in to comment.