diff --git a/AUTO_MAA.exe b/AUTO_MAA.exe index 5f51638..aa140c9 100644 Binary files a/AUTO_MAA.exe and b/AUTO_MAA.exe differ diff --git a/AUTO_MAA.py b/AUTO_MAA.py index 3088bd4..de5a95b 100644 --- a/AUTO_MAA.py +++ b/AUTO_MAA.py @@ -42,11 +42,14 @@ import sqlite3 import json import datetime import os +import sys +import ctypes import hashlib import subprocess import time import random import secrets +import winreg from Crypto.Cipher import AES from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP @@ -60,6 +63,9 @@ class MaaRunner(QtCore.QThread): UpGui = QtCore.Signal(str, str, str, str, str) UpUserInfo = QtCore.Signal(list, list, list, list) Accomplish = QtCore.Signal() + AppPath = os.path.dirname(os.path.realpath(sys.argv[0])).replace( + "\\", "/" + ) # 获取软件自身的路径 ifRun = False def __init__(self, SetPath, LogPath, MaaPath, Routine, Annihilation, Num, data): @@ -167,7 +173,6 @@ class MaaRunner(QtCore.QThread): "\n".join([self.data[k][0] for k in ErrorUid]), "检测到MAA进程完成代理任务\n正在等待相关程序结束\n请等待10s", ) - maa.wait() time.sleep(10) break elif ( @@ -199,7 +204,6 @@ class MaaRunner(QtCore.QThread): info, ) os.system("taskkill /F /T /PID " + str(maa.pid)) - maa.wait() if self.ifRun: time.sleep(10) break @@ -220,7 +224,7 @@ class MaaRunner(QtCore.QThread): self.UpUserInfo.emit(AllUid, days, lasts, numbs) # 保存运行日志 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( "已完成数:" + str(len(OverUid)) + ",未完成数:" + str(len(ErrorUid)), @@ -234,7 +238,7 @@ class MaaRunner(QtCore.QThread): if len(WaitUid) != 0: print("未代理的用户:", 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() # 恢复GUI运行面板 self.UpGui.emit("", "", "", "", EndLog) @@ -397,19 +401,24 @@ class MaaRunner(QtCore.QThread): return True -class MaaTimer(QtCore.QThread): +class MainTimer(QtCore.QThread): GetConfig = 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 def __init__(self, config): - super(MaaTimer, self).__init__() + super(MainTimer, self).__init__() self.config = config def run(self): while True: self.GetConfig.emit() + self.setSystem() TimeSet = [ self.config["Default"]["TimeSet.run" + str(k + 1)] for k in range(10) @@ -420,16 +429,73 @@ class MaaTimer(QtCore.QThread): self.StartForTimer.emit() 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): + AppPath = os.path.dirname(os.path.realpath(sys.argv[0])).replace( + "\\", "/" + ) # 获取软件自身的路径 + def __init__(self, PASSWARD=""): super().__init__() - self.DatabasePath = "data/data.db" - self.ConfigPath = "config/gui.json" - self.KeyPath = "data/key" - self.GameidPath = "data/gameid.txt" + self.DatabasePath = self.AppPath + "/data/data.db" + self.ConfigPath = self.AppPath + "/config/gui.json" + self.KeyPath = self.AppPath + "/data/key" + self.GameidPath = self.AppPath + "/data/gameid.txt" self.PASSWORD = PASSWARD self.ifUpDatabase = True self.ifUpConfig = True @@ -450,12 +516,13 @@ class Main(QWidget): "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.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): self.initialize() + self.CheckConfig() with open(self.ConfigPath, "r") as f: self.config = json.load(f) if not os.path.exists(self.KeyPath): @@ -510,6 +577,12 @@ class Main(QWidget): self.num = self.ui.findChild(QSpinBox, "spinBox_numt") 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.WaitText = self.ui.findChild(QTextBrowser, "textBrowser_wait") self.OverText = self.ui.findChild(QTextBrowser, "textBrowser_over") @@ -553,10 +626,10 @@ class Main(QWidget): self.MaaRunner.UpUserInfo.connect(self.ChangeUserInfo) self.MaaRunner.Accomplish.connect(self.end) - self.MaaTimer = MaaTimer(self.config) - self.MaaTimer.GetConfig.connect(self.GiveConfig) - self.MaaTimer.StartForTimer.connect(self.RunStarter) - self.MaaTimer.start() + self.MainTimer = MainTimer(self.config) + self.MainTimer.GetConfig.connect(self.GiveConfig) + self.MainTimer.StartForTimer.connect(self.RunStarter) + self.MainTimer.start() # 载入GUI数据 self.UpdateTable("normal") @@ -565,10 +638,10 @@ class Main(QWidget): # 初始化 def initialize(self): # 检查目录 - if not os.path.exists("data"): - os.makedirs("data") - if not os.path.exists("config"): - os.makedirs("config") + if not os.path.exists(self.AppPath + "/data"): + os.makedirs(self.AppPath + "/data") + if not os.path.exists(self.AppPath + "/config"): + os.makedirs(self.AppPath + "/config") # 生成用户数据库 if not os.path.exists(self.DatabasePath): db = sqlite3.connect(self.DatabasePath) @@ -583,55 +656,74 @@ class Main(QWidget): db.close() # 生成配置文件 if not os.path.exists(self.ConfigPath): - config = { - "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, - } - } + config = {"Default": {}} with open(self.ConfigPath, "w") as f: 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): # 检查目录 - if not os.path.exists("data/key"): - os.makedirs("data/key") + if not os.path.exists(self.AppPath + "/data/key"): + os.makedirs(self.AppPath + "/data/key") # 生成RSA密钥对 key = RSA.generate(2048) public_key_local = key.publickey() private_key = key # 保存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()) # 生成密钥转换与校验随机盐 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) 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) # 将管理密钥转化为AES-256密钥 AES_password = hashlib.sha256( @@ -641,18 +733,18 @@ class Main(QWidget): AES_password_verify = hashlib.sha256( AES_password + verifysalt.encode("utf-8") ).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) # AES-256加密RSA私钥并保存密文 AES_key = AES.new(AES_password, AES.MODE_ECB) 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) # 加密 def encryptx(self, note): # 读取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()) # 使用RSA公钥对数据进行加密 cipher = PKCS1_OAEP.new(public_key_local) @@ -662,13 +754,17 @@ class Main(QWidget): # 解密 def decryptx(self, note): # 读入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() - 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() - 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() - 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-256密钥并验证 AES_password = hashlib.sha256( @@ -825,6 +921,15 @@ class Main(QWidget): self.routine.setValue(self.config["Default"]["TimeLimit.routine"]) self.annihilation.setValue(self.config["Default"]["TimeLimit.annihilation"]) 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): self.StartTime[i][0].setChecked( bool(self.config["Default"]["TimeSet.set" + str(i + 1)] == "True") @@ -962,6 +1067,17 @@ class Main(QWidget): self.config["Default"]["TimeLimit.routine"] = self.routine.value() self.config["Default"]["TimeLimit.annihilation"] = self.annihilation.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): if self.StartTime[i][0].isChecked(): self.config["Default"]["TimeSet.set" + str(i + 1)] = "True" @@ -1000,7 +1116,7 @@ class Main(QWidget): return 0 def closeEvent(self, event): - self.MaaTimer.quit() + self.MainTimer.quit() self.MaaRunner.ifRun = False self.MaaRunner.wait() self.cur.close() @@ -1011,7 +1127,7 @@ class Main(QWidget): def end(self): self.MaaRunner.ifRun = False self.MaaRunner.wait() - self.MaaTimer.isMaaRun = False + self.MainTimer.isMaaRun = False self.runnow.clicked.disconnect() self.runnow.setText("立即执行") self.runnow.clicked.connect(self.RunStarter) @@ -1040,11 +1156,12 @@ class Main(QWidget): self.data_ = self.cur.fetchall() self.MaaRunner.data = [list(row) for row in self.data_] # 启动执行线程 - self.MaaTimer.isMaaRun = True + self.MainTimer.isMaaRun = True self.MaaRunner.start() + # 同步配置文件到子线程 def GiveConfig(self): - self.MaaTimer.config = self.config + self.MainTimer.config = self.config class AUTO_MAA(QApplication): diff --git a/gui/ui/main.ui b/gui/ui/main.ui index 207146e..ec26c14 100644 --- a/gui/ui/main.ui +++ b/gui/ui/main.ui @@ -745,7 +745,7 @@ - 时间限制 + 执行限制 @@ -898,6 +898,79 @@ + + + + AUTO_MAA设置 + + + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + 开机自动启动AUTO_MAA + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + + + AUTO_MAA启动时禁止电脑休眠 + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + @@ -932,7 +1005,7 @@ li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'Microsoft YaHei UI'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">致用户:</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> 这是AUTO_MAA_v3.1,项目基本完成可视化。</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> 这是AUTO_MAA_v3.1.1,项目基本完成可视化。</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"> 正式版去除了命令行窗口,但这不意味着BUG不会出现。由于用户与项目贡献者的稀缺,我们无法确保正式版足够完善,还望谅解。</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> diff --git a/res/info.txt b/res/info.txt index f0d6abb..7cab613 100644 --- a/res/info.txt +++ b/res/info.txt @@ -4,7 +4,7 @@ VSVersionInfo( ffi=FixedFileInfo( # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) # Set not needed items to zero 0. - filevers=(3, 1, 0, 0), + filevers=(3, 1, 1, 0), prodvers=(0, 0, 0, 0), # Contains a bitmask that specifies the valid bits 'flags'r mask=0x3f, @@ -31,13 +31,13 @@ VSVersionInfo( [StringStruct('Comments', 'https://github.com/DLmaster361/AUTO_MAA/'), StringStruct('CompanyName', 'AUTO_MAA Team'), StringStruct('FileDescription', 'AUTO_MAA Component'), - StringStruct('FileVersion', '3.1'), + StringStruct('FileVersion', '3.1.1'), StringStruct('InternalName', 'AUTO_MAA'), StringStruct('LegalCopyright', 'Copyright © 2024 DLmaster361'), - StringStruct('OriginalFilename', 'AUTO_MAA'), + StringStruct('OriginalFilename', 'AUTO_MAA.py'), StringStruct('ProductName', 'AUTO_MAA'), - StringStruct('ProductVersion', 'v3.1'), - StringStruct('Assembly Version', 'v3.1')]) + StringStruct('ProductVersion', 'v3.1.1'), + StringStruct('Assembly Version', 'v3.1.1')]) ]) ] ) \ No newline at end of file diff --git a/更新说明.txt b/更新说明.txt index ca24d3c..a6627e3 100644 --- a/更新说明.txt +++ b/更新说明.txt @@ -1,4 +1,5 @@ 项目初始阶段,不会提供专门的版本更新程序,您需要手动更新程序。 v2.1.5及以前的用户,由于新版本采用全新的架构,您需要手动输入之前的信息。 v3.0_Beta版用户,直接用AUTO_MAA.exe替代gui.exe后,重新设置每个用户的“自定义基建”选项(输入“-”以关闭该功能,输入自定义基建配置文件地址以开启该功能)。 +v3.1版用户,将原文件夹下的AUTO_MAA.exe文件和gui文件夹用新版本对应文件替换即可。 新用户请忽略本说明。 \ No newline at end of file