Merge branch 'dev'

This commit is contained in:
DLmaster361
2025-06-16 14:33:49 +08:00
13 changed files with 219 additions and 27 deletions

View File

@@ -108,7 +108,7 @@ jobs:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}' api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '787a1d5f-6177-4f30-9559-d2646473584a' organization-id: '787a1d5f-6177-4f30-9559-d2646473584a'
project-slug: 'AUTO_MAA_' project-slug: 'AUTO_MAA_'
signing-policy-slug: 'test-signing' signing-policy-slug: 'release-signing'
artifact-configuration-slug: "AUTO_MAA" artifact-configuration-slug: "AUTO_MAA"
github-artifact-id: '${{ steps.upload-unsigned-main-program.outputs.artifact-id }}' github-artifact-id: '${{ steps.upload-unsigned-main-program.outputs.artifact-id }}'
wait-for-completion: true wait-for-completion: true
@@ -156,7 +156,7 @@ jobs:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}' api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '787a1d5f-6177-4f30-9559-d2646473584a' organization-id: '787a1d5f-6177-4f30-9559-d2646473584a'
project-slug: 'AUTO_MAA_' project-slug: 'AUTO_MAA_'
signing-policy-slug: 'test-signing' signing-policy-slug: 'release-signing'
artifact-configuration-slug: "AUTO_MAA-Setup" artifact-configuration-slug: "AUTO_MAA-Setup"
github-artifact-id: '${{ steps.upload-unsigned-setup-program.outputs.artifact-id }}' github-artifact-id: '${{ steps.upload-unsigned-setup-program.outputs.artifact-id }}'
wait-for-completion: true wait-for-completion: true

View File

