Compare commits

..

12 Commits
v3.1 ... v3.1.2

Author SHA1 Message Date
DLmaster
44ee917d88 同步到MAA新版本的配置方法 2024-08-03 13:11:54 +08:00
DLmaster
669019c051 优化超时判定 2024-07-28 10:42:55 +08:00
DLmaster
4685c12570 Update python-app.yml
自动构建
2024-07-27 22:25:28 +08:00
DLmaster
d815529510 Update python-app.yml
自动构建
2024-07-27 22:03:02 +08:00
DLmaster
1da3620feb Update python-app.yml
自动构建
2024-07-27 22:00:23 +08:00
DLmaster
44d529c60f Update python-app.yml
自动构建
2024-07-27 21:53:49 +08:00
DLmaster
cdbcebd945 Update python-app.yml
自动构建
2024-07-27 21:51:37 +08:00
DLmaster
5bc2bf9397 Update python-app.yml
自动构建
2024-07-27 21:48:27 +08:00
DLmaster
d41419a579 Update python-app.yml
自动构建
2024-07-27 21:40:34 +08:00
DLmaster
a7e15e509e Create python-app.yml
自动构建
2024-07-27 21:30:41 +08:00
DLmaster
6518a378ac 修正v3.0_Beta版更新说明 2024-07-24 21:13:50 +08:00
DLmaster
ebb73182f7 添加开机自启与阻止休眠功能;优化配置文件处理方式 2024-07-24 20:57:15 +08:00
7 changed files with 386 additions and 143 deletions

44
.github/workflows/python-app.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
name: Python application
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v3
with:
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
pip install -r requirements.txt
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Built with pyinstaller
run: |
pyinstaller -F --version-file res/info.txt -w --icon=res/AUTO_MAA.ico AUTO_MAA.py
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: AUTO_MAA
path: ./

Binary file not shown.

View File

