[Python]钉钉Outgoing机器人初步研究

基于Python的钉钉Outgoing机器人

最近使用钉钉的Webhook机器人,不论定时与否,主动发送消息还是过于局限,想实现@机器人发送指令得到相应的答复,比如天气查询,符合需求的就是Outgoing机器人,奈何网上搜索只有PHP版本较为详细易懂,尝试写了下python的,初学还多有不足

测试截图
机器人作出相应答复

首先申请钉钉的企业内部机器人

没有公网服务器,因此使用钉钉的内网穿透工具

cmd切换到工具目录,输入命令映射本地ip

ding -config=ding.cfg -subdomain=域名 端口

配置好后,运行服务端

Python 3.8.2

# -*- coding: GBK -*-
import requests
import json
import time
import hmac
import hashlib
import base64
import socket
from multiprocessing import Process

def handle_client(client_socket):
	#获取socket
	request_data = client_socket.recv(20000)
	post_userid,post_sign,post_timestamp,post_mes=getPost(request_data)
	#回应socket
	initKey(post_userid,post_sign,post_timestamp,post_mes)
	#关闭socket
	client_socket.close()
	
def getPost(request_data):
	#格式化socket数据
	request_data=str(request_data,encoding = "utf8").split('\r\n')
	items=[]
	for item in request_data[1:-2]:
		items.append(item.split(':'))
	post_useful=dict(items)
	post_mes=json.loads(request_data[-1])
	#得到钉钉请求的sign,timestamp,发送者加密id,消息内容
	post_sign=post_useful.get('sign').strip()
	post_timestamp=post_useful.get('timestamp').strip()
	post_userid=post_mes.get('senderId').strip()
	post_mes=post_mes.get('text').get('content').strip()

	return post_userid,post_sign,post_timestamp,post_mes
	
def initKey(post_userid,post_sign,post_timestamp,post_mes):
	#配置token
	whtoken="机器人的webhook token"
	#得到当前时间戳
	timestamp = str(round(time.time() * 1000))
	#计算签名
	app_secret = '机器人的AppSecret'
	app_secret_enc = app_secret.encode('utf-8')
	string_to_sign = '{}\n{}'.format(post_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')

	#验证是否来自钉钉的合法请求
	if (abs(int(post_timestamp)-int(timestamp))<3600000 and post_sign==sign):
		webhook = "https://oapi.dingtalk.com/robot/send?access_token="+whtoken+"&timestamp="+timestamp+"&sign="+sign
		header = {
			"Content-Type": "application/json",
			"Charset": "UTF-8"
		}
		#发送消息
		message_json = json.dumps(selectMes(post_userid,post_mes))
		#返回发送状态
		info = requests.post(url=webhook,data=message_json,headers=header)
		print(info.text)
	else:
		print("Warning:Not DingDing's post")

def selectMes(post_userid,post_mes):
	#判断指令选择对应回复
	if(post_mes=='1'):
		send_mes='This is 1'
		return sendText(post_userid,send_mes)
	elif (post_mes=='天气'):
		send_mes=getWeather()
		return sendMarkdown('天气预报',send_mes)
	else:
		return sendText(post_userid,'Not understand')
		
def sendText(post_userid,send_mes):
	#发送文本形式
	message={
		"msgtype": "text",
		"text": {
			"content": send_mes
		},
		"at": {
			"atDingtalkIds": [post_userid], 
		   "isAtAll": False
		}
	}
	return message

def sendMarkdown(title,send_mes):
	#发送Markdown形式
	message={
			"msgtype": "markdown",
			"markdown": {
				"title":title,
				"text": send_mes
			},
			"at": {
			"atDingtalkIds": [],
			"isAtAll": False
			}
		}
	return message
	
def getWeather():
	#爬取天气数据返回的方法,这里就不多编写了
	send_mes='#### 今日天气 \n' \
		'> 9度,西北风1级,空气良89,相对温度73%\n' \
		'> ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n' \
		'> ###### 10点20分发布 [天气](https://www.dingalk.com) \n'
	return send_mes
	
if __name__ == "__main__":
	#启动服务,端口9000
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(("", 9000))
    server_socket.listen(120)
    while True:
        client_socket, client_address = server_socket.accept()
        print("[%s, %s]用户连接上了" % client_address)
        handle_client_process = Process(target=handle_client, args=(client_socket,))
        handle_client_process.start()
        client_socket.close()

如果有公网ip

不需要apache,python的soket启动后就已经能访问了,
为了不报错加个判断,如果是ping之类的请求返回个正常响应,
webhook配置上消息接收地址:http://公网ip:端口

def handle_client(client_socket):
	request_data=client_socket.recv(20000)
	res=getPost(request_data)
	try:
		if res==0:
			client_socket.sendall(b'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>Hello World</h1>')
		else:
			initKey(res['post_userid'],res['post_sign'],res['post_timestamp'],res['post_mes'])
	except Exception as e:
		print(e)
	finally:
		client_socket.close()
def getPost(request_data):
	request_data=str(request_data,encoding="utf8").split('\r\n')
	items=[]
	for item in request_data[1:-2]:
		items.append(item.split(':'))
	post_useful={}
	for i in items:
		post_useful.update({i[0]:i[1]})
	if post_useful.get('sign')==None:
		print('other connect')
		return 0
	else:
		post_sign=post_useful.get('sign').strip()
		post_timestamp=post_useful.get('timestamp').strip()
		
		post_mes=json.loads(request_data[-1])
		post_userid=post_mes.get('senderId').strip()
		post_mes=post_mes.get('text').get('content').strip()

		return {'post_userid':post_userid,'post_sign':post_sign,'post_timestamp':post_timestamp,'post_mes':post_mes}
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值