python开发钉钉机器人

您所在的位置:网站首页 Python钉钉机器人 python开发钉钉机器人

python开发钉钉机器人

2024-07-17 19:45| 来源: 网络整理| 查看: 265

前情提要

上期分享了如何上线一个钉钉机器人。【python开发钉钉机器人|(1)上线发布、自动回复】http://t.csdnimg.cn/9lrxF

采用花生壳端口映射+python后端的方案,上线了一段时间发现明显的问题:

1、pc需要持续开机,且联网;

期间我经历了出远门关闭路由器、买了新电脑替换了旧电脑(流转给我妈了),这个方案就不切实际了。

2、空请求会导致后端报错。

请求地址填上以后,每天会有大量get请求过来,有的可能是维持连接用的,有的可能是断开连接用的,有的我也不知道是干什么用的。

这些请求如果我一一处理,就会需要写大量的正则,如果我不处理,body格式不正确、拿过来可能就会报错。

是时候改变了,框架不就是干这个用的吗。所以重构了新版本。

准备工作

1.钉钉账号、钉钉开放文档及sdk依赖(同上期)

2.云服务器(新)

3.后端代码(新)

正文 1、钉钉账号、钉钉开放文档及sdk依赖

这部分参照上次的文档即可。http://t.csdnimg.cn/9lrxF

2、云服务器 (1)服务器(本方案必买)

我选用的是腾讯云轻量应用服务器,2核2g系统盘50g带宽4m,新人优惠396元/3年。刚去瞄了一眼,现在贵了,大家酌情选购。嘚瑟一下我买那阵多便宜~~~

我自己这个机器人目前是轻量云够用了,因为文件处理在别的机器上。如果你的机器人要读写数据库,得看好容量选购。

购买完了按照官方提示一路安装,做服务器建议linux,资料经验丰富。我装的ubantu,centos也行,区别不大(我不懂)。

(2)域名(非必买)

域名就是看着美观点,云服都会带公网ip,所以非必须。

买了的话按照管理平台的指引做好解析配置。提示备案就去备案。

(3)配置nginx

下载安装nginx可以参考这个教程。【Linux中安装Nginx,很详细】http://t.csdnimg.cn/RayAf

注意事项有,登录服务器,把压缩包上传到服务器。

安装前切换到root用户,这样可以避免每次执行都要sudo、并输入密码。

​​​​​​​

如下图,第一行使用su root 切换用户。第二行password没有回显,linux都这样,摸黑输入完回车就可以,正确就会看到第三行,lighthouse@VM-16-9-ubuntu:~$变为了root@VM-16-9-ubuntu:/home/lighthouse#,root@……最后一个#,代表切换成功了。

安装好Nginx以后,找到安装路径,我的在这里/usr/local/nginx/。

运行,这是常用的命令。

# 查看nginx是否在运行 netstat -natp |grep nginx #检查配置文件是否配置正确,启动前都会查一下 nginx -t #启动 nginx #停止 killall -3 nginx

在安装路径下找到/usr/local/nginx/conf/nginx.conf,修改成如下样式。此块代表nginx监听80端口,并将其全部转发到18001端口,运行后我们将用flask监听18001端口。

# 上方省略 server { listen 80; server_name xxxx.yyyy.zzzz; # 我看别人写的是自己的域名 charset utf-8; location / { proxy_pass http://localhost:18001; # 记住这个端口号,之后配置到flask中 } # 下方省略

然后检查配置、运行。

确保80端口是开了防火墙的,默认一般是开着的。18001不需要开防火墙。

这样的好处是以后如果用云服做一些别的功能(静态网页什么的)可以在nginx做拓展配置,不需要再增加暴露在公网中的端口。

(4)其他环境依赖

如python3、flask,以及其他业务逻辑需要的三方库比如pandas。

3、后端代码

业务目录结构:

. ├── answers.py # 主要业务逻辑,分支也在此拓展 ├── config # 文件服务中校验用的,不用看 │ └── standard_form_header.xlsx ├── download_file.py # 文件服务中下载和校验用的 ├── Downloads # 下载目录,不用看 │ ├── 正确的测试20231001224226.xlsx │ └── 错误的测试20231001224221.xlsx ├── getAccessToken.py # 上期展示过,定时任务 ├── login.txt # nohup生成的日志 ├── log.txt # nohup生成的日志 ├── main.py # 后端的本体 ├── __pycache__ # 环境,不用看 ├── *******.py # 给其他服务器传递消息用的,手动打码 └── temp ├── 2101********1234.json # 来自用户消息上下文,修改任务状态,每个用户一个文件,手动打码 └── login.txt # 定时任务getAccessToken.py产生的记录 (1)后端的本体 main.py

以下代码主要功能为,接收到消息,解析header中的数据,进行校验,确认是钉钉推送的报文。

这一步也就可以过滤掉空请求。

校验通过的给钉钉回复报文,把body存起来,以便传给处理业务逻辑的模块,向用户发消息。

