From 9040dd3065ade4965f2b08770e0b5544f3fe7e8e Mon Sep 17 00:00:00 2001 From: pooneyy <85266337+pooneyy@users.noreply.github.com> Date: Tue, 19 Sep 2023 16:00:59 +0800 Subject: [PATCH] =?UTF-8?q?=E7=89=88=E6=9C=AC=202.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +- README.md | 60 ++++----- Utils.py | 343 ++++++++++++++++++++++++++++++++++++++++------- config.json | 4 - main.py | 58 +++++--- requirements.txt | 4 + 6 files changed, 370 insertions(+), 105 deletions(-) delete mode 100644 config.json create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index 7dc9450..083f02d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ - +__pycache__ build dist -__pycache__ +env +*.bat +*.json *.spec diff --git a/README.md b/README.md index 87ddf2e..ca3fc33 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,39 @@ -# weiban-tool +

安全微伴自动刷课助手

+

+ Visitors +

-安全微伴自动刷课助手 +相关项目:[安全微伴题库](https://github.com/pooneyy/WeibanQuestionsBank) | 安全微伴自动刷课助手 -[原项目](https://github.com/Coaixy/weiban-tool)作者已停止维护,我在原项目基础上增加多账号的支持 +### 项目介绍 -### 使用方法 +安全微伴自动刷课助手(多账号版),脱胎于[Coaixy/weiban-tool](https://github.com/Coaixy/weiban-tool),在原项目基础上增加多账号的支持,可以同时进行多个账号的学习任务。 -1. 登录[安全微伴 (mycourse.cn)](http://weiban.mycourse.cn/#/login)。 +### 使用说明 -2. 在浏览器地址栏运行 +1. 运行`main.py` 或者 [main.exe](https://github.com/pooneyy/weiban-tool/releases)。 - ```javascript - javascript:(function(){data=JSON.parse(localStorage.user);prompt('',JSON.stringify({token:data['token'],userId:data['userId'], tenantCode:data['tenantCode'], userProjectId: data['preUserProjectId'], realName: data['realName']}));})(); - ``` +2. **支持验证码识别**,验证码识别使用[TrueCaptcha](https://truecaptcha.org/),会提示你输入`userid`和`apikey`,注册的方法此处不过多赘述。 - 浏览器地址栏如果吞掉了“`javascript:`”,请手动加上。 + 需要提醒的是,这是一个付费服务,每个账号每天享有30次免费识别服务,每个账号总共享有100次免费识别服务。 - 或者你可以将上述脚本[添加到收藏夹](https://www.qiuyelin.com/getWei-banToken.html),直接在登录后的页面上运行添加进收藏夹的脚本。 + 关于资费,1美元可以识别3000次。可以使用PayPal国区支付,关于汇率,2023年9月18日,使用PayPal,$1USD=¥7.56CNY。 -3. 复制弹窗内的内容,**按照格式**添加到`config.json`。(格式不对会报错) + **值得一提的是,你可以跳过这一步骤,登录时将手动输入验证码。** - [![1662441411827.png](http://png.eot.ooo/i/2022/09/06/6316d7c7f3567.png)](http://png.eot.ooo/i/2022/09/06/6316d7c7f3567.png) +3. 按照提示录入账号密码,可同时依次输入多个账号,会记录上一个账号的学校名称,当有多个账号来自同一个学校,可以不用重复输入学校名。 -4. 以`UTF-8`的编码方式创建`config.json`文件。其内容格式如下: +4. 按`Ctrl`+`C`结束录入账号,开始登录,如果在第二步没有输入`userid`和`apikey`,会提示输入验证码。 - > `config.json`: - > - > ```json - > [ - > {"token":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","userId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","tenantCode":"00000001","userProjectId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}, - > {"token":"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy","userId":"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy","tenantCode":"00000002","userProjectId":"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"}, - > {"token":"zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz","userId":"zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz","tenantCode":"00000003","userProjectId":"zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz"}, - > {#第4个账号信息#}, - > {#第5个账号信息#}, - > ... - > {#第n个账号信息#} - > ] - > ``` - -5. 运行`main.py` 或者 [main.exe](https://github.com/pooneyy/weiban-tool/releases)。 + ![](https://telegraph-image1.pages.dev/file/e46f287b9733d3b8d21bc.png) ### 更新日志 -```text -版本 1.1 at 2022-09-06 15:08:08 - 优化:增加对多账户的支持。 - -版本 1.2 at 2022-09-07 14:02:39 - 优化:使用异步函数,提高多账户场景下任务执行效率,避免由于多个账户排队时任务流程过长,Token过期,导致后面的账户任务失败。 - 优化:使显示内容更简洁。 -``` +- 版本 1.1 at 2022-09-06 15:08:08 + - 优化:增加对多账户的支持。 +- 版本 1.2 at 2022-09-07 14:02:39 + - 优化:使用异步函数,提高多账户场景下任务执行效率,避免由于多个账户排队时任务流程过长,Token过期,导致后面的账户任务失败。 + - 优化:使显示内容更简洁。 +- 版本 2.0 at 2023-09-18 21:57:16 + - 优化:使用账号密码登录,登录相关的代码来自[Coaixy/weiban-tool/enco.py](https://github.com/Coaixy/weiban-tool/blob/bf08fe823953afa834b49fe8d7e7a1d5abf7e605/enco.py)。 diff --git a/Utils.py b/Utils.py index f655012..ca3166e 100644 --- a/Utils.py +++ b/Utils.py @@ -1,26 +1,68 @@ -import time -import requests -import json import asyncio +import datetime +import json +import os +from PIL import Image +import random +import requests +import time + +# From https://github.com/JefferyHcool/weibanbot/blob/main/enco.py +from Cryptodome.Cipher import AES +from Cryptodome.Util.Padding import pad +import base64 + +DEFAULT_SCHOOL_NAME = '' +'''这个常量的作用是暂存学校名,当同时输入的多个帐号来自同一个学校,用此避免重复地输入学校名''' class main: tenantCode = 0 userId = "" x_token = "" userProjectId = "" + realName = "" + taskName = "" + resourceNames = ['第0项'] headers = {'x-token': "", "User-agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Mobile Safari/537.36 Edg/103.0.1264.77" } - def __init__(self, code, id, token,projectId): + def __init__(self, code, id, token, realName): self.tenantCode = code self.userId = id self.x_token = token - self.userProjectId = projectId + self.realName = realName def init(self): self.headers['x-token'] = self.x_token + # 以下俩个方法来自https://github.com/Sustech-yx/WeiBanCourseMaster + + # js里的时间戳似乎都是保留了三位小数的. + def __get_timestamp(self): + return str(round(datetime.datetime.now().timestamp(), 3)) + + # Magic: 用于构造、拼接"完成学习任务"的url + # js: (jQuery-3.2.1.min.js) + # f = '3.4.1' + # expando = 'jQuery' + (f + Math.random()).replace(/\D/g, "") + def __gen_rand(self): + return ("3.4.1" + str(random.random())).replace(".", "") + + def get_Project_Info(self): + url = f'https://weiban.mycourse.cn/pharos/index/listMyProject.do?timestamp={time.time()}' + data = { + 'tenantCode': self.tenantCode, + 'userId': self.userId, + 'ended': 2 + } + response = requests.post(url, data=data, headers=self.headers) + data = json.loads(response.text)['data'] + if len(data) <= 0:self.userProjectId = '' + else: + self.userProjectId = data[0]["userProjectId"] + self.taskName = data[0]["projectName"] + def getRealName(self): url = f"https://weiban.mycourse.cn/pharos/my/getInfo.do?timestamp={int(time.time())}" data = { @@ -58,13 +100,13 @@ def getProgress(self): data = json.loads(text) return data['data']['progressPet'] - def getCategory(self): + def getCategory(self, chooseType): url = "https://weiban.mycourse.cn/pharos/usercourse/listCategory.do" data = { 'userProjectId': self.userProjectId, 'tenantCode': self.tenantCode, 'userId': self.userId, - 'chooseType': 3 + 'chooseType': chooseType } response = requests.post(url, data=data, headers=self.headers) text = response.text @@ -76,69 +118,278 @@ def getCategory(self): result.append(i['categoryCode']) return result - def getCourse(self): + def getCourse(self, chooseType): url = "https://weiban.mycourse.cn/pharos/usercourse/listCourse.do" result = [] - for i in self.getCategory(): + for i in self.getCategory(chooseType): data = { - 'userProjectId': self.userProjectId, - 'tenantCode': self.tenantCode, - 'userId': self.userId, - 'chooseType': 3, - 'name': "", - 'categoryCode': i + "userProjectId": self.userProjectId, + "tenantCode": self.tenantCode, + "userId": self.userId, + "chooseType": chooseType, + "name": "", + "categoryCode": i, } response = requests.post(url, data=data, headers=self.headers) text = response.text - data = json.loads(text)['data'] + data = json.loads(text)["data"] for i in data: - if i['finished'] == 2: - result.append(i['resourceId']) + if i["finished"] == 2: + result.append(i["resourceId"]) return result - def getFinishIdList(self): + def getFinishIdList(self, chooseType): url = "https://weiban.mycourse.cn/pharos/usercourse/listCourse.do" result = {} - for i in self.getCategory(): + for i in self.getCategory(chooseType): data = { - 'userProjectId': self.userProjectId, - 'tenantCode': self.tenantCode, - 'userId': self.userId, - 'chooseType': 3, - 'name': "", - 'categoryCode': i + "userProjectId": self.userProjectId, + "tenantCode": self.tenantCode, + "userId": self.userId, + "chooseType": chooseType, + "categoryCode": i, } response = requests.post(url, data=data, headers=self.headers) text = response.text - data = json.loads(text)['data'] + data = json.loads(text)["data"] for i in data: - if i['finished'] == 2: - result[i['resourceId']] = i['userCourseId'] + if i["finished"] == 2: + if "userCourseId" in i: + result[i["resourceId"]] = i["userCourseId"] + # print(i['resourceName']) + self.resourceNames.append(i['resourceName']) + self.tempUserCourseId = i["userCourseId"] + else: + result[i["resourceId"]] = self.tempUserCourseId return result async def start(self,courseId): data = { - 'userProjectId': self.userProjectId, - 'tenantCode': self.tenantCode, - 'userId': self.userId, - 'courseId': courseId - } - headers = { - "x-token":self.x_token + "userProjectId": self.userProjectId, + "tenantCode": self.tenantCode, + "userId": self.userId, + "courseId": courseId, } - res = requests.post("https://weiban.mycourse.cn/pharos/usercourse/study.do",data=data,headers=headers) + headers = {"x-token": self.x_token} + res = requests.post( + "https://weiban.mycourse.cn/pharos/usercourse/study.do", + data=data, + headers=headers, + ) while json.loads(res.text)['code'] == -1: await asyncio.sleep(5) - res = requests.post("https://weiban.mycourse.cn/pharos/usercourse/study.do",data=data,headers=headers) + res = requests.post( + "https://weiban.mycourse.cn/pharos/usercourse/study.do", + data=data, + headers=headers, + ) print(f"start:{courseId}\r",end='') - def finish(self,finishId): - params = { - "callback":"", - "userCourseId":finishId, - "tenantCode":self.tenantCode + def finish(self, courseId, finishId): + get_url_url = "https://weiban.mycourse.cn/pharos/usercourse/getCourseUrl.do" + finish_url = "https://weiban.mycourse.cn/pharos/usercourse/v1/{}.do" + data = { + "userProjectId": self.userProjectId, + "tenantCode": self.tenantCode, + "userId": self.userId, + "courseId": courseId, } - url = "https://weiban.mycourse.cn/pharos/usercourse/finish.do" - requests.get(url=url,params=params) - print(f"finish:{finishId}\r",end='') + raw_data = requests.post(get_url_url, data=data, headers=self.headers) + url = json.loads(raw_data.text.encode().decode("unicode-escape"))["data"] + token = url[url.find("methodToken="): url.find("&csCom")].replace( + "methodToken=", "" + ) + # print(token) + finish_url = finish_url.format(token) + ts = self.__get_timestamp().replace(".", "") + param = { + "callback": "jQuery{}_{}".format(self.__gen_rand(), ts), + "userCourseId": finishId, + "tenantCode": self.tenantCode, + "_": str(int(ts) + 1), + } + requests.get(finish_url, params=param, headers=self.headers).text + print(f"{self.realName} Finish:{courseId}") + +def fill_key(key): + key_size = 128 + filled_key = key.ljust(key_size // 8, b'\x00') + return filled_key + + +def aes_encrypt(data, key): + cipher = AES.new(key, AES.MODE_ECB) + ciphertext = cipher.encrypt(pad(data.encode('utf-8'), AES.block_size)) + base64_cipher = base64.b64encode(ciphertext).decode('utf-8') + result_cipher = base64_cipher.replace('+', '-').replace('/', '_') + return result_cipher + + +def login(payload): + init_key = 'xie2gg' + key = fill_key(init_key.encode('utf-8')) + + encrypted = aes_encrypt( + f'{{"keyNumber":"{payload["userName"]}","password":"{payload["password"]}","tenantCode":"{payload["tenantCode"]}","time":{payload["timestamp"]},"verifyCode":"{payload["verificationCode"]}"}}', + key + ) + return encrypted + +def apitruecaptcha(config, content): + image=base64.b64encode(content) + url = 'https://api.apitruecaptcha.org/one/gettext' + data = { + 'data':str(image,'utf-8'), + 'userid':config["TrueCaptcha"]["userId"], + 'apikey':config["TrueCaptcha"]["apiKey"] + } + result = requests.post(url, json.dumps(data)) + res=result.json() + try:verifycode = res['result'] + except: + if res.get('success') == False: + print(f"{res['error_type']} {res['error_message']}") + if 'Credits' in res['error_message']: + print("TrueCaptcha已达每日请求上限,无法再识别验证码。") + return None + else:verifycode = apitruecaptcha(config, content) + elif res.get('message') == 'Internal server error':verifycode = apitruecaptcha(config, content) + else:verifycode = apitruecaptcha(config, content) + return verifycode + +def get_tenant_code(school_name: str) -> str: + tenant_list = requests.get( + "https://weiban.mycourse.cn/pharos/login/getTenantListWithLetter.do" + ).text + data = json.loads(tenant_list)["data"] + for i in data: + for j in i["list"]: + if j["name"] == school_name: + return j["code"] + +def set_accounts(): + global DEFAULT_SCHOOL_NAME + with open("config.json", "r+", encoding='utf8') as file: + try:config = json.load(file) + except: + config = {} + config['TrueCaptcha'] = None + config['Accounts'] = [] + if config.get("TrueCaptcha") is None: + print('验证码识别使用 TrueCaptcha.org,如果你想手动识别验证码,请按 Ctrl + C') + try: + config["TrueCaptcha"] = {} + config["TrueCaptcha"]["userId"] = input('请输入 TrueCaptcha.org 的 userId:') + config["TrueCaptcha"]["apiKey"] = input('请输入 TrueCaptcha.org 的 apiKey:') + if config["TrueCaptcha"]["userId"] == '' or config["TrueCaptcha"]["apiKey"] == '':config["TrueCaptcha"] = None + except KeyboardInterrupt:config["TrueCaptcha"] = None + if config.get("TrueCaptcha") is None:print('\n你选择了手动识别验证码。\n') + print('输入学校名、帐号、密码,结束输入请按 Ctrl + C') + try: + if config["Accounts"]:DEFAULT_SCHOOL_NAME = config["Accounts"][-1]['schoolName'] + while True: + print(f'正在录入第 {len(config["Accounts"])+1} 个帐号') + account = {} + # 如果直接按回车,则将DEFAULT_SCHOOL_NAME的值赋给schoolName,否则将schoolName的值赋给DEFAULT_SCHOOL_NAME + account['schoolName'] = input(f'请输入学校名称(当前默认学校为 {DEFAULT_SCHOOL_NAME}):') + if account['schoolName'] == '':account['schoolName'] = DEFAULT_SCHOOL_NAME + else:DEFAULT_SCHOOL_NAME = account['schoolName'] + account['id'] = input('请输入学号:') + account['password'] = input('请输入密码:') + account['State'] = 0 + if account['id'] == '' or account['password'] == '': + print(f'\n停止输入账号,已保存 {len(config["Accounts"])} 个帐号') + break + config['Accounts'].append(account) + except KeyboardInterrupt:print(f'\n停止输入账号,已保存 {len(config["Accounts"])} 个帐号') + with open('config.json', 'w', encoding='utf8') as file: + file.write(json.dumps(config, indent=4, ensure_ascii=False)) + print('配置已保存。\n') + return config + +def get_Login_State(config : dict, account : dict) -> dict: + ''' + 传入参数 config - 配置内容 + + 传入参数 account - 一组账户信息 + ```json + { + "schoolName": "XX学校", + "id": "20230001", + "password": "12345678", + "State": 0 + } + ``` + 以字典形式 返回该账户的登录态 + + ```json + { + "token": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "userId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + "tenantCode": "00000001", + "realName": "张三" + } + ``` + ''' + school_name = account['schoolName'] + tenant_code = get_tenant_code(school_name=school_name) + user_id = account['id'] + user_pwd = account['password'] + now = time.time() + # 打开验证码 + img_data = requests.get(f"https://weiban.mycourse.cn/pharos/login/randLetterImage.do?time={now}").content + if config['TrueCaptcha'] is None: + print("验证码链接:",end='') + print(f"https://weiban.mycourse.cn/pharos/login/randLetterImage.do?time={now}") + with open("code.jpg", "wb") as file: + file.write(img_data) + file.close() + Image.open("code.jpg").show() + # 获取验证码 + verity_code = input("请输入验证码:") + os.remove("code.jpg") + else: + verity_code = apitruecaptcha(config, img_data) + # 调用js方法 + payload = { + "userName": user_id, + "password": user_pwd, + "tenantCode": tenant_code, + "timestamp": now, + "verificationCode": verity_code + } + ret = login(payload) + request_data = {"data": ret} + + response = requests.post( + "https://weiban.mycourse.cn/pharos/login/login.do", data=request_data + ).text + response = json.loads(response) + if response['code'] == '0': + tenantCode = response.get('data').get('tenantCode') + userId = response.get('data').get('userId') + x_token = response.get('data').get('token') + realName = response.get('data').get('realName') + print(f"用户 {user_id} {realName} 登录成功") + return {"token":x_token,"userId":userId,"tenantCode":tenantCode,"realName":realName,"raw_id":user_id} + elif "账号与密码不匹配" in response["msg"] or "账号已被锁定" in response["msg"] or "权限错误" in response["msg"]: + print(f'用户 {user_id} 登录失败,错误码 {response["code"]} 原因为 {response["msg"]}') + return {"is_locked":True,"raw_id":user_id} + else: + print(f'用户 {user_id} 登录失败,错误码 {response["code"]} 原因为 {response["msg"]}') + return get_Login_State(config, account) + +def save_Login_State(config): + if config.get('Accounts_login_state') is None or len(config['Accounts_login_state']) == 0: + config['Accounts_login_state'] = [] + for account in config.get("Accounts"): + if account['State'] == 1:print(f'用户 {account["id"]} 已经完成,跳过登录') + elif account['State'] == 0: + login_State = get_Login_State(config, account) + if login_State.get("is_locked") is True:account['State'] = -1 + else:config['Accounts_login_state'].append(login_State) + elif account['State'] == -1:print(f'用户 {account["id"]} 密码错误,无法登录') + with open('config.json', 'w', encoding='utf8') as file:file.write(json.dumps(config, indent=4, ensure_ascii=False)) + print('登录态已保存。\n') + else:print('已存在登录态,跳过登录。\n') \ No newline at end of file diff --git a/config.json b/config.json deleted file mode 100644 index 1e6271d..0000000 --- a/config.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - {"token":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","userId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","tenantCode":"00000001","userProjectId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}, - {"token":"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy","userId":"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy","tenantCode":"00000002","userProjectId":"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"} -] \ No newline at end of file diff --git a/main.py b/main.py index 700c698..e60ee7e 100644 --- a/main.py +++ b/main.py @@ -2,38 +2,62 @@ # @repo : https://github.com/pooneyy/weiban-tool import os, sys +import random import Utils import json import asyncio async def weibanTask(user): - # tenantCode UserId x-token userProjectId + # tenantCode id x-token userProjectId tenantCode = user.get('tenantCode') userId = user.get('userId') x_token = user.get('token') - userProjectId = user.get('userProjectId') - realName = user.get('realName',userId) + realName = user.get('realName') + id = user.get('raw_id') taskName = '未知的任务名' - main = Utils.main(tenantCode, userId, x_token, userProjectId) + main = Utils.main(tenantCode, userId, x_token, realName) main.init() try: - realName = main.getRealName() - taskName = main.getTaskName() + main.get_Project_Info() + taskName = main.taskName print(f"开始进行 {realName} 的任务:{taskName}") - finishIdList = main.getFinishIdList() - for i in main.getCourse(): - await main.start(i) - await asyncio.sleep(20) - main.finish(finishIdList[i]) - print(f"{realName} 的任务已完成") - except json.decoder.JSONDecodeError:print(f'{realName} 的账户信息错误或已经过期,请重新获取。详见:https://github.com/pooneyy/weiban-tool') + # 获取列表 + for chooseType in [2,3]: + finishIdList = main.getFinishIdList(chooseType) + index = 1 + for i in main.getCourse(chooseType): + print(f"{realName} 开始学习 {main.resourceNames[index]} {index} / {len(finishIdList)}") + await main.start(i) + await asyncio.sleep(random.randint(15,20)) + main.finish(i, finishIdList[i]) + print(f"{realName} 完成学习 {main.resourceNames[index]}") + index += 1 + print(f"{id} {realName} 的任务已完成") + with open("config.json", "r+", encoding='utf8') as file: + config = json.load(file) + for i in config['Accounts']: + if i.get('id') == id:i['State'] = 1 + for i in config['Accounts_login_state']: + if i.get('raw_id') == id:config['Accounts_login_state'].remove(i) + # seek(0), truncate()用于覆写文件 + file.seek(0) + file.truncate() + json.dump(config, file, ensure_ascii=False, indent=4) + except json.decoder.JSONDecodeError: + print(f'{realName} 的账户登录态已经过期,已删除该登录态。请重新登录。') + with open("config.json", "r+", encoding='utf8') as file: + config = json.load(file) + config['Accounts_login_state'] = [] + file.seek(0) + file.truncate() + json.dump(config, file, ensure_ascii=False, indent=4) except KeyboardInterrupt:print(f'{realName} 的任务被手动终止') async def main(): - usersConfig = {} + usersConfig = [] try: with open("config.json", "r+", encoding='utf8') as file: - try:usersConfig = json.load(file) + try:usersConfig = json.load(file).get('Accounts_login_state') except json.decoder.JSONDecodeError:print('配置文件格式错误,请仔细检查 config.json 。详见:https://github.com/pooneyy/weiban-tool') tasks=[] for user in usersConfig: @@ -43,6 +67,8 @@ async def main(): except FileNotFoundError:print('未找到 config.json!详见:https://github.com/pooneyy/weiban-tool') if __name__ =='__main__': - try:asyncio.run(main()) + try: + Utils.save_Login_State(Utils.set_accounts()) + asyncio.run(main()) except KeyboardInterrupt:print(f'\n任务被手动终止') os.system("pause") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f94ff5c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +requests~=2.28.2 +DateTime~=5.1 +Pillow~=9.5.0 +pycryptodomex