两个概念

在刚开始的时候,钉钉机器人只支持incoming方式,也就是被动接收通知,在19年下半年钉钉的outgoing就开始内测,但是一直没放出来,大约是在今年过年的时候才正式推出来outgoing的功能,也就是今天我们要说的企业机器人。

群机器人 (incoming模式)

来自官方的描述: "群机器人是钉钉群的高级扩展功能。群机器人可以将第三方服务的信息聚合到群聊中,实现自动化的信息同步。目前,大部分机器人在添加后,还需要进行 Webhook配置,才可正常使用(配置说明详见操作流程中的帮助链接)"。

企业机器人 (outgoing模式)

来自官方的描述: "企业机器人是钉钉为用户提供的组织内部使用的机器人,为组织数字化转型业务服务。开发者可通过本文所描述步骤进行机器人的自主开发和上架,组织内其它成员可通过方便快捷地在群内添加企业机器人,并使用机器人的能力"。

使用钉钉企业机器人的先决条件

1、一个认证的钉钉组织(可以是公司的也可以是个人的)

2、一个公网IP(添加钉钉机器人的时候需要核对)

3、一个消息推送接口(POST方式的接口,需要公网能访问)

4、python

5、flask

钉钉群机器人创建

这里为了演示,我采用的是个人注册的钉钉组织,如果大家在公司的话,可以直接使用公司的,不用把时间浪费在这一步上面。

创建机器人

如果没有认证会提示你进行认证操作

如果完成了认证,那就开始创建

填写你创建机器人的相关描述,由于业务需求,可能不单单会创建一个机器人,所以要有一个清晰的描述

填写你的公网出口IP和接收消息的接口

需要注意的是,这里的接口是临时用Flask写了一个hello worldPOST接收,要不然验证不过去。记得这里的路由跟你在页面上填写的接收消息的路径确保是一致的。

# -- coding:UTF-8 --from flask import Flask, request
app = Flask(__name__)
@app.route('/call', methods=['POST'])def call(): if request.method == 'POST': return 'This is a POST request'
if __name__ == '__main__':    app.run(host='0.0.0.0', port=8888, debug=True)

创建成功之后,需要保存key和secret,后面为消息鉴权使用

最后需要确认机器人发布

创建钉钉群,添加企业机器人

需要注意事项

由于我们后端还没有任何的逻辑代码,所以我们在at群机器人的时候是没有任何响应的,那么接下来我们就来编写后端代码实现下域名过期的检测。

后端代码逻辑的检测

我们把上面验证接口的代码稍微改下

确保路由信息一致,这里我们就把用户请求的发送的信息加工返回,来看看效果。

代码如下

# -- coding:UTF-8 --from flask import Flask, request
app = Flask(__name__)
@app.route('/call', methods=['POST'])def call(): if request.method == 'POST':        content = request.get_json().get('text').get('content')        msg = { "msgtype": "text", "text": { "content": "你说的是:  {0}".format(content) }, "at": { "atMobiles": [
], "isAtAll": False } } return msg

if __name__ == '__main__':    app.run(host='0.0.0.0', port=8888, debug=True)

效果如下

手把手带你上手钉钉的outgoing_java

如何实现域名过期的检测

既然我们已经调通流程来,那么我们就来一个真正的实战吧,毕竟我们创建机器人也不是为了好玩,是为了帮我们解决我们实际的问题的,运维可能关注域名或证书的过期,那我们就来实现下这块的逻辑。

获取域名信息使用来python-whois这个包,解析域名的时候并不是百分之百都能解析,比如life后缀的域名

python-whois

python-whois安装

pip install python-whois

check domain expire 伪代码

# -- coding:UTF-8 --
import sysimport whois

def expire_result(domain):     domain_infos = whois.whois(domain)     expire_date = domain_infos.expiration_date[1] return expire_date

和我们入口程序集成来看下效果

# -- coding:UTF-8 --from flask import Flask, requestfrom check_domain_expire import expire_result
app = Flask(__name__)
@app.route('/call', methods=['POST'])def call(): if request.method == 'POST':        content = request.get_json().get('text').get('content')        domain_expire_date = expire_result(content.strip())
       msg = { "msgtype": "text", "text": { "content": u"你查询的域名是:  {0}\n域名过期时间是: {1}\n".format(content, domain_expire_date) }, "at": { "atMobiles": [
], "isAtAll": False } } return msg