# -*- coding: utf-8 -*- from flask import Flask, request import json, sys from answers import msgtype_list, tips # answers是我写的业务逻辑 from typing import List from alibabacloud_dingtalk.robot_1_0.client import Client as dingtalkrobot_1_0Client from alibabacloud_tea_openapi import models as open_api_models from alibabacloud_dingtalk.robot_1_0 import models as dingtalkrobot__1__0_models from alibabacloud_tea_util import models as util_models from alibabacloud_tea_util.client import Client as UtilClient app = Flask(__name__) # 钉钉的报文都是POST方法 @app.route('/', methods = ['POST']) def robot(): print(request.headers) token = request.headers.get('token') if token == 'aaaaaaaa': # 在钉钉的报文header中,黏贴出来 req = request.get_json() print(req) # 判断消息类型 try: msg_type = req.get('msgtype') # 业务逻辑:解析用户发来的消息类型 staff_id = [req.get('senderStaffId')] # 业务逻辑:解析用户id,以便稍后给用户回消息;即便只有1个也要以列表传入 except: print('传入的json格式有误,请检查输入') if msg_type: if msg_type in msgtype_list.keys(): words = msgtype_list[msg_type](req) # 业务逻辑:根据消息内容,给对应用户发送对应的消息 Dingdingbot.send_text(sys.argv[1:], words=words, user_ids=staff_id, token=my_token) else: # 业务逻辑:若消息类型未覆盖,则回复固定信息 Dingdingbot.send_text(sys.argv[1:], words=tips, user_ids=staff_id, token=my_token) response_header = "HTTP/1.1 200 OK\r\n" response_header += "\r\n" response_body = "hello" response = response_header + response_body return response # 这部分来自钉钉官方示例代码 class Dingdingbot: def __init__(self): pass @staticmethod def create_client() -> dingtalkrobot_1_0Client: """ 使用 Token 初始化账号Client @return: Client @throws Exception """ config = open_api_models.Config() config.protocol = 'https' config.region_id = 'central' return dingtalkrobot_1_0Client(config) def send_text( args: List[str], words, user_ids,token, ) -> None: client = Dingdingbot.create_client() batch_send_otoheaders = dingtalkrobot__1__0_models.BatchSendOTOHeaders() batch_send_otoheaders.x_acs_dingtalk_access_token = token batch_send_otorequest = dingtalkrobot__1__0_models.BatchSendOTORequest( msg_param='{ "content": "%s" }' %words, msg_key='sampleText', # 回复纯文本类型,如果需要更多可以参考官方文档 robot_code='xxxxxxxx', # 每个机器人一个,找到自己的黏贴 user_ids=user_ids # 即便只有1个也要以列表传入 ) try: client.batch_send_otowith_options(batch_send_otorequest, batch_send_otoheaders, util_models.RuntimeOptions()) except Exception as err: if not UtilClient.empty(err.code) and not UtilClient.empty(err.message): print(err) pass with open('./temp/login.txt', 'r') as f: file = f.read() dict = json.loads(file) my_token = dict['token'] app.run(port=18001) # ng转发到18001端口,flask监听 (2)主要业务逻辑answers.py

解析钉钉推送的body(示例)可以得到消息类型、消息内容:

{ "conversationId":"", "chatbotCorpId":"", "chatbotUserId":"", "msgId":"", "senderNick":"王 语嫣", "isAdmin":true, "senderStaffId":"manager****", "sessionWebhookExpiredTime":1684938346289, "createAt":1684932944260, "senderCorpId":"", "conversationType":"1", "senderId":"", "sessionWebhook":"", "text":{ "content":"这是一条文本消息,内容会在这里,可以读出来" }, "robotCode":"", "msgtype":"text" }

就可以实现不同类型、不同内容的消息回复不同的内容了。

浅浅展示一下。

随机回复的消息:

不能覆盖的类型的消息:

文件类型消息:

定义某些指令:

(3)上下文处理

只靠main.py和answer.py只能实现一问一答的消息处理,如果想要拥有上下文,就需要在处理这个任务分支的时候加入任务记录,我使用的是每个用户一份json文件,通过修改'state'这个状态改变任务的完成状态。

# 修改任务状态,写入文件 message['state'] = 1 js = json.dumps(message, ensure_ascii=False) with open(file_path, 'w', encoding='utf-8') as json_file: json_file.write(js)

再次收到这名用户的请求时,读该用户的任务记录,查阅最后一次执行停留在什么状态。

# 读取任务状态 with open(file_path, 'r') as f: staff_state = f.read() message = json.loads(staff_state) if message['state'] == 1: pass else: pass

我这里状态只有简单的三四种,只需要记最近一次执行的记录,不需要把用户历史所有的信息记下来。如果有更复杂的,就需要提升抽象程度、上数据库了,资源占用也会增加。

(4)代码的运行

首先cd 项目目录下。

我主要习惯的有2种运行方式。

python3 main.py

可以在控制台里看打印信息,调试方便。

或者:

nohup python3 main.py > log.txt 2>&1 &

后台运行,输出会记入log.txt中。服务端长期运行的选它,靠谱。

# 查看后台运行的python ps aux |grep python # 关指定进程 kill -9 进程号

以上是这次分享的全部内容。从萌生这个念头,到入坑学代码,到今天已经过去了13个月零3天(去年我还连#后面是注释都不懂)。

功能上线了反而感觉心里空落落的,需要整理下学习心得和下一步的目标,再出发吧。



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3