终于到了实战阶段。用微信公众号实现一个简单的签到功能。
前情提要:
程序逻辑如下图
发起签到
生成"随机数.txt"文件,并将随机数返回作为签到码,将签到码返回给发起签到的用户
def gensign(): sign_number=random.randint(1000,9999) f = open(str(sign_number)+‘.txt‘,‘w‘) f.close() return str(sign_number)
签到
用户发送"签到 签到码 用户名"给公众号(各项之间用一个空格分开)。服务器接收到信息后将用户名写入签到文件中,并返回是否签到成功
def sign(sign_number,username): if(os.path.exists(sign_number+‘.txt‘)): with open(sign_number+‘.txt‘,‘a‘) as f: f.write(username+‘\n‘) return "签到成功" elif(os.path.exists(sign_number)): return "已超出签到时间" else: return "签到失败"
关闭签到
关闭签到是为了给签到设定期限,由用户选择何时关闭签到。我这里的关闭签到就是把后缀名的txt给删掉。发送"关闭 签到码"给公众号(各项之间用一个空格分开)。即可关闭签到
def closesign(sign_number): if(os.path.exists(sign_number+‘.txt‘)): os.rename(sign_number+‘.txt‘,str(sign_number))
查看签到
发送"查看 签到码"给公众号(各项之间用一个空格分开)即可查看某个签到的签到情况。这里发送的是签到码,所以只有在关闭签到后才能查看(当然,发带.txt的也能)。就是把读取某个签到文件的内容返回给查看签到的用户即可。
所有代码
from flask import Flask,request import hashlib import xmltodict import time import random import os def gensign(): sign_number=random.randint(1000,9999) f = open(str(sign_number)+‘.txt‘,‘w‘) f.close() return str(sign_number) def closesign(sign_number): if(os.path.exists(sign_number+‘.txt‘)): os.rename(sign_number+‘.txt‘,str(sign_number)) def sign(sign_number,username): if(os.path.exists(sign_number+‘.txt‘)): with open(sign_number+‘.txt‘,‘a‘) as f: f.write(username+‘\n‘) return "签到成功" elif(os.path.exists(sign_number)): return "已超出签到时间" else: return "签到失败" app = Flask(__name__) @app.route(‘/wx‘, methods=["GET", "POST"]) def getinput(): if (request.method == "GET"): # 表示是第一次接入微信服务器的验证 signature=request.args.get(‘signature‘) timestamp=request.args.get(‘timestamp‘) nonce=request.args.get(‘nonce‘) token = "maluguang" list = [token, timestamp, nonce] list.sort() sha1 = hashlib.sha1() sha1.update(list[0].encode(‘utf-8‘)) sha1.update(list[1].encode(‘utf-8‘)) sha1.update(list[2].encode(‘utf-8‘)) hashcode = sha1.hexdigest() echostr = request.args.get("echostr") if hashcode == signature: return echostr else: return "" elif request.method == "POST": # 表示微信服务器转发消息过来 xml_str = request.data if not xml_str: return"" # 对xml字符串进行解析 xml_dict = xmltodict.parse(xml_str) xml_dict = xml_dict.get("xml") # 提取消息类型 msg_type = xml_dict.get("MsgType") if msg_type == "text": # 表示发送的是文本消息 # 构造返回值,经由微信服务器回复给用户的消息内容 userinput=xml_dict.get("Content") if(userinput==‘发起签到‘): sign_number=gensign() resp_dict = { "xml": { "ToUserName": xml_dict.get("FromUserName"), "FromUserName": xml_dict.get("ToUserName"), "CreateTime": int(time.time()), "MsgType": "text", "Content": "您的签到码为" +sign_number } } # 将字典转换为xml字符串 resp_xml_str = xmltodict.unparse(resp_dict) # 返回消息数据给微信服务器 return resp_xml_str else: userinput=xml_dict.get("Content") if("关闭" in userinput): try: msglist=userinput.split(" ") sign_number=msglist[1] closesign(sign_number) resp_dict = { "xml": { "ToUserName": xml_dict.get("FromUserName"), "FromUserName": xml_dict.get("ToUserName"), "CreateTime": int(time.time()), "MsgType": "text", "Content": "成功关闭签到" +sign_number } } # 将字典转换为xml字符串 resp_xml_str = xmltodict.unparse(resp_dict) # 返回消息数据给微信服务器 return resp_xml_str except: return "success" if("签到" in userinput): try: msglist=userinput.split(‘ ‘) sign_number=msglist[1] username=msglist[2] return_msg=sign(sign_number,username) resp_dict = { "xml": { "ToUserName": xml_dict.get("FromUserName"), "FromUserName": xml_dict.get("ToUserName"), "CreateTime": int(time.time()), "MsgType": "text", "Content": username+return_msg+sign_number } } # 将字典转换为xml字符串 resp_xml_str = xmltodict.unparse(resp_dict) # 返回消息数据给微信服务器 return resp_xml_str except: return "success" if("查看" in userinput): try: msglist=userinput.split(‘ ‘) sign_number=msglist[1] if(os.path.exists(sign_number)): with open(sign_number,‘r‘) as f: data=f.read(); else: data=‘打开签到文件失败‘ resp_dict = { "xml": { "ToUserName": xml_dict.get("FromUserName"), "FromUserName": xml_dict.get("ToUserName"), "CreateTime": int(time.time()), "MsgType": "text", "Content": sign_number+‘签到情况为\n‘+data } } # 将字典转换为xml字符串 resp_xml_str = xmltodict.unparse(resp_dict) # 返回消息数据给微信服务器 return resp_xml_str except: return "success" else: resp_dict = { "xml": { "ToUserName": xml_dict.get("FromUserName"), "FromUserName": xml_dict.get("ToUserName"), "CreateTime": int(time.time()), "MsgType": "text", "Content": "Dear I Love you so much" } } resp_xml_str = xmltodict.unparse(resp_dict) # 返回消息数据给微信服务器 return resp_xml_str if __name__ == ‘__main__‘: app.run(port=‘80‘)
一些漏洞和缺陷
1.【大漏洞】没有对签到的控制验证权限,任何人只要发送正确的命令都可以操控签到,关闭签到,查看签到等。可能会有人趁此捣乱。其实应该是只有发起签到的人有权力操纵的
2.每次都会写返回的xml的同样的东西,应该可以省略然后只改不一样的部分的。后面再看看
3.代码很丑,一堆if可能效率还很低
4.异常处理也没有管那么多。可能会有意料之外的错误
5.发送的消息未经token验证,这个是懒得写,反正又不是投入生产环境的
6.生成的随机数可能会重复而产生问题
7.一个微信号可以一直签到,可以替任何人签到,没有进行微信号和签到人的绑定来确定签到的唯一性
应该还有很多问题,暂时就想到这么多了。不过就算一堆问题也不重要,重要的是这个prototype已经做出来了,后面要是用就可以在这基础上进行更改了。不过我也不是为了用它才写这个的,就是想学学微信公众号的开发,写点东西练练手。
写完了,快乐呀。哈哈