钉钉机器人 API 是阿里巴巴旗下钉钉平台提供的一种基于 HTTP 协议的 API 服务,它可以帮助开发者快速构建智能机器人,实现与用户的实时互动和自动回复。钉钉机器人 API 的主要功能如下:
消息处理:钉钉机器人 API 提供了消息处理功能,可以实现对用户发送的消息进行接收、解析和回复。开发者可以根据消息内容触发不同的动作,如发送文件、发送卡片、回复文本等。
知识库管理:钉钉机器人 API 提供了知识库管理功能,可以实现对机器人所掌握的知识进行增删改查。知识库中可以包含各种类型的信息,如问题、答案、事件等,可以方便地支持机器人的语义理解。
事件订阅:钉钉机器人 API 提供了事件订阅功能,可以实现对钉钉平台上的各种事件进行订阅。开发者可以订阅特定的事件,如用户添加好友、用户进入某个房间等,并触发相应的动作。
智能回复:钉钉机器人 API 提供了智能回复功能,可以实现根据用户发送的消息自动回复。开发者可以根据消息内容进行语义分析,并返回合适的回复。智能回复可以有效地提高机器人的智能化水平,提升用户体验。
用户管理:钉钉机器人 API 提供了用户管理功能,可以实现对用户进行增删改查。开发者可以管理用户的个人信息、好友关系、聊天记录等,并可以对用户进行相应的操作。
数据统计:钉钉机器人 API 提供了数据统计功能,可以实现对机器人的数据进行统计和分析。开发者可以查看机器人的消息处理量、用户活跃度、回复率等数据,并可以根据数据进行相应的优化。
钉钉机器人 API 提供了丰富的功能,可以帮助开发者快速构建智能机器人,实现与用户的实时互动和自动回复。
# -*- coding: utf-8 -*-
import hmac
import hashlib
import base64
import urllib.parse
import time
import json
import urllib.request
from loguru import logger
from requests import request
class DingTalkBot():
"""
钉钉机器人
"""
def __init__(self, secret=None, key=None):
if key:
self.secret = secret
self.webhook_url = 'https://oapi.dingtalk.com/robot/send?access_token={}'.format(key)
else:
# 测试群的配置
self.secret = '自己配置'
self.webhook_url = 'https://oapi.dingtalk.com/robot/send?access_token=自己配置'
# 适配钉钉机器人的加签模式和关键字模式/白名单IP模式
if self.secret:
timestamp = str(round(time.time() * 1000))
sign = self.get_sign(self.secret, timestamp)
self.webhook_url = self.webhook_url + f'×tamp={timestamp}&sign={sign}' # 最终url,url+时间戳+签名
else:
self.webhook_url = self.webhook_url
self.headers = {
"Content-Type": "application/json",
"Charset": "UTF-8"
}
def get_sign(self, secret, timestamp):
"""
根据时间戳 + "sign" 生成密钥
把timestamp+"\n"+密钥当做签名字符串,使用HmacSHA256算法计算签名,然后进行Base64 encode,最后再把签名参数再进行urlEncode,得到最终的签名(需要使用UTF-8字符集)。
:return:
"""
secret = f'{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))
# string_to_sign = f'{timestamp}\n{secret}'.encode('utf-8')
# hmac_code = hmac.new(
# secret.encode('utf-8'),
# string_to_sign,
# digestmod=hashlib.sha256).digest()
#
# sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
return sign
def send_text(self, content, mobiles=None, is_at_all=False):
"""
发送文本消息
:param content: 发送的内容
:param mobiles: 被艾特的用户的手机号码,格式是列表,注意需要在content里面添加@人的手机号码
:param is_at_all: 是否艾特所有人,布尔类型,true为艾特所有人,false为不艾特
"""
if mobiles:
if isinstance(mobiles, list):
payload = {
"msgtype": "text",
"text": {
"content": content
},
"at": {
"atMobiles": mobiles,
"isAtAll": False
}
}
for mobile in mobiles:
payload["text"]["content"] += f"@{mobile}"
else:
raise TypeError("mobiles类型错误 不是list类型.")
else:
payload = {
"msgtype": "text",
"text": {
"content": content
},
"at": {
"atMobiles": "",
"isAtAll": is_at_all
}
}
response = request(url=self.webhook_url, json=payload, headers=self.headers, method="POST")
if response.json().get("errcode") == 0:
logger.debug(f"send_text发送钉钉消息成功:{response.json()}")
return True
else:
logger.debug(f"send_text发送钉钉消息失败:{response.text}")
return False
def send_link(self, title, text, message_url, pic_url=None):
"""
发送链接消息
:param title: 消息标题
:param text: 消息内容,如果太长只会部分展示
:param message_url: 点击消息跳转的url地址
:param pic_url: 图片url
"""
payload = {
"msgtype": "link",
"link": {
"title": title,
"text": text,
"picUrl": pic_url,
"messageUrl": message_url
}
}
response = request(url=self.webhook_url, json=payload, headers=self.headers, method="POST")
if response.json().get("errcode") == 0:
logger.debug(f"send_link发送钉钉消息成功:{response.json()}")
return True
else:
logger.debug(f"send_link发送钉钉消息失败:{response.text}")
return False
def send_markdown(self, title, text, mobiles=None, is_at_all=False):
"""
发送markdown消息
目前仅支持md语法的子集,如标题,引用,文字加粗,文字斜体,链接,图片,无序列表,有序列表
:param title: 消息标题,首屏回话透出的展示内容
:param text: 消息内容,markdown格式
:param mobiles: 被艾特的用户的手机号码,格式是列表,注意需要在text里面添加@人的手机号码
:param is_at_all: 是否艾特所有人,布尔类型,true为艾特所有人,false为不艾特
"""
print(mobiles)
if mobiles:
if isinstance(mobiles, list):
payload = {
"msgtype": "markdown",
"markdown": {
"title": title,
"text": text
},
"at": {
"atMobiles": mobiles,
"isAtAll": False
}
}
for mobile in mobiles:
payload["markdown"]["text"] += f" @{mobile}"
else:
raise TypeError("mobiles类型错误 不是list类型.")
else:
payload = {
"msgtype": "markdown",
"markdown": {
"title": title,
"text": text
},
"at": {
"atMobiles": "",
"isAtAll": is_at_all
}
}
response = request(url=self.webhook_url, json=payload, headers=self.headers, method="POST")
if response.json().get("errcode") == 0:
logger.debug(f"send_markdown发送钉钉消息成功:{response.json()}")
return True
else:
logger.debug(f"send_markdown发送钉钉消息失败:{response.text}")
return False
def send_action_card_single(self, title, text, single_title, single_url, btn_orientation=0):
"""
发送消息卡片(整体跳转ActionCard类型)
:param title: 消息标题
:param text: 消息内容,md格式消息
:param single_title: 单个按钮的标题
:param single_url: 点击singleTitle按钮后触发的URL
:param btn_orientation: 0-按钮竖直排列,1-按钮横向排列
"""
payload = {
"msgtype": "actionCard",
"actionCard": {
"title": title,
"text": text,
"singleTitle": single_title,
"singleURL": single_url,
"btnOrientation": btn_orientation,
}
}
response = request(url=self.webhook_url, json=payload, headers=self.headers, method="POST")
if response.json().get("errcode") == 0:
logger.debug(f"send_action_card_single发送钉钉消息成功:{response.json()}")
return True
else:
logger.debug(f"send_action_card_single发送钉钉消息失败:{response.text}")
return False
def send_action_card_split(self, title, text, btns, btn_orientation=0):
"""
发送消息卡片(独立跳转ActionCard类型)
:param title: 消息标题
:param text: 消息内容,md格式消息
:param btns: 列表嵌套字典类型,"btns": [{"title": "内容不错", "actionURL": "https://www.dingtalk.com/"}, ......]
:param btn_orientation: 0-按钮竖直排列,1-按钮横向排列
"""
payload = {
"msgtype": "actionCard",
"actionCard": {
"title": title,
"text": text,
"btns": [],
"btnOrientation": btn_orientation,
}
}
for btn in btns:
payload["actionCard"]["btns"].append({
"title": btn.get("title"),
"actionURL": btn.get("action_url")
})
response = request(url=self.webhook_url, json=payload, headers=self.headers, method="POST")
if response.json().get("errcode") == 0:
logger.debug(f"send_action_card_split发送钉钉消息成功:{response.json()}")
return True
else:
logger.debug(f"send_action_card_split发送钉钉消息失败:{response.text}")
return False
def send_feed_card(self, links_msg):
"""
发送多组消息卡片(FeedCard类型)
:param links_msg: 列表嵌套字典类型,每一个字段包括如下参数:title(单条信息文本), messageURL(点击单条信息后的跳转链接), picURL(单条信息后面图片的url)
"""
payload = {
"msgtype": "feedCard",
"feedCard": {
"links": []
}
}
for link in links_msg:
payload["feedCard"]["links"].append(
{
"title": link.get("title"),
"messageURL": link.get("messageURL"),
"picURL": link.get("picURL")
}
)
response = request(url=self.webhook_url, json=payload, headers=self.headers, method="POST")
if response.json().get("errcode") == 0:
logger.debug(f"send_feed_card发送钉钉消息成功:{response.json()}")
return True
else:
logger.debug(f"send_feed_card发送钉钉消息失败:{response.text}")
return False
def encapsulation_report(self, project: str, end: str, case_time: str, tq_total: str, tq_passed: str, tq_skipped: str, tq_failed: str,
tq_error, tq_expected,tq_unexpected_passes, tq_rerun, address):
"""
:param project: 项目名称
:param end: 指定端[web、app、h5]
:param case_time: 时长
:param tq_total: 总数
:param tq_passed: 通过
:param tq_skipped: 跳过
:param tq_failed: 失败
:param tq_error: 错误
:param tq_expected: 预期失败
:param tq_unexpected_passes: 意外通过
:param tq_rerun: 重新运行
:param address: 报告地址
:return:
"""
# "# 一级标题 \n## 二级标题 \n> 引用文本 \n**加粗** \n*斜体* \n[百度链接](https://www.baidu.com)\n\n\n\n",
# data = # "# **提醒!接口自动化测试反馈**\n#### **请相关同事注意,及时跟进!**\n"
data = '项目名称:' + "" + project + " \n\n" \
'项目指定端:' + "" + end + " \n\n"\
'执行测试用例时长:' + "" + case_time +" 秒\n\n"\
'测试用例总数:' + "" + tq_total + "条\n\n"\
"**--------------------运行详情--------------------**\n\n"\
'通过数:' + "" + tq_passed + "条\n\n"\
'跳过数:' + "" + tq_skipped + "条\n\n"\
'失败数:' + "" + tq_failed + "条\n\n"\
'错误数:' + " " + tq_error + "条\n\n"\
'预期失败:' + "" + tq_expected + " 条\n\n"\
'意外通过:' + "" + tq_unexpected_passes + "条\n\n"\
'重新运行:' + "" + tq_rerun + " 条\n\n"\
'报告链接:' + "jenkins报告,请点击后进入查看 " + address + "\n\n"
# "##### **报告链接:** [jenkins报告,请点击后进入查看]({})"
# 加粗:**需要加粗的字**
# 引用:> 需要引用的文字
# 字体颜色(只支持3种内置颜色)
# 标题 (支持1至6级标题,注意#与文字中间要有空格)
# 绿色:info、灰色:comment、橙红:warning
return data
if __name__ == '__main__':
dingding = DingTalkBot()
dingding.send_text(content="发送钉钉消息的响应数据12", mobiles=['1816398****', "1326332****"], is_at_all=False)
dingding.send_link(title="chytest", text="时代的长河", message_url="https://www.gitlink.org.cn/chenyh")
dingding.send_markdown(title="test markdown",
text="# 一级标题 \n"
"## 二级标题 \n"
"> 引用文本 \n"
"**加粗** \n"
"*斜体* \n"
"颜色 \n"
"[百度链接](https://www.baidu.com)\n\n\n\n",
mobiles=['1877497****'])
dingding.send_action_card_single(title="测试消息的标题",
text="### 乔布斯 20 年前想打造的苹果咖啡厅 Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划",
single_title="阅读全文", single_url="https://www.gitlink.org.cn/chenyh", btn_orientation=0)
dingding.send_action_card_split(title="测试消息的标题", text="### 乔布斯 20 年前想打造的苹果咖啡厅 Apple Store",
btns=[{"title": "内容不错", "actionURL": "https://www.dingtalk.com/"},
{"title": "不感兴趣", "actionURL": "https://www.dingtalk.com/"}],
btn_orientation=1)
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://tse1-mm.cn.bing.net/th/id/OIP-C.nelCIKYD30NJW6W68-ZHxAHaJA?pid=ImgDet&rs=1"
},
{
"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://tse1-mm.cn.bing.net/th/id/OIP-C.nelCIKYD30NJW6W68-ZHxAHaJA?pid=ImgDet&rs=1"
},
{
"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://tse1-mm.cn.bing.net/th/id/OIP-C.nelCIKYD30NJW6W68-ZHxAHaJA?pid=ImgDet&rs=1"
},
]
dingding.send_feed_card(links)
搜集自网络