很多时候,我们都希望计算机程序能够为我们自动化的处理一些学习、工作和生活上的任务,毕竟「偷懒」可是促进科技进步和社会发展的一大动力。基于这种需求,诞生了很多种自动化的工具,比如爬虫,自动地帮我们采集数据;比如智能问答机器人,自动地回答客户提出的问题。

在聊天工具大肆侵入我们生活各个方面的今天,各种消息无时无刻不在侵扰我们的每一寸时间,这种情况下,一个聊天的机器人就很有必要了。

今天,我们来学习一下使用 Python 开发一个钉钉的应答机器人,助你「人生苦短,少回消息」。

开始吧

前提

搭建钉钉应答机器人,需要先准备或拥有以下权限:

  • 钉钉企业的管理员或子管理员(如果不是企业管理员,可以自己创建一个企业,很方便的)

  • 有公网通信地址(内网穿透也可以);

钉钉群机器人开发文档:https://developers.dingtalk.com/document/app/overview-of-group-robots

创建「机器人」应用

登录「钉钉开发者后台」,选择「应用开发」——「企业内部开发」—— 「机器人」

输入好机器人的基本信息之后,就会生成创建一个「钉钉机器人」

我们的后端应用通过其提供的「AgentId」、「AppKey」、「AppSecret」就能够与钉钉机器人进行通信。

接收消息

在钉钉机器人的设定中,当用户@机器人时,钉钉会通过机器人开发者的服务器地址,用 POST 请求方法把消息内容发送出去,其 HTTP header 如下所示:

{"Content-Type": "application/json; charset=utf-8","timestamp": "1577262236757","sign":"xxxxxxxxxx"
}

其中,timestamp是消息发送时的时间戳,sign是签名值,我们需要对这两个值进行校验。

如果timestamp与系统当前时间相差1小时以上,则为非法请求。

如果sign签名值与后台计算的值不一样,也为非法请求。

其中sign签名值的计算方法为:header中的timestamp + “\n” + 机器人的appSecret当做签名字符串,使用HmacSHA256算法计算签名,然后进行Base64 encode,得到最终的签名值。

其 Python 实现代码如下所示:

import hmac
import hashlib
import base64timestamp = '1577262236757'
app_secret = 'this is a secret'
app_secret_enc = app_secret.encode('utf-8')
string_to_sign = '{}\n{}'.format(timestamp, app_secret)
string_to_sign_enc = string_to_sign.encode('utf-8')
hmac_code = hmac.new(app_secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = base64.b64encode(hmac_code).decode('utf-8')
print(sign)

其发送的消息格如下所示:

{"conversationId": "xxx","atUsers": [{"dingtalkId": "xxx","staffId":"xxx"}],"chatbotCorpId": "dinge8a565xxxx","chatbotUserId": "$:LWCP_v1:$Cxxxxx","msgId": "msg0xxxxx","senderNick": "杨xx","isAdmin": true,"senderStaffId": "user123","sessionWebhookExpiredTime": 1613635652738,"createAt": 1613630252678,"senderCorpId": "dinge8a565xxxx","conversationType": "2","senderId": "$:LWCP_v1:$Ff09GIxxxxx","conversationTitle": "机器人测试-TEST","isInAtList": true,"sessionWebhook": "https://oapi.dingtalk.com/robot/sendBySession?session=xxxxx","text": {"content": " 你好"},"msgtype": "text"
}

其中,一些参数的说明如下图所示:

我们接收到钉钉的消息后,可以根据实际的业务需求解析出相应字段的数据来进行处理。

响应消息

钉钉机器人支持我们通过「text」、「Markdown」、「整体跳转actionCard」、「独立跳转actionCard」和「feedCard」这5种消息类型发送消息到群里。

下面我们通过实际的代码来展示接收钉钉机器人的消息,以及发送 5 种消息类型到钉钉群里。

创建一个后端应用

接下来,我们通过创建一个 Django 应用来接收的处理用户发送给钉钉机器人的消息。

首先,创建一个 Django 项目和应用:

django-admin startproject DdRobot
python manage.py startapp app_robot

然后打开 “C:\DdRobot\DdRobot\settings.py” 文件,修改 ALLOWED_HOSTS 变量:

ALLOWED_HOSTS = ['*']

将 app_robot 添加到 INSTALLED_APPS 变量列表中:

INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app_robot',
]

