feat(maa): 森空岛签到功能上线
This commit is contained in:
@@ -426,11 +426,14 @@ class MaaUserConfig(LQConfig):
|
|||||||
self.Info_GameId_1 = ConfigItem("Info", "GameId_1", "-")
|
self.Info_GameId_1 = ConfigItem("Info", "GameId_1", "-")
|
||||||
self.Info_GameId_2 = ConfigItem("Info", "GameId_2", "-")
|
self.Info_GameId_2 = ConfigItem("Info", "GameId_2", "-")
|
||||||
self.Info_GameId_Remain = ConfigItem("Info", "GameId_Remain", "-")
|
self.Info_GameId_Remain = ConfigItem("Info", "GameId_Remain", "-")
|
||||||
|
self.Info_IfSkland = ConfigItem("Info", "IfSkland", False, BoolValidator())
|
||||||
|
self.Info_SklandToken = ConfigItem("Info", "SklandToken", "")
|
||||||
|
|
||||||
self.Data_LastProxyDate = ConfigItem("Data", "LastProxyDate", "2000-01-01")
|
self.Data_LastProxyDate = ConfigItem("Data", "LastProxyDate", "2000-01-01")
|
||||||
self.Data_LastAnnihilationDate = ConfigItem(
|
self.Data_LastAnnihilationDate = ConfigItem(
|
||||||
"Data", "LastAnnihilationDate", "2000-01-01"
|
"Data", "LastAnnihilationDate", "2000-01-01"
|
||||||
)
|
)
|
||||||
|
self.Data_LastSklandDate = ConfigItem("Data", "LastSklandDate", "2000-01-01")
|
||||||
self.Data_ProxyTimes = ConfigItem(
|
self.Data_ProxyTimes = ConfigItem(
|
||||||
"Data", "ProxyTimes", 0, RangeValidator(0, 1024)
|
"Data", "ProxyTimes", 0, RangeValidator(0, 1024)
|
||||||
)
|
)
|
||||||
@@ -572,7 +575,7 @@ class MaaPlanConfig(LQConfig):
|
|||||||
|
|
||||||
class AppConfig(GlobalConfig):
|
class AppConfig(GlobalConfig):
|
||||||
|
|
||||||
VERSION = "4.3.9.0"
|
VERSION = "4.3.10.1"
|
||||||
|
|
||||||
gameid_refreshed = Signal()
|
gameid_refreshed = Signal()
|
||||||
PASSWORD_refreshed = Signal()
|
PASSWORD_refreshed = Signal()
|
||||||
@@ -1256,6 +1259,10 @@ class AppConfig(GlobalConfig):
|
|||||||
user_config.Data_LastAnnihilationDate,
|
user_config.Data_LastAnnihilationDate,
|
||||||
info["Config"]["Data"]["LastAnnihilationDate"],
|
info["Config"]["Data"]["LastAnnihilationDate"],
|
||||||
)
|
)
|
||||||
|
user_config.set(
|
||||||
|
user_config.Data_LastSklandDate,
|
||||||
|
info["Config"]["Data"]["LastSklandDate"],
|
||||||
|
)
|
||||||
user_config.set(
|
user_config.set(
|
||||||
user_config.Data_ProxyTimes, info["Config"]["Data"]["ProxyTimes"]
|
user_config.Data_ProxyTimes, info["Config"]["Data"]["ProxyTimes"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,14 +32,18 @@ import subprocess
|
|||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
|
import requests
|
||||||
import win32com.client
|
import win32com.client
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from urllib import parse
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
from typing import Union, List, Dict
|
from typing import Union, List, Dict
|
||||||
|
|
||||||
from app.core import Config, MaaConfig, MaaUserConfig
|
from app.core import Config, MaaConfig, MaaUserConfig
|
||||||
from app.services import Notify, System
|
from app.services import Notify, Crypto, System
|
||||||
|
|
||||||
|
|
||||||
class MaaManager(QObject):
|
class MaaManager(QObject):
|
||||||
@@ -220,6 +224,34 @@ class MaaManager(QObject):
|
|||||||
user_logs_list = []
|
user_logs_list = []
|
||||||
user_start_time = datetime.now()
|
user_start_time = datetime.now()
|
||||||
|
|
||||||
|
if user_data["Info"]["IfSkland"] and user_data["Info"]["SklandToken"]:
|
||||||
|
|
||||||
|
if user_data["Data"]["LastSklandDate"] != datetime.now().strftime(
|
||||||
|
"%Y-%m-%d"
|
||||||
|
):
|
||||||
|
|
||||||
|
self.update_log_text.emit("正在执行森空岛签到中\n请稍候~")
|
||||||
|
|
||||||
|
skland_result = self.skland_sign(
|
||||||
|
Crypto.win_decryptor(user_data["Info"]["SklandToken"])
|
||||||
|
)
|
||||||
|
|
||||||
|
if skland_result["total"] > 0 and skland_result["fail"] == 0:
|
||||||
|
user_data["Data"][
|
||||||
|
"LastSklandDate"
|
||||||
|
] = datetime.now().strftime("%Y-%m-%d")
|
||||||
|
self.play_sound.emit("森空岛签到成功")
|
||||||
|
else:
|
||||||
|
self.play_sound.emit("森空岛签到失败")
|
||||||
|
|
||||||
|
elif user_data["Info"]["IfSkland"]:
|
||||||
|
logger.warning(
|
||||||
|
f"{self.name} | 用户: {user[0]} - 未配置森空岛签到Token,跳过森空岛签到"
|
||||||
|
)
|
||||||
|
self.push_info_bar.emit(
|
||||||
|
"warning", "森空岛签到失败", "未配置鹰角网络通行证登录凭证", -1
|
||||||
|
)
|
||||||
|
|
||||||
# 剿灭-日常模式循环
|
# 剿灭-日常模式循环
|
||||||
for mode in ["Annihilation", "Routine"]:
|
for mode in ["Annihilation", "Routine"]:
|
||||||
|
|
||||||
@@ -1747,6 +1779,216 @@ class MaaManager(QObject):
|
|||||||
with self.maa_tasks_path.open(mode="w", encoding="utf-8") as f:
|
with self.maa_tasks_path.open(mode="w", encoding="utf-8") as f:
|
||||||
json.dump(data, f, ensure_ascii=False, indent=4)
|
json.dump(data, f, ensure_ascii=False, indent=4)
|
||||||
|
|
||||||
|
def skland_sign(self, token) -> dict:
|
||||||
|
"""森空岛签到"""
|
||||||
|
|
||||||
|
app_code = "4ca99fa6b56cc2ba"
|
||||||
|
# 用于获取grant code
|
||||||
|
grant_code_url = "https://as.hypergryph.com/user/oauth2/v2/grant"
|
||||||
|
# 用于获取cred
|
||||||
|
cred_code_url = (
|
||||||
|
"https://zonai.skland.com/api/v1/user/auth/generate_cred_by_code"
|
||||||
|
)
|
||||||
|
# 查询角色绑定
|
||||||
|
binding_url = "https://zonai.skland.com/api/v1/game/player/binding"
|
||||||
|
# 签到接口
|
||||||
|
sign_url = "https://zonai.skland.com/api/v1/game/attendance"
|
||||||
|
|
||||||
|
# 基础请求头
|
||||||
|
header = {
|
||||||
|
"cred": "",
|
||||||
|
"User-Agent": "Skland/1.5.1 (com.hypergryph.skland; build:100501001; Android 34;) Okhttp/4.11.0",
|
||||||
|
"Accept-Encoding": "gzip",
|
||||||
|
"Connection": "close",
|
||||||
|
}
|
||||||
|
header_login = header.copy()
|
||||||
|
header_for_sign = {
|
||||||
|
"platform": "1",
|
||||||
|
"timestamp": "",
|
||||||
|
"dId": "",
|
||||||
|
"vName": "1.5.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 生成签名
|
||||||
|
def generate_signature(token_for_sign: str, path, body_or_query):
|
||||||
|
"""
|
||||||
|
生成请求签名
|
||||||
|
:param token_for_sign: 用于加密的token
|
||||||
|
:param path: 请求路径(如 /api/v1/game/player/binding)
|
||||||
|
:param body_or_query: GET用query字符串,POST用body字符串
|
||||||
|
:return: (sign, 新的header_for_sign字典)
|
||||||
|
"""
|
||||||
|
t = str(int(time.time()) - 2) # 时间戳,-2秒以防服务器时间不一致
|
||||||
|
token_bytes = token_for_sign.encode("utf-8")
|
||||||
|
header_ca = dict(header_for_sign)
|
||||||
|
header_ca["timestamp"] = t
|
||||||
|
header_ca_str = json.dumps(header_ca, separators=(",", ":"))
|
||||||
|
s = path + body_or_query + t + header_ca_str # 拼接原始字符串
|
||||||
|
# HMAC-SHA256 + MD5得到最终sign
|
||||||
|
hex_s = hmac.new(token_bytes, s.encode("utf-8"), hashlib.sha256).hexdigest()
|
||||||
|
md5 = hashlib.md5(hex_s.encode("utf-8")).hexdigest()
|
||||||
|
return md5, header_ca
|
||||||
|
|
||||||
|
# 获取带签名的header
|
||||||
|
def get_sign_header(url: str, method, body, old_header, sign_token):
|
||||||
|
"""
|
||||||
|
获取带签名的请求头
|
||||||
|
:param url: 请求完整url
|
||||||
|
:param method: 请求方式 GET/POST
|
||||||
|
:param body: POST请求体或GET时为None
|
||||||
|
:param old_header: 原始请求头
|
||||||
|
:param sign_token: 当前会话的签名token
|
||||||
|
:return: 新请求头
|
||||||
|
"""
|
||||||
|
h = json.loads(json.dumps(old_header))
|
||||||
|
p = parse.urlparse(url)
|
||||||
|
if method.lower() == "get":
|
||||||
|
sign, header_ca = generate_signature(sign_token, p.path, p.query)
|
||||||
|
else:
|
||||||
|
sign, header_ca = generate_signature(
|
||||||
|
sign_token, p.path, json.dumps(body) if body else ""
|
||||||
|
)
|
||||||
|
h["sign"] = sign
|
||||||
|
for i in header_ca:
|
||||||
|
h[i] = header_ca[i]
|
||||||
|
return h
|
||||||
|
|
||||||
|
# 复制请求头并添加cred
|
||||||
|
def copy_header(cred):
|
||||||
|
v = json.loads(json.dumps(header))
|
||||||
|
v["cred"] = cred
|
||||||
|
return v
|
||||||
|
|
||||||
|
# 使用token一步步拿到cred和sign_token
|
||||||
|
def login_by_token(token_code):
|
||||||
|
"""
|
||||||
|
:param token_code: 你的skyland token
|
||||||
|
:return: (cred, sign_token)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# token为json对象时提取data.content
|
||||||
|
t = json.loads(token_code)
|
||||||
|
token_code = t["data"]["content"]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
grant_code = get_grant_code(token_code)
|
||||||
|
return get_cred(grant_code)
|
||||||
|
|
||||||
|
# 通过grant code换cred和sign_token
|
||||||
|
def get_cred(grant):
|
||||||
|
rsp = requests.post(
|
||||||
|
cred_code_url, json={"code": grant, "kind": 1}, headers=header_login
|
||||||
|
).json()
|
||||||
|
if rsp["code"] != 0:
|
||||||
|
raise Exception(f'获得cred失败:{rsp.get("messgae")}')
|
||||||
|
sign_token = rsp["data"]["token"]
|
||||||
|
cred = rsp["data"]["cred"]
|
||||||
|
return cred, sign_token
|
||||||
|
|
||||||
|
# 通过token换grant code
|
||||||
|
def get_grant_code(token):
|
||||||
|
rsp = requests.post(
|
||||||
|
grant_code_url,
|
||||||
|
json={"appCode": app_code, "token": token, "type": 0},
|
||||||
|
headers=header_login,
|
||||||
|
).json()
|
||||||
|
if rsp["status"] != 0:
|
||||||
|
raise Exception(
|
||||||
|
f'使用token: {token} 获得认证代码失败:{rsp.get("msg")}'
|
||||||
|
)
|
||||||
|
return rsp["data"]["code"]
|
||||||
|
|
||||||
|
# 获取已绑定的角色列表
|
||||||
|
def get_binding_list(cred, sign_token):
|
||||||
|
"""
|
||||||
|
查询绑定的角色
|
||||||
|
:param cred: 当前cred
|
||||||
|
:param sign_token: 当前sign_token
|
||||||
|
:return: 角色列表
|
||||||
|
"""
|
||||||
|
v = []
|
||||||
|
rsp = requests.get(
|
||||||
|
binding_url,
|
||||||
|
headers=get_sign_header(
|
||||||
|
binding_url, "get", None, copy_header(cred), sign_token
|
||||||
|
),
|
||||||
|
).json()
|
||||||
|
if rsp["code"] != 0:
|
||||||
|
logger.error(f"{self.name} | 请求角色列表出现问题:{rsp['message']}")
|
||||||
|
if rsp.get("message") == "用户未登录":
|
||||||
|
logger.error(f"{self.name} | 用户登录可能失效了,请重新登录!")
|
||||||
|
return v
|
||||||
|
# 只取明日方舟(arknights)的绑定账号
|
||||||
|
for i in rsp["data"]["list"]:
|
||||||
|
if i.get("appCode") != "arknights":
|
||||||
|
continue
|
||||||
|
v.extend(i.get("bindingList"))
|
||||||
|
return v
|
||||||
|
|
||||||
|
# 执行签到
|
||||||
|
def do_sign(cred, sign_token):
|
||||||
|
"""
|
||||||
|
对所有绑定的角色进行签到
|
||||||
|
:param cred: 当前cred
|
||||||
|
:param sign_token: 当前sign_token
|
||||||
|
:return: (签到成功数量, 失败数量)
|
||||||
|
"""
|
||||||
|
characters = get_binding_list(cred, sign_token)
|
||||||
|
success_num = 0
|
||||||
|
fail_num = 0
|
||||||
|
for i in characters:
|
||||||
|
body = {"uid": i.get("uid"), "gameId": i.get("channelMasterId")}
|
||||||
|
rsp = requests.post(
|
||||||
|
sign_url,
|
||||||
|
headers=get_sign_header(
|
||||||
|
sign_url, "post", body, copy_header(cred), sign_token
|
||||||
|
),
|
||||||
|
json=body,
|
||||||
|
).json()
|
||||||
|
if rsp["code"] != 0:
|
||||||
|
if rsp.get("message") == "请勿重复签到!":
|
||||||
|
self.push_info_bar.emit(
|
||||||
|
"warning",
|
||||||
|
"森空岛重复签到",
|
||||||
|
f"{i.get("nickName")}({i.get("channelName")})",
|
||||||
|
-1,
|
||||||
|
)
|
||||||
|
success_num += 1
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
self.push_info_bar.emit(
|
||||||
|
"warning",
|
||||||
|
"森空岛签到失败",
|
||||||
|
f"{i.get("nickName")}({i.get("channelName")})",
|
||||||
|
-1,
|
||||||
|
)
|
||||||
|
fail_num += 1
|
||||||
|
continue
|
||||||
|
awards = rsp["data"]["awards"]
|
||||||
|
for j in awards:
|
||||||
|
res = j["resource"]
|
||||||
|
self.push_info_bar.emit(
|
||||||
|
"success",
|
||||||
|
"森空岛签到成功",
|
||||||
|
f"{i.get("nickName")}({i.get("channelName")})",
|
||||||
|
3,
|
||||||
|
)
|
||||||
|
success_num += 1
|
||||||
|
return success_num, fail_num
|
||||||
|
|
||||||
|
# 主流程
|
||||||
|
try:
|
||||||
|
# 拿到cred和sign_token
|
||||||
|
cred, sign_token = login_by_token(token)
|
||||||
|
self.sleep(1)
|
||||||
|
# 依次签到
|
||||||
|
success, fail = do_sign(cred, sign_token)
|
||||||
|
self.sleep(3)
|
||||||
|
return {"success": success, "fail": fail, "total": success + fail}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"{self.name} | 森空岛签到失败: {e}")
|
||||||
|
return {"success": 0, "fail": 0, "total": 0}
|
||||||
|
|
||||||
def push_notification(
|
def push_notification(
|
||||||
self,
|
self,
|
||||||
mode: str,
|
mode: str,
|
||||||
|
|||||||
@@ -608,6 +608,83 @@ class PushAndSwitchButtonSettingCard(SettingCard):
|
|||||||
self.switchButton.setText("开" if isChecked else "关")
|
self.switchButton.setText("开" if isChecked else "关")
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordLineAndSwitchButtonSettingCard(SettingCard):
|
||||||
|
"""Setting card with PasswordLineEdit and SwitchButton"""
|
||||||
|
|
||||||
|
textChanged = Signal()
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
icon: Union[str, QIcon, FluentIconBase],
|
||||||
|
title: str,
|
||||||
|
content: Union[str, None],
|
||||||
|
text: str,
|
||||||
|
algorithm: str,
|
||||||
|
qconfig: QConfig,
|
||||||
|
configItem_bool: ConfigItem,
|
||||||
|
configItem_info: ConfigItem,
|
||||||
|
parent=None,
|
||||||
|
):
|
||||||
|
|
||||||
|
super().__init__(icon, title, content, parent)
|
||||||
|
self.algorithm = algorithm
|
||||||
|
self.qconfig = qconfig
|
||||||
|
self.configItem_bool = configItem_bool
|
||||||
|
self.configItem_info = configItem_info
|
||||||
|
self.LineEdit = PasswordLineEdit(self)
|
||||||
|
self.LineEdit.setMinimumWidth(200)
|
||||||
|
self.LineEdit.setPlaceholderText(text)
|
||||||
|
if algorithm == "AUTO":
|
||||||
|
self.LineEdit.setViewPasswordButtonVisible(False)
|
||||||
|
self.SwitchButton = SwitchButton(self)
|
||||||
|
|
||||||
|
self.hBoxLayout.addWidget(self.LineEdit, 0, Qt.AlignRight)
|
||||||
|
self.hBoxLayout.addSpacing(16)
|
||||||
|
self.hBoxLayout.addWidget(self.SwitchButton, 0, Qt.AlignRight)
|
||||||
|
self.hBoxLayout.addSpacing(16)
|
||||||
|
|
||||||
|
self.configItem_info.valueChanged.connect(self.setInfo)
|
||||||
|
self.LineEdit.textChanged.connect(self.__textChanged)
|
||||||
|
self.configItem_bool.valueChanged.connect(self.SwitchButton.setChecked)
|
||||||
|
self.SwitchButton.checkedChanged.connect(
|
||||||
|
lambda isChecked: self.qconfig.set(self.configItem_bool, isChecked)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.setInfo(self.qconfig.get(configItem_info))
|
||||||
|
self.SwitchButton.setChecked(self.qconfig.get(configItem_bool))
|
||||||
|
|
||||||
|
def __textChanged(self, content: str):
|
||||||
|
|
||||||
|
self.configItem_info.valueChanged.disconnect(self.setInfo)
|
||||||
|
if self.algorithm == "DPAPI":
|
||||||
|
self.qconfig.set(self.configItem_info, Crypto.win_encryptor(content))
|
||||||
|
elif self.algorithm == "AUTO":
|
||||||
|
self.qconfig.set(self.configItem_info, Crypto.AUTO_encryptor(content))
|
||||||
|
self.configItem_info.valueChanged.connect(self.setInfo)
|
||||||
|
|
||||||
|
self.textChanged.emit()
|
||||||
|
|
||||||
|
def setInfo(self, content: str):
|
||||||
|
|
||||||
|
self.LineEdit.textChanged.disconnect(self.__textChanged)
|
||||||
|
if self.algorithm == "DPAPI":
|
||||||
|
self.LineEdit.setText(Crypto.win_decryptor(content))
|
||||||
|
elif self.algorithm == "AUTO":
|
||||||
|
if Crypto.check_PASSWORD(Config.PASSWORD):
|
||||||
|
self.LineEdit.setText(Crypto.AUTO_decryptor(content, Config.PASSWORD))
|
||||||
|
self.LineEdit.setPasswordVisible(True)
|
||||||
|
self.LineEdit.setReadOnly(False)
|
||||||
|
elif Config.PASSWORD:
|
||||||
|
self.LineEdit.setText("管理密钥错误")
|
||||||
|
self.LineEdit.setPasswordVisible(True)
|
||||||
|
self.LineEdit.setReadOnly(True)
|
||||||
|
else:
|
||||||
|
self.LineEdit.setText("************")
|
||||||
|
self.LineEdit.setPasswordVisible(False)
|
||||||
|
self.LineEdit.setReadOnly(True)
|
||||||
|
self.LineEdit.textChanged.connect(self.__textChanged)
|
||||||
|
|
||||||
|
|
||||||
class PushAndComboBoxSettingCard(SettingCard):
|
class PushAndComboBoxSettingCard(SettingCard):
|
||||||
"""Setting card with push & combo box"""
|
"""Setting card with push & combo box"""
|
||||||
|
|
||||||
@@ -1129,6 +1206,13 @@ class UserLableSettingCard(SettingCard):
|
|||||||
== Config.server_date().isocalendar()[:2]
|
== Config.server_date().isocalendar()[:2]
|
||||||
else "本周剿灭未完成"
|
else "本周剿灭未完成"
|
||||||
)
|
)
|
||||||
|
if self.qconfig.get(self.configItems["IfSkland"]):
|
||||||
|
text_list.append(
|
||||||
|
"森空岛已签到"
|
||||||
|
if datetime.now().strftime("%Y-%m-%d")
|
||||||
|
== self.qconfig.get(self.configItems["LastSklandDate"])
|
||||||
|
else "森空岛未签到"
|
||||||
|
)
|
||||||
|
|
||||||
self.Lable.setText(" | ".join(text_list))
|
self.Lable.setText(" | ".join(text_list))
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ from .Widget import (
|
|||||||
EditableComboBoxWithPlanSettingCard,
|
EditableComboBoxWithPlanSettingCard,
|
||||||
SpinBoxWithPlanSettingCard,
|
SpinBoxWithPlanSettingCard,
|
||||||
PasswordLineEditSettingCard,
|
PasswordLineEditSettingCard,
|
||||||
|
PasswordLineAndSwitchButtonSettingCard,
|
||||||
UserLableSettingCard,
|
UserLableSettingCard,
|
||||||
UserTaskSettingCard,
|
UserTaskSettingCard,
|
||||||
ComboBoxSettingCard,
|
ComboBoxSettingCard,
|
||||||
@@ -1585,6 +1586,18 @@ class MemberManager(QWidget):
|
|||||||
parent=self,
|
parent=self,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
self.card_Skland = PasswordLineAndSwitchButtonSettingCard(
|
||||||
|
icon=FluentIcon.CERTIFICATE,
|
||||||
|
title="森空岛签到",
|
||||||
|
content="此功能具有一定风险,请谨慎使用",
|
||||||
|
text="鹰角网络通行证登录凭证",
|
||||||
|
algorithm="DPAPI",
|
||||||
|
qconfig=self.config,
|
||||||
|
configItem_bool=self.config.Info_IfSkland,
|
||||||
|
configItem_info=self.config.Info_SklandToken,
|
||||||
|
parent=self,
|
||||||
|
)
|
||||||
|
self.card_Skland.LineEdit.setMinimumWidth(250)
|
||||||
|
|
||||||
self.card_UserLable = UserLableSettingCard(
|
self.card_UserLable = UserLableSettingCard(
|
||||||
icon=FluentIcon.INFO,
|
icon=FluentIcon.INFO,
|
||||||
@@ -1596,6 +1609,8 @@ class MemberManager(QWidget):
|
|||||||
"LastAnnihilationDate": self.config.Data_LastAnnihilationDate,
|
"LastAnnihilationDate": self.config.Data_LastAnnihilationDate,
|
||||||
"ProxyTimes": self.config.Data_ProxyTimes,
|
"ProxyTimes": self.config.Data_ProxyTimes,
|
||||||
"IfPassCheck": self.config.Data_IfPassCheck,
|
"IfPassCheck": self.config.Data_IfPassCheck,
|
||||||
|
"IfSkland": self.config.Info_IfSkland,
|
||||||
|
"LastSklandDate": self.config.Data_LastSklandDate,
|
||||||
},
|
},
|
||||||
parent=self,
|
parent=self,
|
||||||
)
|
)
|
||||||
@@ -1778,6 +1793,7 @@ class MemberManager(QWidget):
|
|||||||
Layout.addLayout(h6_layout)
|
Layout.addLayout(h6_layout)
|
||||||
Layout.addLayout(h7_layout)
|
Layout.addLayout(h7_layout)
|
||||||
Layout.addLayout(h8_layout)
|
Layout.addLayout(h8_layout)
|
||||||
|
Layout.addWidget(self.card_Skland)
|
||||||
Layout.addWidget(self.card_TaskSet)
|
Layout.addWidget(self.card_TaskSet)
|
||||||
Layout.addWidget(self.card_NotifySet)
|
Layout.addWidget(self.card_NotifySet)
|
||||||
|
|
||||||
|
|||||||
BIN
resources/sounds/noisy/森空岛签到失败.wav
Normal file
BIN
resources/sounds/noisy/森空岛签到失败.wav
Normal file
Binary file not shown.
BIN
resources/sounds/noisy/森空岛签到成功.wav
Normal file
BIN
resources/sounds/noisy/森空岛签到成功.wav
Normal file
Binary file not shown.
@@ -1,26 +1,9 @@
|
|||||||
{
|
{
|
||||||
"main_version": "4.3.9.0",
|
"main_version": "4.3.10.1",
|
||||||
"version_info": {
|
"version_info": {
|
||||||
"4.3.9.0": {
|
"4.3.10.1": {
|
||||||
"修复bug": [
|
|
||||||
"修复网络模块子线程未及时销毁导致的程序崩溃"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"4.3.9.2": {
|
|
||||||
"修复bug": [
|
|
||||||
"修复语音包禁忌二重奏"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"4.3.9.1": {
|
|
||||||
"新增功能": [
|
"新增功能": [
|
||||||
"语音功能上线"
|
"森空岛签到功能上线"
|
||||||
],
|
|
||||||
"修复bug": [
|
|
||||||
"网络模块支持并发请求",
|
|
||||||
"修复中止任务时程序异常卡顿"
|
|
||||||
],
|
|
||||||
"程序优化": [
|
|
||||||
"非UI组件转为QObject类"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user