if __name__ == '__main__':    app.run(host='0.0.0.0', port=8888, debug=True)

效果

站长之家查询结果

手把手带你上手钉钉的outgoing_java_02

如何检测证书的过期时间

检测ssl证书过期这里用到了pyopenssl这个库来进行解析结果。

pyopenssl

pyopenssl安装

pip install pyopenssl

check ssl expire 伪代码

# -- coding:UTF-8 --

import OpenSSLimport ssl, socket
def ssl_expire_result(domain):    cert=ssl.get_server_certificate((domain, 443))    x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) return x509.get_notAfter()

修改入口程序

# -- coding:UTF-8 --from flask import Flask, requestfrom check_ssl_expire import ssl_expire_result
app = Flask(__name__)
@app.route('/call', methods=['POST'])def call(): if request.method == 'POST':        content = request.get_json().get('text').get('content')        ssl_expire_date = ssl_expire_result(content.strip())
       msg = { "msgtype": "text", "text": { "content": u"你查询的域名是:  {0}\n域名对应证书过期时间是: {1}\n".format(content, ssl_expire_date) }, "at": { "atMobiles": [
], "isAtAll": False } } return msg

效果

站长之家查询结果

更复杂的场景

经过上面的两个实际案例,我们能看出来,钉钉企业机器人能实现什么功能取决于我们后端逻辑能提供什么样的功能,可能有的小伙伴就会说了,如果我想把你上面两个功能给合并下应该怎么办呢?那也好说,制订好规则就好了,比如我们要求输入的格式是这种的域名|xxx.com证书|xxx.com, 后端解析的时候按照关键字进行筛选执行不同的逻辑即可,当然你也可以使用数字来代表,比如1代表域名查询,2代表证书查询。那么接下来我们来实现下

定义默认返回

# 默认返回信息,当用户没有任何输入的时候        msg = { "msgtype": "text", "text": { "content": u"查询格式:  \n1、域名过期查询: 域名|xxxx.com\n2、证书过期查询: 证书|xxx.com", }, "at": { "atMobiles": [
], "isAtAll": False } }

修改之后的效果如下

工作中真正的实用场景

1、对接CMDB实现对话式信息获取(某服务对应的域名和监控列表等)

2、对接监控程序,实现对话式操作(执行日志压缩等等信息)

3、对接日志系统,实现实时的日志流信息返回

4、排班系统安排(at机器人:我这周工作安排,即可按需分配)

5、日常运维开发琐事

中间碰到的问题

1、钉钉群机器人的调试群里机器人返回的body数据并不全,无法正常获取userid, 刚开始也是折腾了好久

2、官方文档里提到的签名认证不用也没关系

官方文档

钉钉企业机器人

钉钉群机器人

信息被淹没了怎么办?

如果是在一个大群里的话,人数比较多,你at机器人提了一个问题,机器人回复你了,但是由于群内人员比较多,聊天信息过多,可能会把你想要的信息给淹没,这个时候应该咋办呢?

这个时候你需要有读取用户通讯录的权限,通过返回的body信息里的请求的userid去调用钉钉通讯录的接口来实现对应用户的电话,通过电话来进行机器人对你的at的操作,这样就能保障信息不容易被遗漏的操作了

有了通讯录的权限的话,就要防范代码泄露等方面的安全事宜,因为你代码里有keysecret, 所以万事都是相通的,安全无小事,万事须谨慎~

后记

如果大家玩过图灵机器人的话,相信对上面的一幕并不会感到陌生,其实简单的逻辑实现起来并不复杂,复杂的是如何根据用户输入实现智能回复,也就是所谓的语料库的丰富程度决定了功能的多少,我也是抛砖引玉,具体在公司场景里如何实现要看你的需求如何,我当前做的主要是针对CMDB的对接,因为老有人喜欢问,我服务对应的信息是啥,这个时候安排上这个工具对接CMDB信息,所有的都一目了然,真香哈~