最近使用钉钉的Webhook机器人,不论定时与否,主动发送消息还是过于局限,想实现@机器人发送指令得到相应的答复,比如天气查询,符合需求的就是Outgoing机器人,奈何网上搜索只有PHP版本较为详细易懂,尝试写了下python的,初学还多有不足
首先申请钉钉的企业内部机器人
没有公网服务器,因此使用钉钉的内网穿透工具
cmd切换到工具目录,输入命令映射本地ip
ding -config=ding.cfg -subdomain=域名 端口
配置好后,运行服务端
# -*- 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+"×tamp="+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()
不需要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\nHello World
')
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}