From 202ddc8a82793ceff59c7ec4280dbe60df65df11 Mon Sep 17 00:00:00 2001 From: ChenXuzheng <1092889706@qq.com> Date: Sun, 8 May 2022 03:34:18 +0000 Subject: [PATCH] add main.py --- main.py | 382 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..9e03e8d --- /dev/null +++ b/main.py @@ -0,0 +1,382 @@ +import requests +import re +import json +import datetime +import time +import os +import random +import schedule +import sys +import ddddocr + +# 签到程序模块 + + +class LoginError(Exception): + """Login Exception""" + pass + + +def get_day(delta=0): + """ + 获得指定格式的日期 + """ + today = datetime.date.today() + oneday = datetime.timedelta(days=delta) + yesterday = today - oneday + return yesterday.strftime("%Y%m%d") + + +def take_out_json(content): + """ + 从字符串jsonp中提取json数据 + """ + s = re.search("^jsonp_\d+_\((.*?)\);?$", content) + return json.loads(s.group(1) if s else "{}") + + +def get_date(): + """Get current date""" + today = datetime.date.today() + return "%4d%02d%02d" % (today.year, today.month, today.day) + + +def get_photo(res): + try: + with open('./verify.png', 'wb') as f: + f.write(res) + return True + + except Exception as e: + print(e) + return False + + +def parse(): + ocr = ddddocr.DdddOcr(show_ad=False, old=True) + with open('./verify.png', 'rb') as f: + image = f.read() + res = ocr.classification(image) + print('验证码识别结果:', res) + return res + + +def get_and_parse(res): + if get_photo(res): + return parse() + else: + return False + + +class ZJULogin(object): + """ + Attributes: + username: (str) 浙大统一认证平台用户名(一般为学号) + password: (str) 浙大统一认证平台密码 + sess: (requests.Session) 统一的session管理 + """ + headers = { + 'user-agent': 'Mozilla/5.0 (Linux; U; Android 11; zh-CN; M2012K11AC Build/RKQ1.200826.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 UWS/3.22.0.36 Mobile Safari/537.36 AliApp(DingTalk/6.0.7.1) com.alibaba.android.rimet.zju/14785964 Channel/1543545060864 language/zh-CN 2ndType/exclusive UT4Aplus/0.2.25 colorScheme/light', + } + + def __init__(self, username, password, delay_run=False): + self.username = username + self.password = password + self.delay_run = delay_run + self.sess = requests.Session() + self.imgaddress = 'https://healthreport.zju.edu.cn/ncov/wap/default/code' + self.BASE_URL = "https://healthreport.zju.edu.cn/ncov/wap/default/index" + self.LOGIN_URL = "https://zjuam.zju.edu.cn/cas/login?service=http%3A%2F%2Fservice.zju.edu.cn%2F" + + self.TG_TOKEN = os.getenv("TG_TOKEN") # TG机器人的TOKEN + self.CHAT_ID = os.getenv("CHAT_ID") # 推送消息的CHAT_ID + self.DD_BOT_TOKEN = os.getenv("DD_BOT_TOKEN") + self.DD_BOT_SECRET = os.getenv("DD_BOT_SECRET") # 哈希算法验证(可选) + self.reminders = os.getenv("REMINDERS") + + self.lng = "120.123138" + self.lat = "30.267931" + + def login(self): + """Login to ZJU platform""" + res = self.sess.get(self.LOGIN_URL) + execution = re.search( + 'name="execution" value="(.*?)"', res.text).group(1) + res = self.sess.get( + url='https://zjuam.zju.edu.cn/cas/v2/getPubKey').json() + n, e = res['modulus'], res['exponent'] + encrypt_password = self._rsa_encrypt(self.password, e, n) + + data = { + 'username': self.username, + 'password': encrypt_password, + 'execution': execution, + '_eventId': 'submit', + "authcode": "" + } + res = self.sess.post(url=self.LOGIN_URL, data=data) + # check if login successfully + if '用户名或密码错误' in res.content.decode(): + raise LoginError('登录失败,请核实账号密码重新登录') + print("统一认证平台登录成功~") + return self.sess + + def _rsa_encrypt(self, password_str, e_str, M_str): + password_bytes = bytes(password_str, 'ascii') + password_int = int.from_bytes(password_bytes, 'big') + e_int = int(e_str, 16) + M_int = int(M_str, 16) + result_int = pow(password_int, e_int, M_int) + return hex(result_int)[2:].rjust(128, '0') + + +class HealthCheckInHelper(ZJULogin): + headers = { + 'user-agent': 'Mozilla/5.0 (Linux; U; Android 11; zh-CN; M2012K11AC Build/RKQ1.200826.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 UWS/3.22.0.36 Mobile Safari/537.36 AliApp(DingTalk/6.0.7.1) com.alibaba.android.rimet.zju/14785964 Channel/1543545060864 language/zh-CN 2ndType/exclusive UT4Aplus/0.2.25 colorScheme/light', + } + + REDIRECT_URL = "https://zjuam.zju.edu.cn/cas/login?service=https%3A%2F%2Fhealthreport.zju.edu.cn%2Fa_zju%2Fapi%2Fsso%2Findex%3Fredirect%3Dhttps%253A%252F%252Fhealthreport.zju.edu.cn%252Fncov%252Fwap%252Fdefault%252Findex%26from%3Dwap" + + def get_ip_location(self): + headers = { + 'authority': 'webapi.amap.com', + 'pragma': 'no-cache', + 'cache-control': 'no-cache', + 'sec-ch-ua': '"Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"', + 'sec-ch-ua-mobile': '?0', + 'user-agent': 'Mozilla/5.0 (Linux; U; Android 11; zh-CN; M2012K11AC Build/RKQ1.200826.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 UWS/3.22.0.36 Mobile Safari/537.36 AliApp(DingTalk/6.0.7.1) com.alibaba.android.rimet.zju/14785964 Channel/1543545060864 language/zh-CN 2ndType/exclusive UT4Aplus/0.2.25 colorScheme/light', + 'accept': '*/*', + 'sec-fetch-site': 'cross-site', + 'sec-fetch-mode': 'no-cors', + 'sec-fetch-dest': 'script', + 'referer': 'https://healthreport.zju.edu.cn/', + 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8', + 'cookie': 'isg=BIaGbUMSG7BxFM4x941hm4D913wI58qhRFwZi3CvdKmEcyaN2nUJsfYKT6-_W8K5', + } + + params = ( + ('key', '729923f88542d91590470f613adb27b5'), + ('callback', 'jsonp_859544_'), + ('platform', 'JS'), + ('logversion', '2.0'), + ('appname', 'https://healthreport.zju.edu.cn/ncov/wap/default/index'), + ('csid', '17F714D6-756D-49E4-96F2-B31F04B14A5A'), + ('sdkversion', '1.4.16'), + ) + response = self.sess.get( + 'https://webapi.amap.com/maps/ipLocation?key=729923f88542d91590470f613adb27b5&callback=jsonp_859544_&platform=JS&logversion=2.0&appname=https%3A%2F%2Fhealthreport.zju.edu.cn%2Fncov%2Fwap%2Fdefault%2Findex&csid=17F714D6-756D-49E4-96F2-B31F04B14A5A&sdkversion=1.4.16', + headers=headers, params=params) + return take_out_json(response.text) + + def get_geo_info(self, location: dict): + params = ( + ('key', '729923f88542d91590470f613adb27b5'), + ('s', 'rsv3'), + ('language', 'zh_cn'), + ('location', '{lng},{lat}'.format( + lng=location.get("lng"), lat=location.get("lat"))), + ('extensions', 'base'), + ('callback', 'jsonp_607701_'), + ('platform', 'JS'), + ('logversion', '2.0'), + ('appname', 'https://healthreport.zju.edu.cn/ncov/wap/default/index'), + ('csid', '63157A4E-D820-44E1-B032-A77418183A4C'), + ('sdkversion', '1.4.16'), + ) + + response = self.sess.get( + 'https://restapi.amap.com/v3/geocode/regeo', headers=self.headers, params=params, ) + return take_out_json(response.text) + + def take_in(self, geo_info: dict): + formatted_address = geo_info.get("regeocode").get("formatted_address") + address_component = geo_info.get("regeocode").get("addressComponent") + if not formatted_address or not address_component: + return + + # 获得id和uid参数 + time.sleep(3) + res = self.sess.get(self.BASE_URL, headers=self.headers) + if len(res.content) == 0: + print('网页获取失败,请检查网络并重试') + html = res.content.decode() + try: + done = re.findall( + '温馨提示: 不外出、不聚集、不吃野味, 戴口罩、勤洗手、咳嗽有礼,开窗通风,发热就诊', html)[0] + print(done) + try: + res = self.sess.get(self.imgaddress, headers=self.headers) + code = get_and_parse(res.content) + if not code: + return + except: + print('验证码识别失败') + except: + print('打卡网页获取失败') + finally: + new_info_tmp = json.loads(re.findall(r'def = ({[^\n]+})', html)[0]) + new_id = new_info_tmp['id'] + new_uid = new_info_tmp['uid'] + # 拼凑geo信息 + lng, lat = address_component.get( + "streetNumber").get("location").split(",") + geo_api_info_dict = {"type": "complete", "info": "SUCCESS", "status": 1, + "position": {"Q": lat, "R": lng, "lng": lng, "lat": lat}, + "message": "Get geolocation success.Convert Success.Get address success.", "location_type": "ip", + "accuracy": "null", "isConverted": "true", "addressComponent": address_component, + "formattedAddress": formatted_address, "roads": [], "crosses": [], "pois": []} + #print('打卡地点:', formatted_address) + # 拿到校验值 + verify_data = re.findall( + r'"([a-z0-9]*?)": "([0-9]*?)","([a-z0-9]*?)":"([a-z0-9]*?)"', html)[0] + verify_code = { + verify_data[0]: verify_data[1], + verify_data[2]: verify_data[3], + } + data = { + 'sfymqjczrj': '0', + 'zjdfgj': '', + 'sfyrjjh': '0', + 'cfgj': '', + 'tjgj': '', + 'nrjrq': '0', + 'rjka': '', + 'jnmddsheng': '', + 'jnmddshi': '', + 'jnmddqu': '', + 'jnmddxiangxi': '', + 'rjjtfs': '', + 'rjjtfs1': '', + 'rjjtgjbc': '', + 'jnjtfs': '', + 'jnjtfs1': '', + 'jnjtgjbc': '', + # 是否确认信息属实 + 'sfqrxxss': '1', + 'sfqtyyqjwdg': '0', + 'sffrqjwdg': '0', + 'sfhsjc': '', + 'zgfx14rfh': '0', + 'zgfx14rfhdd': '', + 'sfyxjzxgym': '1', + # 是否不宜接种人群 + 'sfbyjzrq': '5', + 'jzxgymqk': '6', # 这里是第三针相关参数,1是已接种第一针,4是已接种第二针(已满6个月),5是已接种第二针(未满6个月),6是已接种第三针,3是未接种,记得自己改 + 'tw': '0', + 'sfcxtz': '0', + 'sfjcbh': '0', + 'sfcxzysx': '0', + 'jcjg': '', + 'qksm': '', + 'sfyyjc': '0', + 'jcjgqr': '0', + 'remark': '', + 'address': formatted_address, + # {"type":"complete","position":{"Q":30.30975640191,"R":120.085647515191,"lng":120.085648,"lat":30.309756},"location_type":"html5","message":"Get geolocation success.Convert Success.Get address success.","accuracy":40,"isConverted":true,"status":1,"addressComponent":{"citycode":"0571","adcode":"330106","businessAreas":[],"neighborhoodType":"","neighborhood":"","building":"","buildingType":"","street":"龙宇街","streetNumber":"17-18号","country":"中国","province":"浙江省","city":"杭州市","district":"西湖区","towncode":"330106109000","township":"三墩镇"},"formattedAddress":"浙江省杭州市西湖区三墩镇翠柏浙江大学(紫金港校区)","roads":[],"crosses":[],"pois":[],"info":"SUCCESS"} + 'geo_api_info': geo_api_info_dict, + # 浙江省 杭州市 西湖区 + # '\u6D59\u6C5F\u7701 \u676D\u5DDE\u5E02 \u897F\u6E56\u533A' + 'area': "{} {} {}".format(address_component.get("province"), address_component.get("city"), + address_component.get("district")), + # 浙江省 + # '\u6D59\u6C5F\u7701' + 'province': address_component.get("province"), + # 杭州市 + # '\u676D\u5DDE\u5E02' + 'city': address_component.get("city"), + # 是否在校:在校将'sfzx'改为1 + 'sfzx': '1', + 'sfjcwhry': '0', + 'sfjchbry': '0', + 'sfcyglq': '0', + 'gllx': '', + 'glksrq': '', + 'jcbhlx': '', + 'jcbhrq': '', + 'bztcyy': '4', + 'sftjhb': '0', + 'sftjwh': '0', + 'fjsj': '0', + 'sfjcqz': '', + 'jcqzrq': '', + 'jrsfqzys': '', + 'jrsfqzfy': '', + 'sfyqjzgc': '', + # 是否申领杭州健康码 + 'sfsqhzjkk': '1', + # 杭州健康吗颜色,1:绿色 2:红色 3:黄色 + 'sqhzjkkys': '1', + 'gwszgzcs': '', + 'szgj': '', + 'fxyy': '', + 'jcjg': '', + # uid每个用户不一致 + 'uid': new_uid, + # id每个用户不一致 + 'id': new_id, + # 日期 + 'date': get_date(), + 'created': round(time.time()), + 'szsqsfybl': '0', + 'sfygtjzzfj': '0', + 'gtjzzfjsj': '', + 'gwszdd': '', + 'szgjcs': '', + 'ismoved': '0', # 位置变化为1,不变为0 + 'zgfx14rfhsj': '', + 'jrdqjcqk': '', + 'jcwhryfs': '', + 'jchbryfs': '', + 'xjzd': '', + 'sfsfbh': '0', + 'jhfjrq': '', + 'jhfjjtgj': '', + 'jhfjhbcc': '', + 'jhfjsftjwh': '0', + 'jhfjsftjhb': '0', + 'szsqsfybl': '0', + 'gwszgz': '', + 'campus': '紫金港校区', # 紫金港校区 玉泉校区 西溪校区 华家池校区 之江校区 海宁校区 舟山校区 宁波校区 工程师学院 杭州国际科创中心 其他 + # 👇-----2022.5.7日修改-----👇 + 'verifyCode': code, + # 👆-----2022.5.7日修改-----👆 + } + data.update(verify_code) + response = self.sess.post('https://healthreport.zju.edu.cn/ncov/wap/default/save', data=data, + headers=self.headers) + return response.json() + + def run(self): + print("正在为{}健康打卡".format(self.username)) + if self.delay_run: + # 确保定时脚本执行时间不太一致 + time.sleep(random.randint(10, 100)) + try: + self.login() + # 拿取eai-sess的cookies信息 + self.sess.get(self.REDIRECT_URL) + # location = get_ip_location() + # print(location) + location = {'info': 'LOCATE_SUCCESS', + 'status': 1, 'lng': self.lng, 'lat': self.lat} + geo_info = self.get_geo_info(location) + # print(geo_info) + res = self.take_in(geo_info) + print(res) + except requests.exceptions.ConnectionError: + # reraise as KubeException, but log stacktrace. + print("打卡失败,请检查github服务器网络状态") + + +if __name__ == '__main__': + # 因为是github action版本,所以不加上循环多人打卡功能 + account = os.getenv("account") + password = os.getenv("password") + s = HealthCheckInHelper(account, password, delay_run=True) + schedule.every().day.at("06:04").do(s.run) + while True: + try: + schedule.run_pending() + time.sleep(30) + except Exception: + print("ERROR") \ No newline at end of file