From b33ab1f04252652ee28edb649120fd32295a2ef8 Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sun, 24 Dec 2023 21:06:26 +0800 Subject: [PATCH] add zyys --- zyys.py | 397 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 397 insertions(+) create mode 100644 zyys.py diff --git a/zyys.py b/zyys.py new file mode 100644 index 0000000..2315cc6 --- /dev/null +++ b/zyys.py @@ -0,0 +1,397 @@ +from argparse import ArgumentParser +from getpass import getpass +from hashlib import sha1 as _sha1 +from json import dumps as _dumps, load +from math import ceil +from random import random +from urllib.parse import urlencode +from requests import Session + + +def stringify(json): + return _dumps(json, ensure_ascii=False, separators=(",", ":")) + + +def sha1(data: str): + f = _sha1() + f.update(data.encode()) + return f.hexdigest() + + +class Client(): + MYSHOP_FORAPP_KEY = "987654321" + SOFTWARE = "0" + LOGINDEVICETYPE = "android" + FROMANDROID = "1" + VERSION = "131" + DEVICEID = "设备Id获取失败,null值!版本\u003d131" + + def __init__(self): + self._ses = Session() + self._ses.headers.update({ + "User-Agent": "okhttp/3.6.0", + "Accept-Encoding": "gzip", + }) + self._baseUrl = "http://39.104.60.22:6023/" + self._token = "" + self._fromteacher = False + self._portalUrl = None + + def check_in(self, longitude: float, latitude: float, localname, note): + if self._portalUrl is None: + raise Exception("请先选择医院") + if not self._token: + raise Exception("请先登录") + url = self._portalUrl + "doctor_train/rest/ningboschedule/signstudent.do" # noqa: E501 + url += f"?{self.get_base_params()}" + d = stringify({ + "logindevicetype": self.LOGINDEVICETYPE, + "longitude": f"{longitude:.06f}", + "latitude": f"{latitude:.06f}", + "fromandroid": self.FROMANDROID, + "note": note, + "fromteacher": "1" if self._fromteacher else "0", + "localname": localname, + }) + re = self._ses.post(url, data={"data": d}) + if re.status_code != 200: + raise Exception(f"HTTP {re.status_code} {re.reason}") + return self.check_error2(re.json()) + + def check_error(self, r): + if r["code"] != "1" or "data" not in r or r["data"] is None: + raise Exception(f"Error: {r['msg']}") + return r["data"] + + def check_error2(self, r): + if r["code"] != "1": + raise Exception(f"Error: {r['msg']}") + return r + + def get_base_params(self): + d = { + "token": self._token, + "myshop_app_key": self.MYSHOP_FORAPP_KEY, + "software": self.SOFTWARE, + } + return urlencode(d) + + def get_ningbo_schedule_init_data(self): + if self._portalUrl is None: + raise Exception("请先选择医院") + if not self._token: + raise Exception("请先登录") + url = self._portalUrl + "doctor_train/rest/ningboschedule/getInitData.do" # noqa: E501 + url += f"?{self.get_base_params()}" + d = stringify({ + "logindevicetype": self.LOGINDEVICETYPE, + "fromteacher": "1" if self._fromteacher else "0", + "fromandroid": self.FROMANDROID, + }) + re = self._ses.post(url, data={"data": d}) + if re.status_code != 200: + raise Exception(f"HTTP {re.status_code} {re.reason}") + return self.check_error2(re.json()) + + def login_check(self, loginid: str, password: str): + if self._portalUrl is None: + raise Exception("请先选择医院") + url = self._portalUrl + "doctor_portal/rest/loginCheck.do" + url += f"?{self.get_base_params()}" + d = stringify({ + "logindevicetype": self.LOGINDEVICETYPE, + "password": sha1(password), + "fromandroid": self.FROMANDROID, + "devicetype": self.LOGINDEVICETYPE, + "deviceid": self.DEVICEID, + "fromteacher": "1" if self._fromteacher else "0", + "loginid": loginid, + "mac": self.DEVICEID, + }) + re = self._ses.post(url, data={"data": d}) + if re.status_code != 200: + raise Exception(f"HTTP {re.status_code} {re.reason}") + return self.check_error2(re.json()) + + def init_with_hospital(self, hospital): + self._portalUrl = f'http://{hospital["portalurl"]}/' + + def init_with_user(self, user): + self._token = user["token"] + + def query_train_hospital(self): + url = self._baseUrl + "cloud_doctor_train/rest/trainHospital/query.do" + url += f"?{self.get_base_params()}" + d = stringify({ + "logindevicetype": self.LOGINDEVICETYPE, + "fromteacher": "1" if self._fromteacher else "0", + "fromandroid": self.FROMANDROID, + }) + re = self._ses.post(url, data={"data": d}) + if re.status_code != 200: + raise Exception(f"HTTP {re.status_code} {re.reason}") + return self.check_error(re.json()) + + +class Main(): + def __init__(self, config, arg): + self._config = config + self._arg = arg + self._client = Client() + + def ask_choice(self, choices: list, prompt="请选择:", fn=None, extra=None): + count = len(choices) + total_pages = ceil(count / self._arg.page_size) + page = 1 + + def show_page(): + nonlocal page + base = (page - 1) * self._arg.page_size + if total_pages > 1: + print(f"第{page}/{total_pages}页") + for i in range(self._arg.page_size): + index = base + i + if index >= count: + break + s = fn(choices[index]) if fn else choices[index] + print(f"{i}. {s}") + if page > 1: + print("f. 第一页") + print("p. 上一页") + if page < total_pages: + print("n. 下一页") + print("l. 最后一页") + if extra is not None: + for t in extra: + print(f"{t[0]}. {t[1]}") + + while True: + show_page() + s = input(prompt) + if s == "f": + page = 1 + elif s == "p": + page = max(1, page - 1) + elif s == "n": + page = min(total_pages, page + 1) + elif s == "l": + page = total_pages + else: + if extra is not None: + for t in extra: + if t[0] == s: + return t[2] + try: + index = int(s) + except Exception: + continue + base = (page - 1) * self._arg.page_size + index += base + if index < 0 or index >= count: + continue + return choices[index] + + def checkIn(self): + self.init_client() + if "location" not in self._config: + self._config["location"] = [] + location = self.ask_choice(self._config["location"], "请选择位置:", lambda x: f'{x["name"]} ({x["longitude"]},{x["latitude"]},{x["localname"]})', [("a", "添加位置", "add")]) # noqa: E501 + if location == "add": + name = input("请输入位置名称(仅用作标识,不会上传到服务器):") + longitude = float(input("请输入经度:")) + latitude = float(input("请输入纬度:")) + localname = input("请输入地名(会在签到时上传到服务器):") + location = { + "name": name, + "longitude": longitude, + "latitude": latitude, + "localname": localname, + "longitude_radius": 0.00001, + "latitude_radius": 0.00001, + } + while True: + self.show_location(location) + s = input("是否需要进行调整?(y/n)") + if s == "n": + break + elif s == "y": + t = { + "name": "位置名称", + "longitude": "经度", + "latitude": "纬度", + "localname": "地名", + "longitude_radius": "随机化经度半径", + "latitude_radius": "随机化纬度半径", + } + c = self.ask_choice(["name", "longitude", "latitude", + "localname", "longitude_radius", + "latitude_radius"], "请选择要修改的内容:", + lambda x: t[x]) + if c == "name": + location["name"] = input("请输入位置名称:") + elif c == "longitude": + location["longitude"] = float(input("请输入经度:")) + elif c == "latitude": + location["latitude"] = float(input("请输入纬度:")) + elif c == "localname": + location["localname"] = input("请输入地名:") + elif c == "longitude_radius": + location["longitude_radius"] = float( + input("请输入随机化经度半径:")) + elif c == "latitude_radius": + location["latitude_radius"] = float( + input("请输入随机化纬度半径:")) + else: + continue + self._config["location"].append(location) + print("获取签到信息...") + data = self._client.get_ningbo_schedule_init_data() + print("已签到列表:") + for x in data["signlist"]: + print(x) + prompt = f"要进行{data['buttonname']}吗?(y/n)" + while True: + s = input(prompt) + if s == "y": + break + elif s == "n": + return + localname = location["localname"] + longitude = round(location["longitude"] + (random() - 0.5) * location["longitude_radius"], 6) # noqa: E501 + latitude = round(location["latitude"] + (random() - 0.5) * location["latitude_radius"], 6) # noqa: E501 + note = "" + if self._arg.note: + note = self._arg.note + while True: + print("经度:", longitude) + print("纬度:", latitude) + print("地名:", localname) + print("备注:", note) + s = input("是否需要进行调整?(y/n)") + if s == "n": + break + elif s == "y": + t = { + "longitude": "经度", + "latitude": "纬度", + "localname": "地名", + "note": "备注", + } + c = self.ask_choice(["longitude", "latitude", "localname", + "note"], "请选择要修改的内容:", lambda x: t[x]) + if c == "longitude": + longitude = float(input("请输入经度:")) + elif c == "latitude": + latitude = float(input("请输入纬度:")) + elif c == "localname": + localname = input("请输入地名:") + elif c == "note": + note = input("请输入备注:") + print("签到中...") + self._client.check_in(longitude, latitude, localname, note) + print("签到成功") + + def get_hospital(self): + print("获取医院列表...") + data = self._client.query_train_hospital() + if self._arg.hospital_id: + for x in data: + if x["trainhospitalid"] == self._arg.hospital_id: + return x + raise Exception(f"医院ID {self._arg.hospital_id} 不存在") + if self._arg.hospital_name: + for x in data: + if x["name"] == self._arg.hospital_name: + return x + raise Exception(f"医院名称 {self._arg.hospital_name} 不存在") + if self._arg.hospital_code: + for x in data: + if x["hospitalcode"] == self._arg.hospital_code: + return x + raise Exception(f"医院编码 {self._arg.hospital_code} 不存在") + return self.ask_choice(data, "请选择医院:", lambda x: x["name"]) + + def init_client(self): + if "hospital" not in self._config or "user" not in self._config: + self.login() + else: + self._client.init_with_hospital(self._config["hospital"]) + self._client.init_with_user(self._config["user"]) + + def login(self): + if "hospital" not in self._config or self._arg.change_hospital: + self._config["hospital"] = self.get_hospital() + self._client.init_with_hospital(self._config["hospital"]) + if self._arg.login_id is None: + self._arg.login_id = input("请输入登录ID:") + if self._arg.password is None: + if self._arg.no_getpass: + self._arg.password = input("请输入密码:") + else: + self._arg.password = getpass("请输入密码:") + print("登录中...") + self._config["user"] = self._client.login_check( + self._arg.login_id, self._arg.password) + self._client.init_with_user(self._config["user"]) + print("登录成功") + + def show_location(self, loc): + print("位置名称:", loc["name"]) + print("经度:", loc["longitude"]) + print("纬度:", loc["latitude"]) + print("地名:", loc["localname"]) + print("随机化经度半径:", loc["longitude_radius"]) + print("随机化纬度半径:", loc["latitude_radius"]) + + +ACTIONS = ["login", "checkin"] +EACTIONS = ["login2"] + + +def get_action_str(action): + if action == "login": + return "登录" + elif action == "login2": + return "登录(重新选择医院)" + elif action == "checkin": + return "签到" + + +def main(args=None): + p = ArgumentParser() + p.add_argument("-c", "--config", default="zyys.json", help="配置文件") + p.add_argument("-l", "--login-id", help="登录ID") + p.add_argument("-p", "--password", help="密码") + p.add_argument("--hospital-id", help="医院ID", type=int) + p.add_argument("--hospital-name", help="医院名称") + p.add_argument("--hospital-code", help="医院编码") + p.add_argument("-P", "--page-size", default=10, help="每页数量", type=int) + p.add_argument("-C", "--change-hospital", action="store_true", + help="重新选择医院") + p.add_argument("--no-getpass", action="store_true", help="不使用getpass") + p.add_argument("-n", "--note", help="备注") + p.add_argument("action", choices=ACTIONS, help="操作", nargs="?") + arg = p.parse_args(args) + try: + with open(arg.config) as f: + config = load(f) + except Exception: + config = {} + m = Main(config, arg) + if arg.action is None: + arg.action = m.ask_choice(ACTIONS + EACTIONS, "请选择操作:", get_action_str) + try: + if arg.action == "login": + m.login() + elif arg.action == "login2": + arg.change_hospital = True + m.login() + elif arg.action == "checkin": + m.checkIn() + finally: + with open(arg.config, "w") as f: + f.write(stringify(config)) + + +if __name__ == "__main__": + main()