Merge branch 'dev'
This commit is contained in:
2
.github/workflows/build-app.yml
vendored
2
.github/workflows/build-app.yml
vendored
@@ -111,7 +111,7 @@ jobs:
|
|||||||
NOTES_MAIN="$(sed 's/\r$//g' <(tail -n +3 version_info.txt))"
|
NOTES_MAIN="$(sed 's/\r$//g' <(tail -n +3 version_info.txt))"
|
||||||
NOTES="$NOTES_MAIN
|
NOTES="$NOTES_MAIN
|
||||||
|
|
||||||
[已有 Mirror酱 CDK ?前往 Mirror酱 高速下载](https://mirrorchyan.com/zh/projects?rid=AUTO_MAA)
|
[已有 Mirror酱 CDK ?前往 Mirror酱 高速下载](https://mirrorchyan.com/zh/projects?rid=AUTO_MAA&source=auto_maa-release)
|
||||||
|
|
||||||
\`\`\`本release通过GitHub Actions自动构建\`\`\`"
|
\`\`\`本release通过GitHub Actions自动构建\`\`\`"
|
||||||
if [ "${{ github.ref_name }}" == "main" ]; then
|
if [ "${{ github.ref_name }}" == "main" ]; then
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<a href="https://github.com/DLmaster361/AUTO_MAA/graphs/contributors"><img alt="GitHub Contributors" src="https://img.shields.io/github/contributors/DLmaster361/AUTO_MAA?style=flat-square"></a>
|
<a href="https://github.com/DLmaster361/AUTO_MAA/graphs/contributors"><img alt="GitHub Contributors" src="https://img.shields.io/github/contributors/DLmaster361/AUTO_MAA?style=flat-square"></a>
|
||||||
<a href="https://github.com/DLmaster361/AUTO_MAA/blob/main/LICENSE"><img alt="GitHub License" src="https://img.shields.io/github/license/DLmaster361/AUTO_MAA?style=flat-square"></a>
|
<a href="https://github.com/DLmaster361/AUTO_MAA/blob/main/LICENSE"><img alt="GitHub License" src="https://img.shields.io/github/license/DLmaster361/AUTO_MAA?style=flat-square"></a>
|
||||||
<a href="https://deepwiki.com/DLmaster361/AUTO_MAA"><img alt="DeepWiki" src="https://deepwiki.com/badge.svg"></a>
|
<a href="https://deepwiki.com/DLmaster361/AUTO_MAA"><img alt="DeepWiki" src="https://deepwiki.com/badge.svg"></a>
|
||||||
<a href="https://mirrorchyan.com/zh/projects?rid=AUTO_MAA"><img alt="mirrorc" src="https://img.shields.io/badge/Mirror%E9%85%B1-%239af3f6?logo=countingworkspro&logoColor=4f46e5"></a>
|
<a href="https://mirrorchyan.com/zh/projects?rid=AUTO_MAA&source=auto_maa-readme"><img alt="mirrorc" src="https://img.shields.io/badge/Mirror%E9%85%B1-%239af3f6?logo=countingworkspro&logoColor=4f46e5"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 软件介绍
|
## 软件介绍
|
||||||
|
|||||||
@@ -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.2"
|
||||||
|
|
||||||
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"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ from PySide6.QtCore import QObject, Signal, QEventLoop, QFileSystemWatcher, QTim
|
|||||||
import json
|
import json
|
||||||
import subprocess
|
import subprocess
|
||||||
import shutil
|
import shutil
|
||||||
import time
|
|
||||||
import re
|
import re
|
||||||
import win32com.client
|
import win32com.client
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@@ -39,7 +38,7 @@ 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, skland_sign_in
|
||||||
|
|
||||||
|
|
||||||
class MaaManager(QObject):
|
class MaaManager(QObject):
|
||||||
@@ -220,6 +219,51 @@ 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 = skland_sign_in(
|
||||||
|
Crypto.win_decryptor(user_data["Info"]["SklandToken"])
|
||||||
|
)
|
||||||
|
|
||||||
|
for type, user_list in skland_result.items():
|
||||||
|
|
||||||
|
if type != "总计" and len(user_list) > 0:
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"{self.name} | 用户: {user[0]} - 森空岛签到{type}: {'、'.join(user_list)}"
|
||||||
|
)
|
||||||
|
self.push_info_bar.emit(
|
||||||
|
"info",
|
||||||
|
f"森空岛签到{type}",
|
||||||
|
"、".join(user_list),
|
||||||
|
-1,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
skland_result["总计"] > 0
|
||||||
|
and len(skland_result["失败"]) == 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"]:
|
||||||
|
|
||||||
@@ -910,6 +954,9 @@ class MaaManager(QObject):
|
|||||||
|
|
||||||
self.sleep(self.wait_time)
|
self.sleep(self.wait_time)
|
||||||
|
|
||||||
|
if self.isInterruptionRequested:
|
||||||
|
return None
|
||||||
|
|
||||||
# 移除静默进程标记
|
# 移除静默进程标记
|
||||||
Config.silence_list.remove(self.emulator_path)
|
Config.silence_list.remove(self.emulator_path)
|
||||||
|
|
||||||
@@ -979,6 +1026,7 @@ class MaaManager(QObject):
|
|||||||
else:
|
else:
|
||||||
logger.info(f"{self.name} | 无法连接到ADB地址:{ADB_address}")
|
logger.info(f"{self.name} | 无法连接到ADB地址:{ADB_address}")
|
||||||
|
|
||||||
|
if not self.isInterruptionRequested:
|
||||||
self.play_sound.emit("ADB失败")
|
self.play_sound.emit("ADB失败")
|
||||||
|
|
||||||
def refresh_maa_log(self) -> None:
|
def refresh_maa_log(self) -> None:
|
||||||
|
|||||||
@@ -32,5 +32,6 @@ __license__ = "GPL-3.0 license"
|
|||||||
from .notification import Notify
|
from .notification import Notify
|
||||||
from .security import Crypto
|
from .security import Crypto
|
||||||
from .system import System
|
from .system import System
|
||||||
|
from .skland import skland_sign_in
|
||||||
|
|
||||||
__all__ = ["Notify", "Crypto", "System"]
|
__all__ = ["Notify", "Crypto", "System", "skland_sign_in"]
|
||||||
|
|||||||
239
app/services/skland.py
Normal file
239
app/services/skland.py
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
|
||||||
|
# Copyright © 2024-2025 DLmaster361
|
||||||
|
|
||||||
|
# This file incorporates work covered by the following copyright and
|
||||||
|
# permission notice:
|
||||||
|
#
|
||||||
|
# skland-checkin-ghaction Copyright © 2023 Yanstory
|
||||||
|
# https://github.com/Yanstory/skland-checkin-ghaction
|
||||||
|
|
||||||
|
# This file is part of AUTO_MAA.
|
||||||
|
|
||||||
|
# AUTO_MAA is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation, either version 3 of the License,
|
||||||
|
# or (at your option) any later version.
|
||||||
|
|
||||||
|
# AUTO_MAA is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||||
|
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||||
|
# the GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Contact: DLmaster_361@163.com
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
AUTO_MAA
|
||||||
|
AUTO_MAA森空岛服务
|
||||||
|
v4.3
|
||||||
|
作者:DLmaster_361、ClozyA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
import requests
|
||||||
|
from urllib import parse
|
||||||
|
|
||||||
|
|
||||||
|
def skland_sign_in(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"森空岛服务 | 请求角色列表出现问题:{rsp['message']}")
|
||||||
|
if rsp.get("message") == "用户未登录":
|
||||||
|
logger.error(f"森空岛服务 | 用户登录可能失效了,请重新登录!")
|
||||||
|
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) -> dict:
|
||||||
|
"""
|
||||||
|
对所有绑定的角色进行签到
|
||||||
|
:param cred: 当前cred
|
||||||
|
:param sign_token: 当前sign_token
|
||||||
|
:return: 签到结果字典
|
||||||
|
"""
|
||||||
|
|
||||||
|
characters = get_binding_list(cred, sign_token)
|
||||||
|
result = {"成功": [], "重复": [], "失败": [], "总计": len(characters)}
|
||||||
|
|
||||||
|
for character in characters:
|
||||||
|
|
||||||
|
body = {
|
||||||
|
"uid": character.get("uid"),
|
||||||
|
"gameId": character.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:
|
||||||
|
|
||||||
|
result[
|
||||||
|
"重复" if rsp.get("message") == "请勿重复签到!" else "失败"
|
||||||
|
].append(
|
||||||
|
f"{character.get("nickName")}({character.get("channelName")})"
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
result["成功"].append(
|
||||||
|
f"{character.get("nickName")}({character.get("channelName")})"
|
||||||
|
)
|
||||||
|
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
# 主流程
|
||||||
|
try:
|
||||||
|
# 拿到cred和sign_token
|
||||||
|
cred, sign_token = login_by_token(token)
|
||||||
|
time.sleep(1)
|
||||||
|
# 依次签到
|
||||||
|
return do_sign(cred, sign_token)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"森空岛服务 | 森空岛签到失败: {e}")
|
||||||
|
return {"成功": [], "重复": [], "失败": [], "总计": 0}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
|
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
|
||||||
# Copyright © 2024-2025 DLmaster361
|
# Copyright © 2024-2025 DLmaster361
|
||||||
|
|
||||||
|
# This file incorporates work covered by the following copyright and
|
||||||
|
# permission notice:
|
||||||
|
#
|
||||||
|
# ZenlessZoneZero-OneDragon Copyright © 2024-2025 DoctorReid
|
||||||
|
# https://github.com/DoctorReid/ZenlessZoneZero-OneDragon
|
||||||
|
|
||||||
# This file is part of AUTO_MAA.
|
# This file is part of AUTO_MAA.
|
||||||
|
|
||||||
# AUTO_MAA is free software: you can redistribute it and/or modify
|
# AUTO_MAA is free software: you can redistribute it and/or modify
|
||||||
@@ -608,6 +614,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 +1212,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))
|
||||||
|
|
||||||
|
|||||||
@@ -405,4 +405,6 @@ class ButtonGroup(SimpleCardWidget):
|
|||||||
|
|
||||||
def open_sales(self):
|
def open_sales(self):
|
||||||
"""打开 MirrorChyan 链接"""
|
"""打开 MirrorChyan 链接"""
|
||||||
QDesktopServices.openUrl(QUrl("https://mirrorchyan.com/"))
|
QDesktopServices.openUrl(
|
||||||
|
QUrl("https://mirrorchyan.com/zh/get-start?source=auto_maa-home")
|
||||||
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -1093,7 +1093,9 @@ class UpdaterSettingCard(HeaderCardWidget):
|
|||||||
parent=self,
|
parent=self,
|
||||||
)
|
)
|
||||||
mirrorchyan_url = HyperlinkButton(
|
mirrorchyan_url = HyperlinkButton(
|
||||||
"https://mirrorchyan.com/", "获取Mirror酱CDK", self
|
"https://mirrorchyan.com/zh/get-start?source=auto_maa-setting_card",
|
||||||
|
"获取Mirror酱CDK",
|
||||||
|
self,
|
||||||
)
|
)
|
||||||
self.card_MirrorChyanCDK.hBoxLayout.insertWidget(
|
self.card_MirrorChyanCDK.hBoxLayout.insertWidget(
|
||||||
5, mirrorchyan_url, 0, Qt.AlignRight
|
5, mirrorchyan_url, 0, Qt.AlignRight
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
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,17 @@
|
|||||||
{
|
{
|
||||||
"main_version": "4.3.9.0",
|
"main_version": "4.3.10.2",
|
||||||
"version_info": {
|
"version_info": {
|
||||||
"4.3.9.0": {
|
"4.3.10.2": {
|
||||||
"修复bug": [
|
|
||||||
"修复网络模块子线程未及时销毁导致的程序崩溃"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"4.3.9.2": {
|
|
||||||
"修复bug": [
|
|
||||||
"修复语音包禁忌二重奏"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"4.3.9.1": {
|
|
||||||
"新增功能": [
|
"新增功能": [
|
||||||
"语音功能上线"
|
"公招喜报模板优化"
|
||||||
],
|
|
||||||
"修复bug": [
|
|
||||||
"网络模块支持并发请求",
|
|
||||||
"修复中止任务时程序异常卡顿"
|
|
||||||
],
|
],
|
||||||
"程序优化": [
|
"程序优化": [
|
||||||
"非UI组件转为QObject类"
|
"Mirror 酱链接添加`source`字段,用于标识来源"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"4.3.10.1": {
|
||||||
|
"新增功能": [
|
||||||
|
"森空岛签到功能上线"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user