@@ -52,13 +52,10 @@
- **传播:** AUTO_MAA原则上允许传播者自由传播本软件但无论在何种传播过程中不得删除项目作者与开发者所留版权声明不得隐瞒项目作者与相关开发者的存在。由于软件性质项目组不希望发现任何人在明日方舟官方媒体包括官方媒体账号与森空岛社区等或明日方舟游戏相关内容包括同好群、线下活动与游戏内容讨论等下提及AUTO_MAA或MAA希望各位理解。 - **传播:** AUTO_MAA原则上允许传播者自由传播本软件但无论在何种传播过程中不得删除项目作者与开发者所留版权声明不得隐瞒项目作者与相关开发者的存在。由于软件性质项目组不希望发现任何人在明日方舟官方媒体包括官方媒体账号与森空岛社区等或明日方舟游戏相关内容包括同好群、线下活动与游戏内容讨论等下提及AUTO_MAA或MAA希望各位理解。
- **衍生:** AUTO_MAA允许任何人对软件本体或软件部分代码进行二次开发或利用。但依据GPL相关成果再次分发时也必须使用GPL或兼容的协议开源。 - **衍生:** AUTO_MAA允许任何人对软件本体或软件部分代码进行二次开发或利用。但依据GPL相关成果再次分发时也必须使用GPL或兼容的协议开源。
- **贡献:** 不论是直接参与软件的维护编写或是撰写文档、测试、反馈BUG、给出建议、参与讨论都为AUTO_MAA项目的发展完善做出了不可忽视的贡献。项目组提倡各位贡献者遵照GitHub开源社区惯例发布Issues参与项目。避免私信或私发邮件安全性漏洞或敏感问题除外以帮助更多用户。 - **贡献:** 不论是直接参与软件的维护编写或是撰写文档、测试、反馈BUG、给出建议、参与讨论都为AUTO_MAA项目的发展完善做出了不可忽视的贡献。项目组提倡各位贡献者遵照GitHub开源社区惯例发布Issues参与项目。避免私信或私发邮件安全性漏洞或敏感问题除外以帮助更多用户。
- **图像:** `AUTO_MAA主页默认图像` 并不适用开源协议,著作权归 [NARINpopo](https://space.bilibili.com/1877154) 画师所有,商业使用权归 [DLmaster (@DLmaster361)](https://github.com/DLmaster361) 所有,软件用户仅拥有非商业使用权。不得以开源协议已授权为由在未经授权的情况下使用 `AUTO_MAA主页默认图像`,不得在未经授权的情况下将 `AUTO_MAA主页默认图像` 用于任何商业用途。
以上细则是本项目对GPL的相关补充与强调。未提及的以GPL为准发生冲突的以本细则为准。如有不清楚的部分请发Issues询问。若发生纠纷相关内容也没有在Issues上提及的项目组拥有最终解释权。 以上细则是本项目对GPL的相关补充与强调。未提及的以GPL为准发生冲突的以本细则为准。如有不清楚的部分请发Issues询问。若发生纠纷相关内容也没有在Issues上提及的项目组拥有最终解释权。
**注意**
- 由于本软件有修改其它目录JSON文件等行为使用前请将AUTO_MAA添加入Windows Defender信任区以及防病毒软件的信任区或开发者目录避免被误杀。
--- ---
# 使用方法 # 使用方法

View File

@@ -576,7 +576,7 @@ class MaaPlanConfig(LQConfig):
class AppConfig(GlobalConfig): class AppConfig(GlobalConfig):
VERSION = "4.3.10.3" VERSION = "4.3.10.0"
gameid_refreshed = Signal() gameid_refreshed = Signal()
PASSWORD_refreshed = Signal() PASSWORD_refreshed = Signal()

View File

@@ -96,17 +96,9 @@ class _MainTimer(QObject):
windows = System.get_window_info() windows = System.get_window_info()
# 排除雷电名为新通知的窗口 # 此处排除雷电名为新通知的窗口
windows = [
window
for window in windows
if not (
window[0] == "新通知" and Path(window[1]) in Config.silence_list
)
]
if any( if any(
str(emulator_path) in window str(emulator_path) in window and window[0] != "新通知"
for window in windows for window in windows
for emulator_path in Config.silence_list for emulator_path in Config.silence_list
): ):

View File

@@ -32,6 +32,7 @@ import subprocess
import shutil import shutil
import re import re
import win32com.client import win32com.client
from functools import partial
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
@@ -39,6 +40,7 @@ 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, Crypto, System, skland_sign_in from app.services import Notify, Crypto, System, skland_sign_in
from app.utils.ImageUtils import ImageUtils
class MaaManager(QObject): class MaaManager(QObject):
@@ -242,9 +244,17 @@ class MaaManager(QObject):
"info", "info",
f"森空岛签到{type}", f"森空岛签到{type}",
"".join(user_list), "".join(user_list),
-1, -1 if type == "失败" else 5000,
) )
if skland_result["总计"] == 0:
self.push_info_bar.emit(
"info",
"森空岛签到失败",
user[0],
-1,
)
if ( if (
skland_result["总计"] > 0 skland_result["总计"] > 0
and len(skland_result["失败"]) == 0 and len(skland_result["失败"]) == 0
@@ -959,8 +969,10 @@ class MaaManager(QObject):
if self.isInterruptionRequested: if self.isInterruptionRequested:
return None return None
# 移除静默进程标记 # 10s后移除静默进程标记
Config.silence_list.remove(self.emulator_path) QTimer.singleShot(
10000, partial(Config.silence_list.remove, self.emulator_path)
)
if "-" in self.ADB_address: if "-" in self.ADB_address:
ADB_ip = f"{self.ADB_address.split("-")[0]}-" ADB_ip = f"{self.ADB_address.split("-")[0]}-"
@@ -1269,10 +1281,15 @@ class MaaManager(QObject):
else: else:
self.agree_bilibili(False) self.agree_bilibili(False)
# 切换配置
if data["Current"] != "Default":
data["Configurations"]["Default"] = data["Configurations"][data["Current"]]
data["Current"] = "Default"
# 自动代理配置 # 自动代理配置
if "自动代理" in mode: if "自动代理" in mode:
data["Current"] = "Default" # 切换配置
for i in range(1, 9): for i in range(1, 9):
data["Global"][f"Timer.Timer{i}"] = "False" # 时间设置 data["Global"][f"Timer.Timer{i}"] = "False" # 时间设置
@@ -1578,7 +1595,6 @@ class MaaManager(QObject):
# 人工排查配置 # 人工排查配置
elif "人工排查" in mode: elif "人工排查" in mode:
data["Current"] = "Default" # 切换配置
for i in range(1, 9): for i in range(1, 9):
data["Global"][f"Timer.Timer{i}"] = "False" # 时间设置 data["Global"][f"Timer.Timer{i}"] = "False" # 时间设置
data["Configurations"]["Default"][ data["Configurations"]["Default"][
@@ -1651,7 +1667,6 @@ class MaaManager(QObject):
# 设置MAA配置 # 设置MAA配置
elif "设置MAA" in mode: elif "设置MAA" in mode:
data["Current"] = "Default" # 切换配置
for i in range(1, 9): for i in range(1, 9):
data["Global"][f"Timer.Timer{i}"] = "False" # 时间设置 data["Global"][f"Timer.Timer{i}"] = "False" # 时间设置
data["Configurations"]["Default"][ data["Configurations"]["Default"][
@@ -1707,7 +1722,6 @@ class MaaManager(QObject):
elif mode == "更新MAA": elif mode == "更新MAA":
data["Current"] = "Default" # 切换配置
for i in range(1, 9): for i in range(1, 9):
data["Global"][f"Timer.Timer{i}"] = "False" # 时间设置 data["Global"][f"Timer.Timer{i}"] = "False" # 时间设置
data["Configurations"]["Default"][ data["Configurations"]["Default"][
@@ -1995,6 +2009,10 @@ class MaaManager(QObject):
"好羡慕~\n\nAUTO_MAA 敬上", "好羡慕~\n\nAUTO_MAA 敬上",
Config.get(Config.notify_CompanyWebHookBotUrl), Config.get(Config.notify_CompanyWebHookBotUrl),
) )
Notify.CompanyWebHookBotPushImage(
Config.app_path / "resources/images/notification/six_star.png",
Config.get(Config.notify_CompanyWebHookBotUrl),
)
# 发送用户单独通知 # 发送用户单独通知
if user_data["Notify"]["Enabled"] and user_data["Notify"]["IfSendSixStar"]: if user_data["Notify"]["Enabled"] and user_data["Notify"]["IfSendSixStar"]:
@@ -2037,8 +2055,14 @@ class MaaManager(QObject):
"好羡慕~\n\nAUTO_MAA 敬上", "好羡慕~\n\nAUTO_MAA 敬上",
user_data["Notify"]["CompanyWebHookBotUrl"], user_data["Notify"]["CompanyWebHookBotUrl"],
) )
Notify.CompanyWebHookBotPushImage(
Config.app_path
/ "resources/images/notification/six_star.png",
Config.get(Config.notify_CompanyWebHookBotUrl),
)
else: else:
logger.error( logger.error(
f"{self.name} |用户CompanyWebHookBot密钥为空无法发送用户单独的CompanyWebHookBot通知" f"{self.name} |用户CompanyWebHookBot密钥为空无法发送用户单独的CompanyWebHookBot通知"
) )
return None return None

View File

@@ -32,6 +32,7 @@ from email.header import Header
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from email.mime.text import MIMEText
from email.utils import formataddr from email.utils import formataddr
from pathlib import Path
import requests import requests
from PySide6.QtCore import QObject, Signal from PySide6.QtCore import QObject, Signal
@@ -40,6 +41,7 @@ from plyer import notification
from app.core import Config from app.core import Config
from app.services.security import Crypto from app.services.security import Crypto
from app.utils.ImageUtils import ImageUtils
class Notification(QObject): class Notification(QObject):
@@ -271,6 +273,100 @@ class Notification(QObject):
) )
return f"使用企业微信群机器人推送通知时出错:{err}" return f"使用企业微信群机器人推送通知时出错:{err}"
def CompanyWebHookBotPushImage(self, image_path: Path, webhook_url: str) -> bool:
"""使用企业微信群机器人推送图片通知"""
try:
# 压缩图片
ImageUtils.compress_image_if_needed(image_path)
# 检查图片是否存在
if not image_path.exists():
logger.error(
"图片推送异常 | 图片不存在或者压缩失败,请检查图片路径是否正确"
)
self.push_info_bar.emit(
"error",
"企业微信群机器人通知推送异常",
"图片不存在或者压缩失败,请检查图片路径是否正确",
-1,
)
return False
if not webhook_url:
logger.error("请正确设置企业微信群机器人的WebHook地址")
self.push_info_bar.emit(
"error",
"企业微信群机器人通知推送异常",
"请正确设置企业微信群机器人的WebHook地址",
-1,
)
return False
# 获取图片base64和md5
try:
image_base64 = ImageUtils.get_base64_from_file(str(image_path))
image_md5 = ImageUtils.calculate_md5_from_file(str(image_path))
except Exception as e:
logger.error(f"图片编码或MD5计算失败{e}")
self.push_info_bar.emit(
"error",
"企业微信群机器人通知推送异常",
f"图片编码或MD5计算失败{e}",
-1,
)
return False
data = {
"msgtype": "image",
"image": {"base64": image_base64, "md5": image_md5},
}
for _ in range(3):
try:
response = requests.post(
url=webhook_url,
json=data,
timeout=10,
)
info = response.json()
break
except requests.RequestException as e:
err = e
logger.warning(f"推送企业微信群机器人图片第{_+1}次失败:{e}")
time.sleep(0.1)
else:
logger.error(f"推送企业微信群机器人图片时出错:{err}")
self.push_info_bar.emit(
"error",
"企业微信群机器人图片推送失败",
f"使用企业微信群机器人推送图片时出错:{err}",
-1,
)
return False
if info.get("errcode") == 0:
logger.info("企业微信群机器人推送图片成功")
return True
else:
logger.error(f"企业微信群机器人推送图片失败:{info}")
self.push_info_bar.emit(
"error",
"企业微信群机器人图片推送失败",
f"使用企业微信群机器人推送图片时出错:{info}",
-1,
)
return False
except Exception as e:
logger.error(f"推送企业微信群机器人图片时发生未知异常:{e}")
self.push_info_bar.emit(
"error",
"企业微信群机器人图片推送失败",
f"发生未知异常:{e}",
-1,
)
return False
def send_test_notification(self): def send_test_notification(self):
"""发送测试通知到所有已启用的通知渠道""" """发送测试通知到所有已启用的通知渠道"""
# 发送系统通知 # 发送系统通知
@@ -307,6 +403,10 @@ class Notification(QObject):
"这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!", "这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!",
Config.get(Config.notify_CompanyWebHookBotUrl), Config.get(Config.notify_CompanyWebHookBotUrl),
) )
Notify.CompanyWebHookBotPushImage(
Config.app_path / "resources/images/notification/test_notify.png",
Config.get(Config.notify_CompanyWebHookBotUrl),
)
return True return True