创建校验时间戳和签名函数

因为钉钉机器人会在请求头里面传入timestamp时间戳和sign签名供我们对请求的合法性进行校验,所以为了机器人的安全,我们需要编写 2 个函数对它们进行校验(在DdRobot/app_robot/views.py文件中进行)。

首先,是时间戳的校验:

def check_timestamp(timestamp):now_timestamp = int(time.time()*1000)if now_timestamp - int(timestamp) > 3600000:return Falseelse:return True

然后是签名值的校验,签名值的计算方法和示例代码钉钉已经提供,我们借用即可:

def check_sign(timestamp,sign):import hmacimport hashlibimport base64# now_timestamp = str(int(time.time()*1000))app_secret = 'teTLGS3xZVLp6Z99mXvgVpINOUyJqFsKJ3jLb7crFdjRsJ3_77E-kxhlIbBGbNjX'app_secret_enc = app_secret.encode('utf-8')string_to_sign = '{}\n{}'.format(timestamp, app_secret)string_to_sign_enc = string_to_sign.encode('utf-8')hmac_code = hmac.new(app_secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()new_sign = base64.b64encode(hmac_code).decode('utf-8')# print(sign)# print(new_sign)if sign == new_sign:return Trueelse:return False

对于这 2 个值,校验成功我们都返回 True,校验失败我们都返回 False。

创建视图函数

接着,我们创建一个视图函数,用来接收钉钉传输过来的消息,以及响应给钉钉。

@csrf_exempt
def resp_dd(request):pass

在 resp_dd() 函数中,首先从请求头中读取钉钉传输过来的时间戳和签名值,然后进行校验:

@csrf_exempt
def resp_dd(request):timestamp = request.headers.get('timestamp','')sign = request.headers.get('sign','')# 校验时间戳if check_timestamp(timestamp) is False:return JsonResponse({'status':False,'data':'非法请求'})# 校验签名if check_sign(timestamp,sign) is False:return JsonResponse({'status':False,'data':'非法请求'})

若是时间戳和签名值校验无误,我们继续从请求 body 里面获取消息信息:

@csrf_exempt
def resp_dd(request):timestamp = request.headers.get('timestamp','')sign = request.headers.get('sign','')# 校验时间戳if check_timestamp(timestamp) is False:return JsonResponse({'status':False,'data':'非法请求'})# 校验签名if check_sign(timestamp,sign) is False:return JsonResponse({'status':False,'data':'非法请求'})body = json.loads(request.body)# 获取用户id# user_id = body['senderStaffId'] 机器人上线后才会返回user_id = body['senderId']# 获取发送的消息msg_type = body['msgtype']if msg_type == 'text':content = body['text']['content']

目前钉钉机器人只支持text文本内容的消息接收,所以在此处我们只对消息类型为text的消息进行处理。

获取到钉钉机器人发送过来的信息之后,我们就可以根据自己的业务逻辑进行处理,然后返回特定的消息类型了。

在这里,我们只对消息进行简单的处理:

  • 当发送来的消息文本为text时,机器人回复文本消息;
  • 当发送来的消息文本为markdown时,机器人回复一个 Markdown 的示例消息;
  • 当发送来的消息文本为整体跳转时,机器人回复一个「整体跳转卡片」的示例消息;
  • 当发送来的消息文本为独立跳转时,机器人回复一个「独立跳转卡片」的示例消息;
  • 当发送来的消息文本为feed时,机器人回复一个「feedCard」的示例消息;

先来定义 5 个不同消息类型的响应格式。

文本消息类型

    # 响应文字resp_text = {"at": {"atUserIds": [user_id],"isAtAll": False},"text": {"content": "你刚刚发的消息是:[{}]".format(content)},"msgtype": "text"}

Markdown消息类型:

    # 响应Markdownresp_markdown = {"msgtype": "markdown","markdown": {"title":"州的先生机器人助理","text": "## 这是什么? \n 这是一个钉钉机器人 \n ![](https://zmister.com/wp-content/uploads/2019/06/login_logo.png)"},"at": {"atUserIds": [user_id],"isAtAll": False}}

