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
+
安全微伴自动刷课助手
+
+
+
-安全微伴自动刷课助手
+相关项目:[安全微伴题库](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`。(格式不对会报错)
+ **值得一提的是,你可以跳过这一步骤,登录时将手动输入验证码。**
- [](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)。
+ 
### 更新日志
-```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 @@ class main:
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 @@ class main:
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
+ "userProjectId": self.userProjectId,
+ "tenantCode": self.tenantCode,
+ "userId": self.userId,
+ "courseId": courseId,
}
- headers = {
- "x-token":self.x_token
- }
- 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