python 调用钉钉机器人接口案例一则 —— 筑梦之路

钉钉机器人 API 是阿里巴巴旗下钉钉平台提供的一种基于 HTTP 协议的 API 服务,它可以帮助开发者快速构建智能机器人,实现与用户的实时互动和自动回复。钉钉机器人 API 的主要功能如下:

  1. 消息处理:钉钉机器人 API 提供了消息处理功能,可以实现对用户发送的消息进行接收、解析和回复。开发者可以根据消息内容触发不同的动作,如发送文件、发送卡片、回复文本等。

  2. 知识库管理:钉钉机器人 API 提供了知识库管理功能,可以实现对机器人所掌握的知识进行增删改查。知识库中可以包含各种类型的信息,如问题、答案、事件等,可以方便地支持机器人的语义理解。

  3. 事件订阅:钉钉机器人 API 提供了事件订阅功能,可以实现对钉钉平台上的各种事件进行订阅。开发者可以订阅特定的事件,如用户添加好友、用户进入某个房间等,并触发相应的动作。

  4. 智能回复:钉钉机器人 API 提供了智能回复功能,可以实现根据用户发送的消息自动回复。开发者可以根据消息内容进行语义分析,并返回合适的回复。智能回复可以有效地提高机器人的智能化水平,提升用户体验。

  5. 用户管理:钉钉机器人 API 提供了用户管理功能,可以实现对用户进行增删改查。开发者可以管理用户的个人信息、好友关系、聊天记录等,并可以对用户进行相应的操作。

  6. 数据统计:钉钉机器人 API 提供了数据统计功能,可以实现对机器人的数据进行统计和分析。开发者可以查看机器人的消息处理量、用户活跃度、回复率等数据,并可以根据数据进行相应的优化。

  7. 钉钉机器人 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'&timestamp={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 = '项目名称:' + "<font color=\"#0000FF\">" + project + "</font> \n\n" \
                '项目指定端:' + "<font color=\"#0000FF\">" + end + "</font> \n\n"\
                '执行测试用例时长:' + "<font color=\"#0000FF\">" + case_time +" 秒</font>\n\n"\
                '测试用例总数:' + "<font color=\"#0000FF\">" + tq_total + "条</font>\n\n"\
                "**--------------------运行详情--------------------**\n\n"\
                '通过数:' + "<font color=\"#00FF00\">" + tq_passed + "条</font>\n\n"\
                '跳过数:' + "<font color=\"#0000FF\">" + tq_skipped + "条</font>\n\n"\
                '失败数:' + "<font color=\"#FF0000\">" + tq_failed + "条</font>\n\n"\
                '错误数:' + "<font color=\"#FF0000\"> " + tq_error + "条</font>\n\n"\
                '预期失败:' + "<font color=\"#FF0000\">" + tq_expected + " 条</font>\n\n"\
                '意外通过:' + "<font color=\"#00FF00\">" + tq_unexpected_passes + "条</font>\n\n"\
                '重新运行:' + "<font color=\"#00FF00\">" + tq_rerun + " 条</font>\n\n"\
                '报告链接:' + "<font color=\"#0000FF\">jenkins报告,请点击后进入查看 " + address + "</font>\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"
                                "<font color=\"#FF0000\">颜色</font> \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)

 搜集自网络

猜你喜欢

转载自blog.csdn.net/qq_34777982/article/details/134494943
今日推荐