一、前言
由于每天上下班都需要打卡,公司打卡方式为OA签到,作为一个每天总是踩着点进办公室的人,打卡迟到在所难免,于是采取技术手段实现自动打卡。
入职到现在与同事打卡经历了几个阶段:
- 按键精灵模拟登录打卡。缺点:下班后公司有时停电后gg
- 使用签到登录数据包,替换cookie,定时发送签到打卡数据包。缺点:cookie时效性问题。
- 写模拟登录,获取cookie之后再发送签到登录打卡。缺点:没多久登录处数据包参数值更改了,自动化登录失败。
- 微信机器人根据识别发送内容进行打卡,实现远程打卡。缺点:微信机器人总掉线,扫码登录频繁。
- 分析手机APP,发现签到打卡包发送用户ID进行打卡,直接可以伪造任意用户身份进行打卡。缺点:出差或者调休打卡时间不可控,周一到周五都打卡,周六日不打卡。
- 当前版本,使用公众号进行打卡,不会因微信账号登录问题存在掉线无法打卡情况,并可远程控制和设置计划任务。
二、环境
- 公司内网服务器一台,win2008系统
- ngrok内网穿透脚本
- 微信公众号一个
三、过程
内网服务器搭建HTTP服务,由于微信公众号接口只支持80、443端口,公司没权限进行映射,我们需要手动把端口映射到公网,可以使用花生壳、nat123、ngrok反向代理等方式。这里我们使用ngrok进行内网穿透,网上大多都需要付费,我用的是Nutz论坛提供的服务,在服务器上下载配置文件与ngrok应用后,进入目录输入ngrok -config ngrok.yml 80
即可将服务器本机80端口映射到公网,公网地址为图中红框部分
使用Python安装web.py模块,命令为pip install web.py
新建main.py文件1
2
3
4
5
6
7
8
9
10
11
12# -*- coding: utf-8 -*-
# filename: main.py
import web
from handle import Handle
urls = (
'/wx', 'Handle',
)
if __name__ == '__main__':
app = web.application(urls, globals())
app.run()
新建Handle.py文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147# -*- coding: utf-8 -*-
# filename: handle.py
import hashlib
import web
import time
import xml.etree.ElementTree as ET
import sys
import qd
reload( sys )
sys.setdefaultencoding('utf-8')
#回复模板
class Msg(object):
def __init__(self, toUserName, fromUserName, content):
self.__dict = dict()
self.__dict['ToUserName'] = toUserName
self.__dict['FromUserName'] = fromUserName
self.__dict['CreateTime'] = int(time.time())
self.__dict['Content'] = content
def send(self):
XmlForm = """
<xml>
<ToUserName><![CDATA[{ToUserName}]]></ToUserName>
<FromUserName><![CDATA[{FromUserName}]]></FromUserName>
<CreateTime>{CreateTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[{Content}]]></Content>
</xml>
"""
return XmlForm.format(**self.__dict)
class TextMsg(Msg):
def __init__(self, toUserName, fromUserName, content):
if content=="签到":
#此处根据id查询执行签到命令
result = qd.selectid(toUserName)
content= result
elif content=="设置":
content="如要登录请按照格式回复OA用户名密码:\n登录:username:password\n如要自动签到设置请按照格式回复:\n签到设置:1,2,3,4,5\n其中数字代表签到的日期,0为周日,1为周一"
elif content[:3]=="登录:":
result = qd.login(toUserName,content[3:])
content=result
elif content[:5]=="签到设置:":
result = qd.jihuarenwu(toUserName,content[5:])
content=result
else:
pass
self.__dict = dict()
self.__dict['ToUserName'] = toUserName
self.__dict['FromUserName'] = fromUserName
self.__dict['CreateTime'] = int(time.time())
self.__dict['Content'] = content
def send(self):
XmlForm = """
<xml>
<ToUserName><![CDATA[{ToUserName}]]></ToUserName>
<FromUserName><![CDATA[{FromUserName}]]></FromUserName>
<CreateTime>{CreateTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[{Content}]]></Content>
</xml>
"""
#print XmlForm.format(**self.__dict)
return XmlForm.format(**self.__dict)
class ImageMsg(Msg):
def __init__(self, toUserName, fromUserName, mediaId):
self.__dict = dict()
self.__dict['ToUserName'] = toUserName
self.__dict['FromUserName'] = fromUserName
self.__dict['CreateTime'] = int(time.time())
self.__dict['MediaId'] = mediaId
def send(self):
XmlForm = """
<xml>
<ToUserName><![CDATA[{ToUserName}]]></ToUserName>
<FromUserName><![CDATA[{FromUserName}]]></FromUserName>
<CreateTime>{CreateTime}</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[{MediaId}]]></MediaId>
</Image>
</xml>
"""
return XmlForm.format(**self.__dict)
#接收消息分类处理
f=open('test.txt','a+')
class Handle(object):
def GET(self):
try:
data = web.input()
if len(data) == 0:
return "hello, this is handle view"
signature = data.signature
timestamp = data.timestamp
nonce = data.nonce
echostr = data.echostr
token = "tokenXXXXXX" #请按照公众平台官网\基本配置中信息填写
list = [token, timestamp, nonce]
list.sort()
sha1 = hashlib.sha1()
map(sha1.update, list)
hashcode = sha1.hexdigest()
print("handle/GET func: %s, %s: "% (hashcode, signature))
if hashcode == signature:
return echostr
else:
return ""
except Exception as Argument:
return Argument
def POST(self):
str_xml = web.data() #获得post来的数据
#f.write('\nstr_xml:'+str(str_xml))
xml = ET.fromstring(str_xml)#进行XML解析
msgType=xml.find("MsgType").text
if msgType == "text":
content=xml.find("Content").text#获得用户所输入的内容
fromUser=xml.find("FromUserName").text
toUser=xml.find("ToUserName").text
createtime=xml.find("CreateTime").text
msgid=xml.find("MsgId").text
#print TextMsg(fromUser, toUser, content).send()
return TextMsg(fromUser, toUser, content).send()
if msgType == "image":
fromUser=xml.find("FromUserName").text
toUser=xml.find("ToUserName").text
createtime=xml.find("CreateTime").text
msgid=xml.find("MsgId").text
picurl=xml.find("PicUrl").text
mediaid=xml.find("MediaId").text
return ImageMsg(fromUser, toUser, mediaid).send()
else:
fromUser=xml.find("FromUserName").text
toUser=xml.find("ToUserName").text
createtime=xml.find("CreateTime").text
msgid=xml.find("MsgId").text
return Msg(fromUser, toUser, "抱歉,我不知道该怎么回答,请等待我的主人给你答复").send()
其次,创建qd.py文件进行调用,这里就不放出来了
申请微信公众平台接口测试账号,直接用微信扫一扫就可以了,
填入自定义设置的token内容,与handle.py中token参数值一致即可通过验证。
发送信息进行测试