Compare commits

..

16 Commits

Author SHA1 Message Date
DLmaster361
fb979e5639 docs: 补充代码签名策略 2025-06-16 21:38:25 +08:00
DLmaster361
e7d0a85ad5 ci: 修正project-slug 2025-06-16 21:24:05 +08:00
DLmaster361
a384711327 Merge branch 'dev' 2025-06-16 14:33:49 +08:00
DLmaster361
3fd4778a48 feat(maa): 适配 MAA 无Default配置情况 2025-06-16 14:26:29 +08:00
DLmaster361
4841dc09b3 refactor(res): 更新软件主页用图 2025-06-14 17:20:03 +08:00
DLmaster361
b3aa4fc776 fix(maa): 修复森空岛签到信息特定情况下不弹出通知的问题 2025-06-13 10:02:43 +08:00
DLmaster361
a9b3b8b6f4 Merge branch 'Clozy_dev' into dev 2025-06-11 23:05:59 +08:00
DLmaster361
56ef196695 refactor(models/services): 简化测试用图 2025-06-11 23:05:46 +08:00
242238d341 refactor(models/services): 优化代码结构和可读性
-移除了不必要的变量声明
-简化了图像路径的构建方式
- 统一了代码格式
2025-06-11 22:51:26 +08:00
f66f6d38fe feat(notification): 用户单独通知六星喜报 2025-06-11 22:31:51 +08:00
d58077f58b fix(app): 修复企业微信机器人图片推送异常
- 移除了不必要的变量 final_image_path
-直接使用 image_path 进行图片存在性检查
- 更新了图片 base64 和 md5计算的逻辑
2025-06-11 22:26:44 +08:00
4d4d6dbedf refactor(app): 重构图片压缩功能并优化类型注解
- 使用 Path 对象替换字符串表示文件路径,提高代码可读性和功能
- 优化类型注解,包括函数参数和返回值- 重构 ImageUtils.compress_image_if_needed 方法,简化逻辑并提高性能
- 更新 notification.py 中使用该方法的代码,直接返回压缩后的 Path 对象
2025-06-11 22:14:09 +08:00
f60b276916 refactor(notify): 重构企业微信群机器人图片推送功能
-将图片压缩、存在性检查、Base64编码和MD5计算移至 CompanyWebHookBotPushImage 方法内部
- 优化错误处理和日志记录
- 简化调用接口,提高代码可读性和维护性
2025-06-11 20:54:45 +08:00
87857fd499 feat(notification): 新增图片压缩处理
- 新增 ImageUtils.compress_image_if_needed 方法,用于压缩图片大小
- 在 MAA.py 和 notification.py 中集成图片压缩功能
- 添加对不同图片格式(JPEG、PNG)的压缩支持
- 优化图片路径处理,确保压缩后图片正确发送
- 更新 requirements.txt,添加 pillow 依赖
2025-06-11 19:50:58 +08:00
3c371cd079 feat(notification): 企业微信群机器人支持图片推送
- 新增 ImageUtils 类,提供图像处理相关工具方法
- 在 MAA.py 中集成 ImageUtils,用于获取和处理通知图片
- 在 notification.py 中实现 CompanyWebHookBotPushImage 方法,支持企业微信群机器人推送图片
- 修改测试通知方法,增加图片推送测试
2025-06-11 17:36:11 +08:00
DLmaster361
428b849bcc fix(maa): 静默模式控制时段延长至模拟器完成启动的10s后 2025-06-11 01:13:57 +08:00
13 changed files with 247 additions and 31 deletions

View File