@@ -42,11 +42,14 @@ import sqlite3
import json import json
import datetime import datetime
import os import os
import sys
import ctypes
import hashlib import hashlib
import subprocess import subprocess
import time import time
import random import random
import secrets import secrets
import winreg
from Crypto.Cipher import AES from Crypto.Cipher import AES
from Crypto.PublicKey import RSA from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP from Crypto.Cipher import PKCS1_OAEP
@@ -60,6 +63,9 @@ class MaaRunner(QtCore.QThread):
UpGui = QtCore.Signal(str, str, str, str, str) UpGui = QtCore.Signal(str, str, str, str, str)
UpUserInfo = QtCore.Signal(list, list, list, list) UpUserInfo = QtCore.Signal(list, list, list, list)
Accomplish = QtCore.Signal() Accomplish = QtCore.Signal()
AppPath = os.path.dirname(os.path.realpath(sys.argv[0])).replace(
"\\", "/"
) # 获取软件自身的路径
ifRun = False ifRun = False
def __init__(self, SetPath, LogPath, MaaPath, Routine, Annihilation, Num, data): def __init__(self, SetPath, LogPath, MaaPath, Routine, Annihilation, Num, data):
@@ -113,21 +119,17 @@ class MaaRunner(QtCore.QThread):
maa = subprocess.Popen([self.MaaPath]) maa = subprocess.Popen([self.MaaPath])
# 记录当前时间 # 记录当前时间
StartTime = datetime.datetime.now() StartTime = datetime.datetime.now()
# 初始化log记录列表 # 记录是否超时的标记
if j == 0: self.TimeOut = False
logx = [k for k in range(self.Annihilation * 60)]
elif j == 1:
logx = [k for k in range(self.Routine * 60)]
logi = 0
# 更新运行信息 # 更新运行信息
WaitUid = [ WaitUid = [
idx for idx in AllUid if (not idx in OverUid + ErrorUid + [uid]) idx for idx in AllUid if (not idx in OverUid + ErrorUid + [uid])
] ]
# 监测MAA运行状态 # 监测MAA运行状态
while True: while True:
# 更新MAA日志 # 获取MAA日志
logs = []
with open(self.LogPath, "r", encoding="utf-8") as f: with open(self.LogPath, "r", encoding="utf-8") as f:
logs = []
for entry in f: for entry in f:
try: try:
entry_time = datetime.datetime.strptime( entry_time = datetime.datetime.strptime(
@@ -137,26 +139,41 @@ class MaaRunner(QtCore.QThread):
logs.append(entry) logs.append(entry)
except ValueError: except ValueError:
pass pass
log = "".join(logs) # 判断是否超时
if j == 0: if len(logs) > 0:
self.UpGui.emit( LastTime = datetime.datetime.strptime(
self.data[uid][0] + "_第" + str(i + 1) + "次_剿灭", logs[-1][1:20], "%Y-%m-%d %H:%M:%S"
"\n".join([self.data[k][0] for k in WaitUid]), )
"\n".join([self.data[k][0] for k in OverUid]), NowTime = datetime.datetime.now()
"\n".join([self.data[k][0] for k in ErrorUid]), if (
log, j == 0
) and NowTime - LastTime
elif j == 1: > datetime.timedelta(minutes=self.Annihilation)
self.UpGui.emit( ) or (
self.data[uid][0] + "_第" + str(i + 1) + "次_日常", j == 1
"\n".join([self.data[k][0] for k in WaitUid]), and NowTime - LastTime
"\n".join([self.data[k][0] for k in OverUid]), > datetime.timedelta(minutes=self.Routine)
"\n".join([self.data[k][0] for k in ErrorUid]), ):
log, self.TimeOut = True
) # 合并日志
if len(logs) != 0: log = "".join(logs)
logx[logi] = logs[-1] # 更新MAA日志
logi = (logi + 1) % len(logx) if j == 0:
self.UpGui.emit(
self.data[uid][0] + "_第" + str(i + 1) + "次_剿灭",
"\n".join([self.data[k][0] for k in WaitUid]),
"\n".join([self.data[k][0] for k in OverUid]),
"\n".join([self.data[k][0] for k in ErrorUid]),
log,
)
elif j == 1:
self.UpGui.emit(
self.data[uid][0] + "_第" + str(i + 1) + "次_日常",
"\n".join([self.data[k][0] for k in WaitUid]),
"\n".join([self.data[k][0] for k in OverUid]),
"\n".join([self.data[k][0] for k in ErrorUid]),
log,
)
# 判断MAA程序运行状态 # 判断MAA程序运行状态
if "任务已全部完成!" in log: if "任务已全部完成!" in log:
runbook[j] = True runbook[j] = True
@@ -167,17 +184,17 @@ class MaaRunner(QtCore.QThread):
"\n".join([self.data[k][0] for k in ErrorUid]), "\n".join([self.data[k][0] for k in ErrorUid]),
"检测到MAA进程完成代理任务\n正在等待相关程序结束\n请等待10s", "检测到MAA进程完成代理任务\n正在等待相关程序结束\n请等待10s",
) )
maa.wait()
time.sleep(10) time.sleep(10)
break break
elif ( elif (
("请检查连接设置或尝试重启模拟器与 ADB 或重启电脑" in log) ("请检查连接设置或尝试重启模拟器与 ADB 或重启电脑" in log)
or ("已停止" in log) or ("已停止" in log)
or ("MaaAssistantArknights GUI exited" in log) or ("MaaAssistantArknights GUI exited" in log)
or self.TimeOut(logx) or self.TimeOut
or not self.ifRun or not self.ifRun
): ):
# 打印中止信息 # 打印中止信息
# 此时log变量内存储的就是出现异常的日志信息可以保存或发送用于问题排查
if ( if (
( (
"请检查连接设置或尝试重启模拟器与 ADB 或重启电脑" "请检查连接设置或尝试重启模拟器与 ADB 或重启电脑"
@@ -187,7 +204,7 @@ class MaaRunner(QtCore.QThread):
or ("MaaAssistantArknights GUI exited" in log) or ("MaaAssistantArknights GUI exited" in log)
): ):
info = "检测到MAA进程异常\n正在中止相关程序\n请等待10s" info = "检测到MAA进程异常\n正在中止相关程序\n请等待10s"
elif self.TimeOut(logx): elif self.TimeOut:
info = "检测到MAA进程超时\n正在中止相关程序\n请等待10s" info = "检测到MAA进程超时\n正在中止相关程序\n请等待10s"
elif not self.ifRun: elif not self.ifRun:
info = "您中止了本次任务\n正在中止相关程序\n请等待" info = "您中止了本次任务\n正在中止相关程序\n请等待"
@@ -199,7 +216,6 @@ class MaaRunner(QtCore.QThread):
info, info,
) )
os.system("taskkill /F /T /PID " + str(maa.pid)) os.system("taskkill /F /T /PID " + str(maa.pid))
maa.wait()
if self.ifRun: if self.ifRun:
time.sleep(10) time.sleep(10)
break break
@@ -220,7 +236,7 @@ class MaaRunner(QtCore.QThread):
self.UpUserInfo.emit(AllUid, days, lasts, numbs) self.UpUserInfo.emit(AllUid, days, lasts, numbs)
# 保存运行日志 # 保存运行日志
endtime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") endtime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open("log.txt", "w", encoding="utf-8") as f: with open(self.AppPath + "log.txt", "w", encoding="utf-8") as f:
print("任务开始时间:" + begintime + ",结束时间:" + endtime, file=f) print("任务开始时间:" + begintime + ",结束时间:" + endtime, file=f)
print( print(
"已完成数:" + str(len(OverUid)) + ",未完成数:" + str(len(ErrorUid)), "已完成数:" + str(len(OverUid)) + ",未完成数:" + str(len(ErrorUid)),
@@ -234,7 +250,7 @@ class MaaRunner(QtCore.QThread):
if len(WaitUid) != 0: if len(WaitUid) != 0:
print("未代理的用户:", file=f) print("未代理的用户:", file=f)
print("\n".join([self.data[k][0] for k in WaitUid]), file=f) print("\n".join([self.data[k][0] for k in WaitUid]), file=f)
with open("log.txt", "r", encoding="utf-8") as f: with open(self.AppPath + "log.txt", "r", encoding="utf-8") as f:
EndLog = f.read() EndLog = f.read()
# 恢复GUI运行面板 # 恢复GUI运行面板
self.UpGui.emit("", "", "", "", EndLog) self.UpGui.emit("", "", "", "", EndLog)
@@ -247,8 +263,8 @@ class MaaRunner(QtCore.QThread):
data = json.load(f) data = json.load(f)
if s == 0: if s == 0:
data["Configurations"]["Default"][ data["Configurations"]["Default"][
"MainFunction.ActionAfterCompleted" "MainFunction.PostActions"
] = "ExitEmulatorAndSelf" # 完成后退出MAA和模拟器 ] = "12" # 完成后退出MAA和模拟器
data["Configurations"]["Default"][ data["Configurations"]["Default"][
"Start.RunDirectly" "Start.RunDirectly"
] = "True" # 启动MAA后直接运行 ] = "True" # 启动MAA后直接运行
@@ -325,46 +341,53 @@ class MaaRunner(QtCore.QThread):
data["Configurations"]["Default"][ data["Configurations"]["Default"][
"TaskQueue.Mall.IsChecked" "TaskQueue.Mall.IsChecked"
] = "True" # 获取信用及购物 ] = "True" # 获取信用及购物
data["Configurations"]["Default"]["MainFunction.Stage1"] = self.data[uid][ # 主关卡
5 if self.data[uid][5] == "-":
] # 主关卡 data["Configurations"]["Default"]["MainFunction.Stage1"] = ""
data["Configurations"]["Default"]["MainFunction.Stage2"] = self.data[uid][ else:
6 data["Configurations"]["Default"]["MainFunction.Stage1"] = self.data[
] # 备选关卡1 uid
data["Configurations"]["Default"]["MainFunction.Stage3"] = self.data[uid][ ][5]
7 # 备选关卡1
] # 备选关卡2 if self.data[uid][6] == "-":
data["Configurations"]["Default"]["MainFunction.Stage2"] = ""
else:
data["Configurations"]["Default"]["MainFunction.Stage2"] = self.data[
uid
][6]
# 备选关卡2
if self.data[uid][7] == "-":
data["Configurations"]["Default"]["MainFunction.Stage3"] = ""
else:
data["Configurations"]["Default"]["MainFunction.Stage3"] = self.data[
uid
][7]
data["Configurations"]["Default"][ data["Configurations"]["Default"][
"Fight.RemainingSanityStage" "Fight.RemainingSanityStage"
] = "" # 剩余理智关卡 ] = "" # 剩余理智关卡
# 连战次数
if self.data[uid][5] == "1-7": if self.data[uid][5] == "1-7":
data["Configurations"]["Default"][ data["Configurations"]["Default"]["MainFunction.Series.Quantity"] = "6"
"MainFunction.Series.Quantity"
] = "6" # 连战次数
else: else:
data["Configurations"]["Default"][ data["Configurations"]["Default"]["MainFunction.Series.Quantity"] = "1"
"MainFunction.Series.Quantity"
] = "1" # 连战次数
data["Configurations"]["Default"][ data["Configurations"]["Default"][
"Penguin.IsDrGrandet" "Penguin.IsDrGrandet"
] = "False" # 博朗台模式 ] = "False" # 博朗台模式
data["Configurations"]["Default"][ data["Configurations"]["Default"][
"GUI.CustomStageCode" "GUI.CustomStageCode"
] = "True" # 手动输入关卡名 ] = "True" # 手动输入关卡名
# 备选关卡
if self.data[uid][6] == "-" and self.data[uid][7] == "-": if self.data[uid][6] == "-" and self.data[uid][7] == "-":
data["Configurations"]["Default"][ data["Configurations"]["Default"]["GUI.UseAlternateStage"] = "False"
"GUI.UseAlternateStage"
] = "False" # 不使用备选关卡
else: else:
data["Configurations"]["Default"][ data["Configurations"]["Default"]["GUI.UseAlternateStage"] = "True"
"GUI.UseAlternateStage"
] = "True" # 使用备选关卡
data["Configurations"]["Default"][ data["Configurations"]["Default"][
"Fight.UseRemainingSanityStage" "Fight.UseRemainingSanityStage"
] = "False" # 使用剩余理智 ] = "False" # 使用剩余理智
data["Configurations"]["Default"][ data["Configurations"]["Default"][
"Fight.UseExpiringMedicine" "Fight.UseExpiringMedicine"
] = "True" # 无限吃48小时内过期的理智药 ] = "True" # 无限吃48小时内过期的理智药
# 自定义基建配置
if self.data[uid][9] == "-": if self.data[uid][9] == "-":
data["Configurations"]["Default"][ data["Configurations"]["Default"][
"Infrast.CustomInfrastEnabled" "Infrast.CustomInfrastEnabled"
@@ -388,28 +411,25 @@ class MaaRunner(QtCore.QThread):
json.dump(data, f, indent=4) json.dump(data, f, indent=4)
return True return True
# 超时判断
def TimeOut(self, logx):
log0 = logx[0]
for i in logx:
if i != log0:
return False
return True
class MainTimer(QtCore.QThread):
class MaaTimer(QtCore.QThread):
GetConfig = QtCore.Signal() GetConfig = QtCore.Signal()
StartForTimer = QtCore.Signal() StartForTimer = QtCore.Signal()
AppPath = os.path.realpath(sys.argv[0]) # 获取软件自身的路径
AppName = os.path.basename(AppPath) # 获取软件自身的名称
ES_CONTINUOUS = 0x80000000
ES_SYSTEM_REQUIRED = 0x00000001
isMaaRun = False isMaaRun = False
def __init__(self, config): def __init__(self, config):
super(MaaTimer, self).__init__() super(MainTimer, self).__init__()
self.config = config self.config = config
def run(self): def run(self):
while True: while True:
self.GetConfig.emit() self.GetConfig.emit()
self.setSystem()
TimeSet = [ TimeSet = [
self.config["Default"]["TimeSet.run" + str(k + 1)] self.config["Default"]["TimeSet.run" + str(k + 1)]
for k in range(10) for k in range(10)
@@ -420,16 +440,73 @@ class MaaTimer(QtCore.QThread):
self.StartForTimer.emit() self.StartForTimer.emit()
time.sleep(1) time.sleep(1)
def setSystem(self):
# 同步系统休眠状态
if self.config["Default"]["SelfSet.IfSleep"] == "True":
# 设置系统电源状态
ctypes.windll.kernel32.SetThreadExecutionState(
self.ES_CONTINUOUS | self.ES_SYSTEM_REQUIRED
)
elif self.config["Default"]["SelfSet.IfSleep"] == "False":
# 恢复系统电源状态
ctypes.windll.kernel32.SetThreadExecutionState(self.ES_CONTINUOUS)
# 同步开机自启
if (
self.config["Default"]["SelfSet.IfSelfStart"] == "True"
and not self.IsStartup()
):
key = winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Run",
winreg.KEY_SET_VALUE,
winreg.KEY_ALL_ACCESS | winreg.KEY_WRITE | winreg.KEY_CREATE_SUB_KEY,
)
winreg.SetValueEx(key, self.AppName, 0, winreg.REG_SZ, self.AppPath)
winreg.CloseKey(key)
elif (
self.config["Default"]["SelfSet.IfSelfStart"] == "False"
and self.IsStartup()
):
key = winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Run",
winreg.KEY_SET_VALUE,
winreg.KEY_ALL_ACCESS | winreg.KEY_WRITE | winreg.KEY_CREATE_SUB_KEY,
)
winreg.DeleteValue(key, self.AppName)
winreg.CloseKey(key)
def IsStartup(self):
key = winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Run",
0,
winreg.KEY_READ,
)
try:
value, _ = winreg.QueryValueEx(key, self.AppName)
return True
except FileNotFoundError:
return False
finally:
winreg.CloseKey(key)
class Main(QWidget): class Main(QWidget):
AppPath = os.path.dirname(os.path.realpath(sys.argv[0])).replace(
"\\", "/"
) # 获取软件自身的路径
def __init__(self, PASSWARD=""): def __init__(self, PASSWARD=""):
super().__init__() super().__init__()
self.DatabasePath = "data/data.db" self.DatabasePath = self.AppPath + "/data/data.db"
self.ConfigPath = "config/gui.json" self.ConfigPath = self.AppPath + "/config/gui.json"
self.KeyPath = "data/key" self.KeyPath = self.AppPath + "/data/key"
self.GameidPath = "data/gameid.txt" self.GameidPath = self.AppPath + "/data/gameid.txt"
self.PASSWORD = PASSWARD self.PASSWORD = PASSWARD
self.ifUpDatabase = True self.ifUpDatabase = True
self.ifUpConfig = True self.ifUpConfig = True
@@ -450,12 +527,13 @@ class Main(QWidget):
"uid", "uid",
] ]
self.ui = uiLoader.load("gui/ui/main.ui") self.ui = uiLoader.load(self.AppPath + "/gui/ui/main.ui")
self.ui.setWindowTitle("AUTO_MAA") self.ui.setWindowTitle("AUTO_MAA")
self.ui.setWindowIcon(QIcon("res/AUTO_MAA.ico")) self.ui.setWindowIcon(QIcon(self.AppPath + "/res/AUTO_MAA.ico"))
# 检查文件完整性 # 检查文件完整性
if not os.path.exists(self.DatabasePath) or not os.path.exists(self.ConfigPath): if not os.path.exists(self.DatabasePath) or not os.path.exists(self.ConfigPath):
self.initialize() self.initialize()
self.CheckConfig()
with open(self.ConfigPath, "r") as f: with open(self.ConfigPath, "r") as f:
self.config = json.load(f) self.config = json.load(f)
if not os.path.exists(self.KeyPath): if not os.path.exists(self.KeyPath):
@@ -510,6 +588,12 @@ class Main(QWidget):
self.num = self.ui.findChild(QSpinBox, "spinBox_numt") self.num = self.ui.findChild(QSpinBox, "spinBox_numt")
self.num.valueChanged.connect(self.ChangeConfig) self.num.valueChanged.connect(self.ChangeConfig)
self.IfSelfStart = self.ui.findChild(QCheckBox, "checkBox_ifselfstart")
self.IfSelfStart.stateChanged.connect(self.ChangeConfig)
self.IfSleep = self.ui.findChild(QCheckBox, "checkBox_ifsleep")
self.IfSleep.stateChanged.connect(self.ChangeConfig)
self.RunText = self.ui.findChild(QTextBrowser, "textBrowser_run") self.RunText = self.ui.findChild(QTextBrowser, "textBrowser_run")
self.WaitText = self.ui.findChild(QTextBrowser, "textBrowser_wait") self.WaitText = self.ui.findChild(QTextBrowser, "textBrowser_wait")
self.OverText = self.ui.findChild(QTextBrowser, "textBrowser_over") self.OverText = self.ui.findChild(QTextBrowser, "textBrowser_over")
@@ -553,10 +637,10 @@ class Main(QWidget):
self.MaaRunner.UpUserInfo.connect(self.ChangeUserInfo) self.MaaRunner.UpUserInfo.connect(self.ChangeUserInfo)
self.MaaRunner.Accomplish.connect(self.end) self.MaaRunner.Accomplish.connect(self.end)
self.MaaTimer = MaaTimer(self.config) self.MainTimer = MainTimer(self.config)
self.MaaTimer.GetConfig.connect(self.GiveConfig) self.MainTimer.GetConfig.connect(self.GiveConfig)
self.MaaTimer.StartForTimer.connect(self.RunStarter) self.MainTimer.StartForTimer.connect(self.RunStarter)
self.MaaTimer.start() self.MainTimer.start()
# 载入GUI数据 # 载入GUI数据
self.UpdateTable("normal") self.UpdateTable("normal")
@@ -565,10 +649,10 @@ class Main(QWidget):
# 初始化 # 初始化
def initialize(self): def initialize(self):
# 检查目录 # 检查目录
if not os.path.exists("data"): if not os.path.exists(self.AppPath + "/data"):
os.makedirs("data") os.makedirs(self.AppPath + "/data")
if not os.path.exists("config"): if not os.path.exists(self.AppPath + "/config"):
os.makedirs("config") os.makedirs(self.AppPath + "/config")
# 生成用户数据库 # 生成用户数据库
if not os.path.exists(self.DatabasePath): if not os.path.exists(self.DatabasePath):
db = sqlite3.connect(self.DatabasePath) db = sqlite3.connect(self.DatabasePath)
@@ -583,55 +667,74 @@ class Main(QWidget):
db.close() db.close()
# 生成配置文件 # 生成配置文件
if not os.path.exists(self.ConfigPath): if not os.path.exists(self.ConfigPath):
config = { config = {"Default": {}}
"Default": {
"TimeSet.set1": "False",
"TimeSet.run1": "00:00",
"TimeSet.set2": "False",
"TimeSet.run2": "00:00",
"TimeSet.set3": "False",
"TimeSet.run3": "00:00",
"TimeSet.set4": "False",
"TimeSet.run4": "00:00",
"TimeSet.set5": "False",
"TimeSet.run5": "00:00",
"TimeSet.set6": "False",
"TimeSet.run6": "00:00",
"TimeSet.set7": "False",
"TimeSet.run7": "00:00",
"TimeSet.set8": "False",
"TimeSet.run8": "00:00",
"TimeSet.set9": "False",
"TimeSet.run9": "00:00",
"TimeSet.set10": "False",
"TimeSet.run10": "00:00",
"MaaSet.path": "",
"TimeLimit.routine": 10,
"TimeLimit.annihilation": 40,
"TimesLimit.run": 3,
}
}
with open(self.ConfigPath, "w") as f: with open(self.ConfigPath, "w") as f:
json.dump(config, f, indent=4) json.dump(config, f, indent=4)
# 检查配置文件字段
def CheckConfig(self):
ConfigList = [
["TimeSet.set1", "False"],
["TimeSet.run1", "00:00"],
["TimeSet.set2", "False"],
["TimeSet.run2", "00:00"],
["TimeSet.set3", "False"],
["TimeSet.run3", "00:00"],
["TimeSet.set4", "False"],
["TimeSet.run4", "00:00"],
["TimeSet.set5", "False"],
["TimeSet.run5", "00:00"],
["TimeSet.set6", "False"],
["TimeSet.run6", "00:00"],
["TimeSet.set7", "False"],
["TimeSet.run7", "00:00"],
["TimeSet.set8", "False"],
["TimeSet.run8", "00:00"],
["TimeSet.set9", "False"],
["TimeSet.run9", "00:00"],
["TimeSet.set10", "False"],
["TimeSet.run10", "00:00"],
["MaaSet.path", ""],
["TimeLimit.routine", 10],
["TimeLimit.annihilation", 40],
["TimesLimit.run", 3],
["SelfSet.IfSelfStart", "False"],
["SelfSet.IfSleep", "False"],
]
# 导入配置文件
with open(self.ConfigPath, "r") as f:
config = json.load(f)
# 检查并补充缺失的字段
for i in range(len(ConfigList)):
if not ConfigList[i][0] in config["Default"]:
config["Default"][ConfigList[i][0]] = ConfigList[i][1]
# 导出配置文件
with open(self.ConfigPath, "w") as f:
json.dump(config, f, indent=4)
# 配置密钥 # 配置密钥
def getPASSWORD(self): def getPASSWORD(self):
# 检查目录 # 检查目录
if not os.path.exists("data/key"): if not os.path.exists(self.AppPath + "/data/key"):
os.makedirs("data/key") os.makedirs(self.AppPath + "/data/key")
# 生成RSA密钥对 # 生成RSA密钥对
key = RSA.generate(2048) key = RSA.generate(2048)
public_key_local = key.publickey() public_key_local = key.publickey()
private_key = key private_key = key
# 保存RSA公钥 # 保存RSA公钥
with open("data/key/public_key.pem", "wb") as f: with open(self.AppPath + "/data/key/public_key.pem", "wb") as f:
f.write(public_key_local.exportKey()) f.write(public_key_local.exportKey())
# 生成密钥转换与校验随机盐 # 生成密钥转换与校验随机盐
PASSWORDsalt = secrets.token_hex(random.randint(32, 1024)) PASSWORDsalt = secrets.token_hex(random.randint(32, 1024))
with open("data/key/PASSWORDsalt.txt", "w", encoding="utf-8") as f: with open(
self.AppPath + "/data/key/PASSWORDsalt.txt", "w", encoding="utf-8"
) as f:
print(PASSWORDsalt, file=f) print(PASSWORDsalt, file=f)
verifysalt = secrets.token_hex(random.randint(32, 1024)) verifysalt = secrets.token_hex(random.randint(32, 1024))
with open("data/key/verifysalt.txt", "w", encoding="utf-8") as f: with open(
self.AppPath + "/data/key/verifysalt.txt", "w", encoding="utf-8"
) as f:
print(verifysalt, file=f) print(verifysalt, file=f)
# 将管理密钥转化为AES-256密钥 # 将管理密钥转化为AES-256密钥
AES_password = hashlib.sha256( AES_password = hashlib.sha256(
@@ -641,18 +744,18 @@ class Main(QWidget):
AES_password_verify = hashlib.sha256( AES_password_verify = hashlib.sha256(
AES_password + verifysalt.encode("utf-8") AES_password + verifysalt.encode("utf-8")
).digest() ).digest()
with open("data/key/AES_password_verify.bin", "wb") as f: with open(self.AppPath + "/data/key/AES_password_verify.bin", "wb") as f:
f.write(AES_password_verify) f.write(AES_password_verify)
# AES-256加密RSA私钥并保存密文 # AES-256加密RSA私钥并保存密文
AES_key = AES.new(AES_password, AES.MODE_ECB) AES_key = AES.new(AES_password, AES.MODE_ECB)
private_key_local = AES_key.encrypt(pad(private_key.exportKey(), 32)) private_key_local = AES_key.encrypt(pad(private_key.exportKey(), 32))
with open("data/key/private_key.bin", "wb") as f: with open(self.AppPath + "/data/key/private_key.bin", "wb") as f:
f.write(private_key_local) f.write(private_key_local)
# 加密 # 加密
def encryptx(self, note): def encryptx(self, note):
# 读取RSA公钥 # 读取RSA公钥
with open("data/key/public_key.pem", "rb") as f: with open(self.AppPath + "/data/key/public_key.pem", "rb") as f:
public_key_local = RSA.import_key(f.read()) public_key_local = RSA.import_key(f.read())
# 使用RSA公钥对数据进行加密 # 使用RSA公钥对数据进行加密
cipher = PKCS1_OAEP.new(public_key_local) cipher = PKCS1_OAEP.new(public_key_local)
@@ -662,13 +765,17 @@ class Main(QWidget):
# 解密 # 解密
def decryptx(self, note): def decryptx(self, note):
# 读入RSA私钥密文、盐与校验哈希值 # 读入RSA私钥密文、盐与校验哈希值
with open("data/key/private_key.bin", "rb") as f: with open(self.AppPath + "/data/key/private_key.bin", "rb") as f:
private_key_local = f.read().strip() private_key_local = f.read().strip()
with open("data/key/PASSWORDsalt.txt", "r", encoding="utf-8") as f: with open(
self.AppPath + "/data/key/PASSWORDsalt.txt", "r", encoding="utf-8"
) as f:
PASSWORDsalt = f.read().strip() PASSWORDsalt = f.read().strip()
with open("data/key/verifysalt.txt", "r", encoding="utf-8") as f: with open(
self.AppPath + "/data/key/verifysalt.txt", "r", encoding="utf-8"
) as f:
verifysalt = f.read().strip() verifysalt = f.read().strip()
with open("data/key/AES_password_verify.bin", "rb") as f: with open(self.AppPath + "/data/key/AES_password_verify.bin", "rb") as f:
AES_password_verify = f.read().strip() AES_password_verify = f.read().strip()
# 将管理密钥转化为AES-256密钥并验证 # 将管理密钥转化为AES-256密钥并验证
AES_password = hashlib.sha256( AES_password = hashlib.sha256(
@@ -825,6 +932,15 @@ class Main(QWidget):
self.routine.setValue(self.config["Default"]["TimeLimit.routine"]) self.routine.setValue(self.config["Default"]["TimeLimit.routine"])
self.annihilation.setValue(self.config["Default"]["TimeLimit.annihilation"]) self.annihilation.setValue(self.config["Default"]["TimeLimit.annihilation"])
self.num.setValue(self.config["Default"]["TimesLimit.run"]) self.num.setValue(self.config["Default"]["TimesLimit.run"])
self.IfSelfStart.setChecked(
bool(self.config["Default"]["SelfSet.IfSelfStart"] == "True")
)
self.IfSleep.setChecked(
bool(self.config["Default"]["SelfSet.IfSleep"] == "True")
)
for i in range(10): for i in range(10):
self.StartTime[i][0].setChecked( self.StartTime[i][0].setChecked(
bool(self.config["Default"]["TimeSet.set" + str(i + 1)] == "True") bool(self.config["Default"]["TimeSet.set" + str(i + 1)] == "True")
@@ -900,15 +1016,11 @@ class Main(QWidget):
if os.path.exists(self.GameidPath): if os.path.exists(self.GameidPath):
with open(self.GameidPath, encoding="utf-8") as f: with open(self.GameidPath, encoding="utf-8") as f:
gameids = f.readlines() gameids = f.readlines()
for i in range(len(gameids)): for line in gameids:
for j in range(len(gameids[i])): if "" in line:
if gameids[i][j] == "" or gameids[i][j] == ":": gamein, gameout = line.split("", 1)
gamein = gameids[i][:j] games[gamein.strip()] = gameout.strip()
gameout = gameids[i][j + 1 :] text = games.get(text, text)
break
games[gamein.strip()] = gameout.strip()
if text in games:
text = games[text]
if item.column() == 9: if item.column() == 9:
text = text.replace("\\", "/") text = text.replace("\\", "/")
if item.column() == 10: if item.column() == 10:
@@ -962,6 +1074,17 @@ class Main(QWidget):
self.config["Default"]["TimeLimit.routine"] = self.routine.value() self.config["Default"]["TimeLimit.routine"] = self.routine.value()
self.config["Default"]["TimeLimit.annihilation"] = self.annihilation.value() self.config["Default"]["TimeLimit.annihilation"] = self.annihilation.value()
self.config["Default"]["TimesLimit.run"] = self.num.value() self.config["Default"]["TimesLimit.run"] = self.num.value()
if self.IfSleep.isChecked():
self.config["Default"]["SelfSet.IfSleep"] = "True"
else:
self.config["Default"]["SelfSet.IfSleep"] = "False"
if self.IfSelfStart.isChecked():
self.config["Default"]["SelfSet.IfSelfStart"] = "True"
else:
self.config["Default"]["SelfSet.IfSelfStart"] = "False"
for i in range(10): for i in range(10):
if self.StartTime[i][0].isChecked(): if self.StartTime[i][0].isChecked():
self.config["Default"]["TimeSet.set" + str(i + 1)] = "True" self.config["Default"]["TimeSet.set" + str(i + 1)] = "True"
@@ -1000,7 +1123,7 @@ class Main(QWidget):
return 0 return 0
def closeEvent(self, event): def closeEvent(self, event):
self.MaaTimer.quit() self.MainTimer.quit()
self.MaaRunner.ifRun = False self.MaaRunner.ifRun = False
self.MaaRunner.wait() self.MaaRunner.wait()
self.cur.close() self.cur.close()
@@ -1011,7 +1134,7 @@ class Main(QWidget):
def end(self): def end(self):
self.MaaRunner.ifRun = False self.MaaRunner.ifRun = False
self.MaaRunner.wait() self.MaaRunner.wait()
self.MaaTimer.isMaaRun = False self.MainTimer.isMaaRun = False
self.runnow.clicked.disconnect() self.runnow.clicked.disconnect()
self.runnow.setText("立即执行") self.runnow.setText("立即执行")
self.runnow.clicked.connect(self.RunStarter) self.runnow.clicked.connect(self.RunStarter)
@@ -1040,11 +1163,12 @@ class Main(QWidget):
self.data_ = self.cur.fetchall() self.data_ = self.cur.fetchall()
self.MaaRunner.data = [list(row) for row in self.data_] self.MaaRunner.data = [list(row) for row in self.data_]
# 启动执行线程 # 启动执行线程
self.MaaTimer.isMaaRun = True self.MainTimer.isMaaRun = True
self.MaaRunner.start() self.MaaRunner.start()
# 同步配置文件到子线程
def GiveConfig(self): def GiveConfig(self):
self.MaaTimer.config = self.config self.MainTimer.config = self.config
class AUTO_MAA(QApplication): class AUTO_MAA(QApplication):

View File

@@ -745,7 +745,7 @@
<item> <item>
<widget class="QGroupBox" name="groupBox_3"> <widget class="QGroupBox" name="groupBox_3">
<property name="title"> <property name="title">
<string>时间限制</string> <string>执行限制</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0"> <item row="0" column="0">
@@ -898,6 +898,79 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox_7">
<property name="title">
<string>AUTO_MAA设置</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_20">
<item>
<widget class="QFrame" name="frame_routine_3">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_19">
<item>
<widget class="QCheckBox" name="checkBox_ifselfstart">
<property name="text">
<string>开机自动启动AUTO_MAA</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="frame_routine_2">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_18">
<item>
<widget class="QCheckBox" name="checkBox_ifsleep">
<property name="text">
<string>AUTO_MAA启动时禁止电脑休眠</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="groupBox_5"> <widget class="QGroupBox" name="groupBox_5">
<property name="title"> <property name="title">
@@ -932,7 +1005,7 @@ li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Microsoft YaHei UI'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt; &lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Microsoft YaHei UI'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;致用户:&lt;/p&gt; &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;致用户:&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt; 这是AUTO_MAA_v3.1,项目基本完成可视化。&lt;/p&gt; &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt; 这是AUTO_MAA_v3.1.1,项目基本完成可视化。&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt; 正式版去除了命令行窗口但这不意味着BUG不会出现。由于用户与项目贡献者的稀缺我们无法确保正式版足够完善还望谅解。&lt;/p&gt; &lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt; 正式版去除了命令行窗口但这不意味着BUG不会出现。由于用户与项目贡献者的稀缺我们无法确保正式版足够完善还望谅解。&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt; &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;

View File

@@ -4,7 +4,7 @@ VSVersionInfo(
ffi=FixedFileInfo( ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0. # Set not needed items to zero 0.
filevers=(3, 1, 0, 0), filevers=(3, 1, 2, 0),
prodvers=(0, 0, 0, 0), prodvers=(0, 0, 0, 0),
# Contains a bitmask that specifies the valid bits 'flags'r # Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f, mask=0x3f,
@@ -31,13 +31,13 @@ VSVersionInfo(
[StringStruct('Comments', 'https://github.com/DLmaster361/AUTO_MAA/'), [StringStruct('Comments', 'https://github.com/DLmaster361/AUTO_MAA/'),
StringStruct('CompanyName', 'AUTO_MAA Team'), StringStruct('CompanyName', 'AUTO_MAA Team'),
StringStruct('FileDescription', 'AUTO_MAA Component'), StringStruct('FileDescription', 'AUTO_MAA Component'),
StringStruct('FileVersion', '3.1'), StringStruct('FileVersion', '3.1.2'),
StringStruct('InternalName', 'AUTO_MAA'), StringStruct('InternalName', 'AUTO_MAA'),
StringStruct('LegalCopyright', 'Copyright © 2024 DLmaster361'), StringStruct('LegalCopyright', 'Copyright © 2024 DLmaster361'),
StringStruct('OriginalFilename', 'AUTO_MAA'), StringStruct('OriginalFilename', 'AUTO_MAA.py'),
StringStruct('ProductName', 'AUTO_MAA'), StringStruct('ProductName', 'AUTO_MAA'),
StringStruct('ProductVersion', 'v3.1'), StringStruct('ProductVersion', 'v3.1.2'),
StringStruct('Assembly Version', 'v3.1')]) StringStruct('Assembly Version', 'v3.1.2')])
]) ])
] ]
) )

View File

@@ -1,5 +1,6 @@
#主界面 #主界面
"MainFunction.ActionAfterCompleted": "ExitEmulatorAndSelf" #完成后 "MainFunction.ActionAfterCompleted": "ExitEmulatorAndSelf" #完成后(旧)
"MainFunction.PostActions": "12" #完成后(新)
"TaskQueue.WakeUp.IsChecked": "True" #开始唤醒 "TaskQueue.WakeUp.IsChecked": "True" #开始唤醒
"TaskQueue.Recruiting.IsChecked": "True" #自动公招 "TaskQueue.Recruiting.IsChecked": "True" #自动公招
"TaskQueue.Base.IsChecked": "True" #基建换班 "TaskQueue.Base.IsChecked": "True" #基建换班

View File

@@ -1,4 +1,5 @@
项目初始阶段,不会提供专门的版本更新程序,您需要手动更新程序。 项目初始阶段,不会提供专门的版本更新程序,您需要手动更新程序。
v2.1.5及以前的用户,由于新版本采用全新的架构,您需要手动输入之前的信息。 v2.1.5及以前的用户,由于新版本采用全新的架构,您需要手动输入之前的信息。
v3.0_Beta版用户直接用AUTO_MAA.exe替代gui.exe后重新设置每个用户的“自定义基建”选项输入“-”以关闭该功能,输入自定义基建配置文件地址以开启该功能)。 v3.0_Beta版用户直接用AUTO_MAA.exe替代gui.exe后将原文件夹下的gui文件夹用新版本对应文件替换重新设置每个用户的“自定义基建”选项(输入“-”以关闭该功能,输入自定义基建配置文件地址以开启该功能)。
v3.1~v3.1.1版用户将原文件夹下的AUTO_MAA.exe文件和gui文件夹用新版本对应文件替换即可。
新用户请忽略本说明。 新用户请忽略本说明。