整体跳转卡片消息类型:

    # 响应整体跳转actionCardresp_actioncard = {"msgtype": "actionCard","actionCard": {"title": "州的先生 Python 实战教程合集", "text": "![](https://zmister.com/wp-content/uploads/2019/06/login_logo.png) \n #### 州的先生 Python 实战教程合集 \n\n 学习Python的一个好方法就是用实际的项目来熟练语言","singleTitle" : "阅读全文","singleURL" : "http://mrdoc.zmister.com"}}

独立跳转卡片消息类型:

    resp_actioncard_2 = {"msgtype": "actionCard","actionCard": {"title": "州的先生 Python 实战教程合集", "text": "![](https://zmister.com/wp-content/uploads/2019/06/login_logo.png) \n #### 州的先生 Python 实战教程合集 \n\n 学习Python的一个好方法就是用实际的项目来熟练语言","hideAvatar": "0", "btnOrientation": "0", "btns": [{"title": "去看看", "actionURL": "http://mrdoc.zmister.com"}, {"title": "不感兴趣", "actionURL": "https://zmister.com/"}]}}

Feed卡片消息类型:

    # 响应feedCardresp_feedcard = {"msgtype": "feedCard","feedCard": {"links": [{"title": "时代的火车向前开1", "messageURL": "http://mrdoc.zmister.com", "picURL": "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png"},{"title": "时代的火车向前开2", "messageURL": "https://zmister.com/", "picURL": "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png"}]}}

其他的消息响应空:

    # 响应空,不回复resp_empty = {"msgtype": "empty"}

定义好几个消息响应类型数据后,我们对获取到的 content 变量进行判断返回响应即可:

    if content[1:] == 'text':return JsonResponse(resp_text)elif content[1:] == 'markdown':return JsonResponse(resp_markdown)elif content[1:] == '整体跳转':return JsonResponse(resp_actioncard)elif content[1:] == '独立跳转':return JsonResponse(resp_actioncard_2)elif content[1:] == 'feed':return JsonResponse(resp_feedcard)else:return JsonResponse(resp_empty)

这样,我们这个钉钉机器人的后端处理函数就写好了。

配置路由

写好视图函数之后,我们配置一下这个函数的 URL 路由。

在 “C:\DdRobot\DdRobot\urls.py” 文件中把内容修改为如下代码所示:

from django.contrib import admin
from django.urls import path
from app_robot import viewsurlpatterns = [path('admin/', admin.site.urls),path('dd_robot/',views.resp_dd, name="resp_dd"),
]

这样 http://ip地址/dd_robot/ 就是钉钉机器人的消息接收地址。

配置钉钉机器人

回到钉钉开发者平台的网页,在钉钉机器人的「开发管理」页面,我们需要把服务器的出口IP 和钉钉机器人的消息接收地址填写好:

调试钉钉机器人

在配置好机器人的「服务器出口IP」与「消息接收地址」之后,我们点击网页菜单的「版本管理与发布」,点击「调试按钮」,进入到钉钉机器人的调试群:

这回在「钉钉机器人名称-TEST」的群里面添加创建的钉钉机器人:

我们可以在这个群里面@创建的群机器人进行测试:

在测试没问题之后,我们就可以点击「上线」按钮。钉钉机器人上线之后,就可以在钉钉群内添加这个机器人。

这样,我们就实现了从 0 到 1 使用 Python 开发钉钉群机器人。

基本的框架和流程大抵如此,具体的业务逻辑则需要根据不同的需求进行额外处理。比如:

查询天气,就得解析消息中的城市,然后请求天气接口获取天气数据,进行消息的响应;

淘宝客,就得解析消息中的文本,进行分词或其他处理,再查询数据库中的商品优惠券数据或是直接请求淘客接口获取商品优惠券数据;

员工绩效,就得接入钉钉的应用开发,借助钉钉开发的用户接口进行数据查询和响应。

还有哪些应用场景呢?欢迎留言交流!

如果需要完整的项目源码,可以添加「州的先生」的私人微信:taoist_ling,备注:钉钉机器人源码,我将发送完整的Django项目源码给你。

