python实现钉钉机器人自动发送消息

一、代码示例

注: 可与jenkins(定时触发任务)搭配,通过钉钉机器人定时自动发送消息。

# -*- coding: utf-8 -*-
"""
Created on Tue Nov  1 14:46:42 2022
ref doc: https://open.dingtalk.com/document/group/custom-robot-access

@author: betty xu
"""

import base64
import hashlib
import hmac
import json
import logging
import os
import time
import urllib
import requests
import argparse

import sys
# 要执行的顶层文件, 将 '项目目录' 添加到 sys.path 中; 底层文件从项目目录下开始导入模块;
sys_path = sys.path
cur_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), r'../'))
if cur_dir not in sys_path:
    sys_path.append(cur_dir)
sys.path = sys_path


try:
    JSONDecodeError = json.decoder.JSONDecodeError
except AttributeError:
    JSONDecodeError = ValueError


def is_not_null_and_blank_str(content):
    """
    Args:
        content (str): send msg

    Returns:
        _type_: Bool
    """
    if content and content.strip():
        return True
    else:
        return False


class DingtalkRobot(object):
    """
    dingtalk robot ref doc: https://open.dingtalk.com/document/robots/customize-robot-security-settings
    """
    def __init__(self, webhook, sign=None):
        super(DingtalkRobot, self).__init__()
        self.webhook = webhook
        self.sign = sign
        self.headers = {'Content-Type': 'application/json; charset=utf-8'}
        self.times = 0
        self.start_time = time.time()

    # 加密签名
    def __spliceUrl(self):
        timestamp = int(round(time.time() * 1000))
        secret = self.sign
        secret_enc = secret.encode('utf-8')
        string_to_sign = f'{timestamp}\n{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))
        url = f"{self.webhook}&timestamp={timestamp}&sign={sign}"
        return url

    def send_text(self, msg, is_at_all=False, at_mobiles=None):
        if at_mobiles is None:
            at_mobiles = []
        data = {"msgtype": "text", "at": {}}
        if is_not_null_and_blank_str(msg):
            data["text"] = {"content": msg}
        else:
            logging.error("text类型,消息内容不能为空!")
            raise ValueError("text类型,消息内容不能为空!")

        if is_at_all:
            data["at"]["isAtAll"] = is_at_all

        if at_mobiles:
            at_mobiles = list(map(str, at_mobiles))
            data["at"]["atMobiles"] = at_mobiles

        logging.debug(f'text类型:{data}')
        return self.__post(data)

    def send_markdown(self, msg, title=None, is_at_all=False, at_mobiles=None):
        if at_mobiles is None:
            at_mobiles = []
        data = {"msgtype": "markdown", "at": {}}
        if is_not_null_and_blank_str(msg):
            data["markdown"] = {"text": msg, "title": title}
        else:
            logging.error("text类型,消息内容不能为空!")
            raise ValueError("text类型,消息内容不能为空!")

        if is_at_all:
            data["at"]["isAtAll"] = is_at_all

        if at_mobiles:
            at_mobiles = list(map(str, at_mobiles))
            data["at"]["atMobiles"] = at_mobiles

        logging.debug(f'text类型:{data}')
        return self.__post(data)

    def __post(self, data):
        """
        发送消息(内容UTF-8编码)
        :param data: 消息数据(字典)
        :return: 返回发送结果
        """
        self.times += 1
        if self.times > 20:
            if time.time() - self.start_time < 60:
                logging.debug('钉钉官方限制每个机器人每分钟最多发送20条, 当前消息发送频率已达到限制条件, 休眠一分钟')
                time.sleep(60)
            self.start_time = time.time()

        post_data = json.dumps(data)
        try:
            response = requests.post(self.__spliceUrl(), headers=self.headers, data=post_data)
        except requests.exceptions.HTTPError as exc:
            logging.error("消息发送失败, HTTP error: %d, reason: %s" % (exc.response.status_code, exc.response.reason))
            raise
        except requests.exceptions.ConnectionError:
            logging.error("消息发送失败, HTTP connection error!")
            raise
        except requests.exceptions.Timeout:
            logging.error("消息发送失败, Timeout error!")
            raise
        except requests.exceptions.RequestException:
            logging.error("消息发送失败, Request Exception!")
            raise
        else:
            try:
                result = response.json()
            except JSONDecodeError:
                logging.error(f"服务器响应异常,状态码:{response.status_code},响应内容:{response.text}")
                return {'errcode': 500, 'errmsg': '服务器响应异常'}
            else:
                logging.debug(f'发送结果:{result}')
                if result['errcode']:
                    error_data = {"msgtype": "text", "text": {"content": f"钉钉机器人消息发送失败,原因:{result['errmsg']}"}, "at": {"isAtAll": False}}
                    logging.error(f"消息发送失败,自动通知:{error_data}")
                    requests.post(self.webhook, headers=self.headers, data=json.dumps(error_data))
                return result


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="DingTalk Robot Send Message ")
    parser.add_argument("--Robot_URL", type=str, default=Test_URL, help="")
    parser.add_argument("--Robot_SIGN", type=str, default=Test_SIGN, help="")
    args = parser.parse_args()
    ding = DingtalkRobot(args.Robot_URL, args.Robot_SIGN)
    dingTxt = "-------DingDing Robot Auto Send Message-------\n"
    ding.send_text(dingTxt, is_at_all=False)



    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值