Merge branch 'Clozy_dev' into dev
This commit is contained in:
@@ -40,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):
|
||||
@@ -1998,6 +1999,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"]:
|
||||
@@ -2040,8 +2045,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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
67
app/utils/ImageUtils.py
Normal file
67
app/utils/ImageUtils.py
Normal 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
|
||||
@@ -10,3 +10,4 @@ requests
|
||||
markdown
|
||||
Jinja2
|
||||
nuitka
|
||||
pillow
|
||||
BIN
resources/images/notification/six_star.png
Normal file
BIN
resources/images/notification/six_star.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 222 KiB |
BIN
resources/images/notification/test_notify.png
Normal file
BIN
resources/images/notification/test_notify.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Reference in New Issue
Block a user