View File

@@ -153,7 +153,9 @@ def skland_sign_in(token) -> dict:
headers=header_login, headers=header_login,
).json() ).json()
if rsp["status"] != 0: if rsp["status"] != 0:
raise Exception(f'使用token: {token} 获得认证代码失败:{rsp.get("msg")}') raise Exception(
f'使用token: {token[:3]}******{token[-3:]} 获得认证代码失败:{rsp.get("msg")}'
)
return rsp["data"]["code"] return rsp["data"]["code"]
# 获取已绑定的角色列表 # 获取已绑定的角色列表

67
app/utils/ImageUtils.py Normal file
View File

@@ -0,0 +1,67 @@
import base64
import hashlib
from pathlib import Path
from PIL import Image
class ImageUtils:
@staticmethod
def get_base64_from_file(image_path):
"""从本地文件读取并返回base64编码字符串"""
with open(image_path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
@staticmethod
def calculate_md5_from_file(image_path):
"""从本地文件读取并返回md5值hex字符串"""
with open(image_path, "rb") as f:
return hashlib.md5(f.read()).hexdigest()
@staticmethod
def calculate_md5_from_base64(base64_content):
"""从base64字符串计算md5"""
image_data = base64.b64decode(base64_content)
return hashlib.md5(image_data).hexdigest()
@staticmethod
def compress_image_if_needed(image_path: Path, max_size_mb=2) -> Path:
"""
如果图片大于max_size_mb则压缩并覆盖原文件返回原始路径Path对象
"""
if hasattr(Image, "Resampling"): # Pillow 9.1.0及以后
RESAMPLE = Image.Resampling.LANCZOS
else:
RESAMPLE = Image.ANTIALIAS
max_size = max_size_mb * 1024 * 1024
if image_path.stat().st_size <= max_size:
return image_path
img = Image.open(image_path)
suffix = image_path.suffix.lower()
quality = 90 if suffix in [".jpg", ".jpeg"] else None
step = 5
if suffix in [".jpg", ".jpeg"]:
while True:
img.save(image_path, quality=quality, optimize=True)
if image_path.stat().st_size <= max_size or quality <= 10:
break
quality -= step
elif suffix == ".png":
width, height = img.size
while True:
img.save(image_path, optimize=True)
if (
image_path.stat().st_size <= max_size
or width <= 200
or height <= 200
):
break
width = int(width * 0.95)
height = int(height * 0.95)
img = img.resize((width, height), RESAMPLE)
else:
raise ValueError("仅支持JPG/JPEG和PNG格式图片的压缩。")
return image_path

View File

@@ -9,4 +9,5 @@ pycryptodome
requests requests
markdown markdown
Jinja2 Jinja2
nuitka nuitka
pillow

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,6 +1,15 @@
{ {
"main_version": "4.3.10.3", "main_version": "4.3.10.0",
"version_info": { "version_info": {
"4.3.10.0": {
"新增功能": [
"更换全新默认主页图",
"适配 MAA 无`Default`配置情况 #52"
],
"程序优化": [
"静默模式控制时段延长至模拟器完成启动的10s后"
]
},
"4.3.10.3": { "4.3.10.3": {
"程序优化": [ "程序优化": [
"使用 keyboard 模块替代 pyautogui 模块" "使用 keyboard 模块替代 pyautogui 模块"