从 0 到 1 使用 Python 开发一个钉钉群应答机器人相关推荐

  1. 从0到1使用python开发一个半自动答题小程序

    从0到1使用python开发一个半自动答题小程序 前言 最近每天都有玩微信读书上面的每日一答的答题游戏,完全答对12题后,可以瓜分无限阅读卡.但是从小就不太爱看书的我,很难连续答对12道题,由此,产生 ...

  2. 用python实现todolist_So easy !用 Python 开发一个todolist

    原标题:So easy !用 Python 开发一个todolist 有一句话是这样说的:"凡事预则立,不预则废",说的是我们在做事之前,最好制定好你的计划,然后有序的去执行,这样 ...

  3. Python开发一个股票类库

    前言 使用Python开发一个股票项目.  项目地址:  https://github.com/pythonstock/stock  相关资料:  http://blog.csdn.net/freew ...

  4. 文案生成 python_用 Python 开发一个【视频营销号】生成器

    原标题:用 Python 开发一个[视频营销号]生成器 之前小帅b在网上看到一个营销号文案生成器,把我给乐的: 其实这个用 Python 实现非常简单,根据用户输入的内容,简单替换一下关键字就可以了, ...

  5. [python] 开发一个跟随角色移动的地图游戏demo

    用python开发一个2D角色游戏的地图Demo 如今很多大学生大学学习了编程语言,想做游戏却迟迟做不出一个游戏雏形来,接下来就和大家谈论下游戏中地图移动的简单原理并用python这一门非常火的语言进 ...

  6. python实现简单小游戏_用python开发一个有趣的猜数字小游戏(实现简单的GUI界面学习)...

    文章目录 用python开发一个有趣的猜数字小游戏 用于字符串的格式化,通过 {} 和 : 来代替以前的 % 1. python的GUI编程 1.1 pythonGUI常用库对比 Python 提供了 ...

  7. 用python开发一个银行存取款系统

    #用python开发一个银行存取款系统ka1='1001' #卡号 pa1='123456' #密码 ye1=1000 #余额 nb=0 #业务类型 qu=None #取款金额 cu=None #存款 ...

  8. 用Python开发一个自制的编程语言(虚拟机解释型)[2] 词法分析器(2)

    在上一篇文章中,我们搭好了一个编程语言解释器基本的框架,完成了一部分的词法分析器,下面让我们继续努力.用Python开发一个自制的编程语言(虚拟机解释型)[1] 词法分析器(1)_嘉定世外的JinJi ...

  9. python开发的著名软件公司_软件开发公司_软件外包_项目外包平台基于Python开发一个全文检索系统...

    基于Python开发一个全文检索系统.功能要求为: 使用全文检索引擎对文本进行检索.文本的格式为Word.PDF.TXT. 同时按数据域进行复合条件检索.数据域指文本对应的信息,例如创建人.文件编号. ...

最新文章

  1. php 安装pdo odbc,PHP PDO ODBC连接
  2. java 加载类java_深入研究Java类加载机制
  3. 培养创造性思维的20个技巧
  4. org/hibernate/validator/internal/engine
  5. JNI中访问JList的代码
  6. 使用ARM芯片的小型NAS设备的一次存储故障的检修
  7. RAKsmart高防服务器防御形式解析
  8. 面试表现不好时补救策略
  9. Hadoop Streaming 实战: 实用Partitioner类KeyFieldBasedPartitioner
  10. 2018云栖大会总结
  11. Codeforces Round #451 (Div. 2)
  12. eNSP—静态路由+NAT网络地址转换
  13. jy-13-LINUX——Linux
  14. 2.2 数据的表示和存储(二)
  15. 工作,是人生的另一道窄门
  16. 微信公众平台 - 授权接口说明
  17. 推荐实用的公众号(一)--微信文章打赏功能
  18. QNX7.1 交叉编译开源库
  19. 二维坐标系空间变换(详细解读,附MATLAB代码)
  20. 财政与金融(农)课程形成性考核册

热门文章

  1. 北师大计算机动画在线作业答案,《精》北师大网络教育计算机应用基础在线作业完整答案.docx...
  2. java部落起源,曾氏起源-mb5ff9820fd69b3的博客-51CTO博客
  3. [bzoj2560] 串珠子
  4. GridView(网格视图)
  5. Android Studio实现远程服务
  6. 人体红外感应-热释电传感器HC-SR501
  7. 【思维导图】文献综述如何可以写得清新脱俗!【整理自:中国青年报】
  8. C/C++ 程序自删除
  9. 星愿浏览器扩展程序Win8路径
  10. LeetCode 925 Long Pressed Name