1 importtime2 importhmac3 importhashlib4 importbase645 importurllib6 importjson7 importrequests8 importlogging9
10 try:11 JSONDecodeError =json.decoder.JSONDecodeError12 exceptAttributeError:13 JSONDecodeError =ValueError14
15
16 defis_not_null_and_blank_str(content):17 if content andcontent.strip():18 returnTrue19 else:20 returnFalse21
22
23 classDingtalkRobot(object):24 def __init__(self, webhook, sign=None):25 super(DingtalkRobot, self).__init__()26 self.webhook =webhook27 self.sign =sign28 self.headers = {'Content-Type': 'application/json; charset=utf-8'}29 self.times =030 self.start_time =time.time()31
32 #加密签名
33 def __spliceUrl(self):34 timestamp = int(round(time.time() * 1000))35 secret =self.sign36 secret_enc = secret.encode('utf-8')37 string_to_sign = '{}\n{}'.format(timestamp, secret)38 string_to_sign_enc = string_to_sign.encode('utf-8')39 hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()40 sign =urllib.parse.quote_plus(base64.b64encode(hmac_code))41 url = f"{self.webhook}×tamp={timestamp}&sign={sign}"
42 returnurl43
44 def send_text(self, msg, is_at_all=False, at_mobiles=[]):45 data = {"msgtype": "text", "at": {}}46 ifis_not_null_and_blank_str(msg):47 data["text"] = {"content": msg}48 else:49 logging.error("text类型,消息内容不能为空!")50 raise ValueError("text类型,消息内容不能为空!")51
52 ifis_at_all:53 data["at"]["isAtAll"] =is_at_all54
55 ifat_mobiles:56 at_mobiles =list(map(str, at_mobiles))57 data["at"]["atMobiles"] =at_mobiles58
59 logging.debug('text类型:%s' %data)60 return self.__post(data)61
62 def __post(self, data):63 """
64 发送消息(内容UTF-8编码)65 :param data: 消息数据(字典)66 :return: 返回发送结果67 """
68 self.times += 1
69 if self.times > 20:70 if time.time() - self.start_time < 60:71 logging.debug('钉钉官方限制每个机器人每分钟最多发送20条,当前消息发送频率已达到限制条件,休眠一分钟')72 time.sleep(60)73 self.start_time =time.time()74
75 post_data =json.dumps(data)76 try:77 response = requests.post(self.__spliceUrl(), headers=self.headers, data=post_data)78 exceptrequests.exceptions.HTTPError as exc:79 logging.error("消息发送失败, HTTP error: %d, reason: %s" %(exc.response.status_code, exc.response.reason))80 raise
81 exceptrequests.exceptions.ConnectionError:82 logging.error("消息发送失败,HTTP connection error!")83 raise
84 exceptrequests.exceptions.Timeout:85 logging.error("消息发送失败,Timeout error!")86 raise
87 exceptrequests.exceptions.RequestException:88 logging.error("消息发送失败, Request Exception!")89 raise
90 else:91 try:92 result =response.json()93 exceptJSONDecodeError:94 logging.error("服务器响应异常,状态码:%s,响应内容:%s" %(response.status_code, response.text))95 return {'errcode': 500, 'errmsg': '服务器响应异常'}96 else:97 logging.debug('发送结果:%s' %result)98 if result['errcode']:99 error_data = {"msgtype": "text", "text": {"content": "钉钉机器人消息发送失败,原因:%s" % result['errmsg']},100 "at": {"isAtAll": True}}101 logging.error("消息发送失败,自动通知:%s" %error_data)102 requests.post(self.webhook, headers=self.headers, data=json.dumps(error_data))103 returnresult104
105
106 if __name__ == '__main__':107 URL = "你的钉钉机器人地址"
108 SIGN = "签名"
109 ding =DingtalkRobot(URL, SIGN)110 print(ding.send_text("Hello World"))