@@ -107,8 +107,8 @@ jobs:
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '787a1d5f-6177-4f30-9559-d2646473584a'
project-slug: 'AUTO_MAA_'
signing-policy-slug: 'test-signing'
project-slug: 'AUTO_MAA'
signing-policy-slug: 'release-signing'
artifact-configuration-slug: "AUTO_MAA"
github-artifact-id: '${{ steps.upload-unsigned-main-program.outputs.artifact-id }}'
wait-for-completion: true
@@ -155,8 +155,8 @@ jobs:
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '787a1d5f-6177-4f30-9559-d2646473584a'
project-slug: 'AUTO_MAA_'
signing-policy-slug: 'test-signing'
project-slug: 'AUTO_MAA'
signing-policy-slug: 'release-signing'
artifact-configuration-slug: "AUTO_MAA-Setup"
github-artifact-id: '${{ steps.upload-unsigned-setup-program.outputs.artifact-id }}'
wait-for-completion: true
@@ -243,7 +243,19 @@ jobs:
TAGNAME="$(sed 's/\r$//g' <(head -n 1 version_info.txt))"
NOTES_MAIN="$(sed 's/\r$//g' <(tail -n +3 version_info.txt))"
NOTES="$NOTES_MAIN
## 代码签名策略Code signing policy
Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
- 审批人Approvers: [DLmaster (@DLmaster361)](https://github.com/DLmaster361)
## 隐私政策Privacy policy
除非用户、安装者或使用者特别要求,否则本程序不会将任何信息传输到其他网络系统。
This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it.
[已有 Mirror酱 CDK ?前往 Mirror酱 高速下载](https://mirrorchyan.com/zh/projects?rid=AUTO_MAA&source=auto_maa-release)
\`\`\`本release通过GitHub Actions自动构建\`\`\`"

View File

@@ -52,13 +52,10 @@
- **传播:** AUTO_MAA原则上允许传播者自由传播本软件但无论在何种传播过程中不得删除项目作者与开发者所留版权声明不得隐瞒项目作者与相关开发者的存在。由于软件性质项目组不希望发现任何人在明日方舟官方媒体包括官方媒体账号与森空岛社区等或明日方舟游戏相关内容包括同好群、线下活动与游戏内容讨论等下提及AUTO_MAA或MAA希望各位理解。
- **衍生:** AUTO_MAA允许任何人对软件本体或软件部分代码进行二次开发或利用。但依据GPL相关成果再次分发时也必须使用GPL或兼容的协议开源。
- **贡献:** 不论是直接参与软件的维护编写或是撰写文档、测试、反馈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上提及的项目组拥有最终解释权。
**注意**
- 由于本软件有修改其它目录JSON文件等行为使用前请将AUTO_MAA添加入Windows Defender信任区以及防病毒软件的信任区或开发者目录避免被误杀。
---
# 使用方法
@@ -75,11 +72,23 @@
可在[《AUTO_MAA开发者协作文档》](https://docs.qq.com/aio/DQ3Z5eHNxdmxFQmZX)的`开发任务`页面中查看开发进度。
## 代码签名策略Code signing policy
Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
- 审批人Approvers: [DLmaster (@DLmaster361)](https://github.com/DLmaster361)
## 隐私政策Privacy policy
除非用户、安装者或使用者特别要求,否则本程序不会将任何信息传输到其他网络系统。
This program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it.
## 特别鸣谢
- 下载服务器:由[AoXuan (@ClozyA)](https://github.com/ClozyA) 个人为项目赞助。
- EXE签名: Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/)
- EXE签名: [SignPath.io](https://signpath.io/)提供免费代码签名,签名来自[SignPath Foundation](https://signpath.org/)
## 贡献者

View File

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

View File

@@ -96,17 +96,9 @@ class _MainTimer(QObject):
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(
str(emulator_path) in window
str(emulator_path) in window and window[0] != "新通知"
for window in windows
for emulator_path in Config.silence_list
):

View File

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

View File

@@ -32,6 +32,7 @@ from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr
from pathlib import Path
import requests
from PySide6.QtCore import QObject, Signal
@@ -40,6 +41,7 @@ from plyer import notification
from app.core import Config
from app.services.security import Crypto
from app.utils.ImageUtils import ImageUtils
class Notification(QObject):
@@ -271,6 +273,100 @@ class Notification(QObject):
)
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):
"""发送测试通知到所有已启用的通知渠道"""
# 发送系统通知
@@ -307,6 +403,10 @@ class Notification(QObject):
"这是 AUTO_MAA 外部通知测试信息。如果你看到了这段内容,说明 AUTO_MAA 的通知功能已经正确配置且可以正常工作!",
Config.get(Config.notify_CompanyWebHookBotUrl),
)
Notify.CompanyWebHookBotPushImage(
Config.app_path / "resources/images/notification/test_notify.png",
Config.get(Config.notify_CompanyWebHookBotUrl),
)
return True

View File

@@ -153,7 +153,9 @@ def skland_sign_in(token) -> dict:
headers=header_login,
).json()
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"]
# 获取已绑定的角色列表

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