終於到了實戰階段。用微信公眾號實現一個簡單的簽到功能。
前情提要:
程序邏輯如下圖
發起簽到
生成"隨機數.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已經做出來了,后面要是用就可以在這基礎上進行更改了。不過我也不是為了用它才寫這個的,就是想學學微信公眾號的開發,寫點東西練練手。
寫完了,快樂呀。哈哈