feat: 后端添加version上报
This commit is contained in:
@@ -116,6 +116,10 @@ class GlobalConfig(ConfigBase):
|
|||||||
"Update", "MirrorChyanCDK", "", EncryptValidator()
|
"Update", "MirrorChyanCDK", "", EncryptValidator()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Data_UID = ConfigItem("Data", "UID", str(uuid.uuid4()), UUIDValidator())
|
||||||
|
Data_LastStatisticsUpload = ConfigItem(
|
||||||
|
"Data", "LastStatisticsUpload", "2000-01-01 00:00:00"
|
||||||
|
)
|
||||||
Data_LastStageUpdated = ConfigItem(
|
Data_LastStageUpdated = ConfigItem(
|
||||||
"Data", "LastStageUpdated", "2000-01-01 00:00:00"
|
"Data", "LastStageUpdated", "2000-01-01 00:00:00"
|
||||||
)
|
)
|
||||||
@@ -571,7 +575,7 @@ TYPE_BOOK = {"MaaConfig": "MAA", "GeneralConfig": "通用"}
|
|||||||
|
|
||||||
class AppConfig(GlobalConfig):
|
class AppConfig(GlobalConfig):
|
||||||
|
|
||||||
VERSION = "5.0.0.1"
|
VERSION = [5, 0, 0, 1]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(if_save_multi_config=False)
|
super().__init__(if_save_multi_config=False)
|
||||||
@@ -579,7 +583,7 @@ class AppConfig(GlobalConfig):
|
|||||||
logger.info("")
|
logger.info("")
|
||||||
logger.info("===================================")
|
logger.info("===================================")
|
||||||
logger.info("AUTO_MAA 后端应用程序")
|
logger.info("AUTO_MAA 后端应用程序")
|
||||||
logger.info(f"版本号: v{self.VERSION}")
|
logger.info(f"版本号: {self.version()}")
|
||||||
logger.info(f"工作目录: {Path.cwd()}")
|
logger.info(f"工作目录: {Path.cwd()}")
|
||||||
logger.info("===================================")
|
logger.info("===================================")
|
||||||
|
|
||||||
@@ -605,6 +609,16 @@ class AppConfig(GlobalConfig):
|
|||||||
|
|
||||||
truststore.inject_into_ssl()
|
truststore.inject_into_ssl()
|
||||||
|
|
||||||
|
def version(self) -> str:
|
||||||
|
"""获取版本号字符串"""
|
||||||
|
|
||||||
|
if self.VERSION[3] == 0:
|
||||||
|
return f"v{'.'.join(str(_) for _ in self.VERSION[0:3])}"
|
||||||
|
else:
|
||||||
|
return (
|
||||||
|
f"v{'.'.join(str(_) for _ in self.VERSION[0:3])}-beta.{self.VERSION[3]}"
|
||||||
|
)
|
||||||
|
|
||||||
async def init_config(self) -> None:
|
async def init_config(self) -> None:
|
||||||
"""初始化配置管理"""
|
"""初始化配置管理"""
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import asyncio
|
|||||||
import keyboard
|
import keyboard
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from app.services import System
|
from app.services import Matomo, System
|
||||||
from app.utils import get_logger
|
from app.utils import get_logger
|
||||||
from .config import Config
|
from .config import Config
|
||||||
|
|
||||||
@@ -42,6 +42,33 @@ class _MainTimer:
|
|||||||
|
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
async def hour_task(self):
|
||||||
|
"""每小时定期任务"""
|
||||||
|
|
||||||
|
logger.info("每小时定期任务启动")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
if (
|
||||||
|
datetime.strptime(
|
||||||
|
Config.get("Data", "LastStatisticsUpload"), "%Y-%m-%d %H:%M:%S"
|
||||||
|
).date()
|
||||||
|
!= datetime.now().date()
|
||||||
|
):
|
||||||
|
await Matomo.send_event(
|
||||||
|
"App",
|
||||||
|
"Version",
|
||||||
|
Config.version(),
|
||||||
|
1 if "beta" in Config.version() else 0,
|
||||||
|
)
|
||||||
|
await Config.set(
|
||||||
|
"Data",
|
||||||
|
"LastStatisticsUpload",
|
||||||
|
datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
)
|
||||||
|
|
||||||
|
await asyncio.sleep(3600)
|
||||||
|
|
||||||
async def set_silence(self):
|
async def set_silence(self):
|
||||||
"""静默模式通过模拟老板键来隐藏模拟器窗口"""
|
"""静默模式通过模拟老板键来隐藏模拟器窗口"""
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,20 @@ class UidValidator(ConfigValidator):
|
|||||||
return value if self.validate(value) else None
|
return value if self.validate(value) else None
|
||||||
|
|
||||||
|
|
||||||
|
class UUIDValidator(ConfigValidator):
|
||||||
|
"""UUID验证器"""
|
||||||
|
|
||||||
|
def validate(self, value: Any) -> bool:
|
||||||
|
try:
|
||||||
|
uuid.UUID(value)
|
||||||
|
return True
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def correct(self, value: Any) -> Any:
|
||||||
|
return value if self.validate(value) else str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
class EncryptValidator(ConfigValidator):
|
class EncryptValidator(ConfigValidator):
|
||||||
"""加数据验证器"""
|
"""加数据验证器"""
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ __version__ = "5.0.0"
|
|||||||
__author__ = "DLmaster361 <DLmaster_361@163.com>"
|
__author__ = "DLmaster361 <DLmaster_361@163.com>"
|
||||||
__license__ = "GPL-3.0 license"
|
__license__ = "GPL-3.0 license"
|
||||||
|
|
||||||
|
from .matomo import Matomo
|
||||||
from .notification import Notify
|
from .notification import Notify
|
||||||
from .system import System
|
from .system import System
|
||||||
|
|
||||||
|
|||||||
125
app/services/matomo.py
Normal file
125
app/services/matomo.py
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
# AUTO_MAA:A MAA Multi Account Management and Automation Tool
|
||||||
|
# Copyright © 2024-2025 DLmaster361
|
||||||
|
|
||||||
|
# This file is part of AUTO_MAA.
|
||||||
|
|
||||||
|
# AUTO_MAA is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation, either version 3 of the License,
|
||||||
|
# or (at your option) any later version.
|
||||||
|
|
||||||
|
# AUTO_MAA is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||||
|
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
||||||
|
# the GNU General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with AUTO_MAA. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Contact: DLmaster_361@163.com
|
||||||
|
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import aiohttp
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
import psutil
|
||||||
|
import platform
|
||||||
|
import time
|
||||||
|
from typing import Dict, Any, Optional
|
||||||
|
|
||||||
|
from app.core import Config
|
||||||
|
from app.utils.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger("信息上报")
|
||||||
|
|
||||||
|
|
||||||
|
class _MatomoHandler:
|
||||||
|
"""Matomo统计上报服务"""
|
||||||
|
|
||||||
|
base_url = "https://statistics.auto-mas.top/matomo.php"
|
||||||
|
site_id = "3"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.session = None
|
||||||
|
|
||||||
|
async def _get_session(self):
|
||||||
|
"""获取HTTP会话"""
|
||||||
|
|
||||||
|
if self.session is None or self.session.closed:
|
||||||
|
timeout = aiohttp.ClientTimeout(total=10)
|
||||||
|
self.session = aiohttp.ClientSession(timeout=timeout)
|
||||||
|
return self.session
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
"""关闭HTTP会话"""
|
||||||
|
if self.session and not self.session.closed:
|
||||||
|
await self.session.close()
|
||||||
|
|
||||||
|
def _build_base_params(self, custom_vars: Optional[Dict[str, Any]] = None):
|
||||||
|
"""构建基础参数"""
|
||||||
|
params = {
|
||||||
|
"idsite": self.site_id,
|
||||||
|
"rec": "1",
|
||||||
|
"action_name": "AUTO-MAS后端",
|
||||||
|
"_id": Config.get("Data", "UID")[:16],
|
||||||
|
"uid": Config.get("Data", "UID"),
|
||||||
|
"rand": str(uuid.uuid4().int)[:10],
|
||||||
|
"apiv": "1",
|
||||||
|
"h": time.strftime("%H"),
|
||||||
|
"m": time.strftime("%M"),
|
||||||
|
"s": time.strftime("%S"),
|
||||||
|
"ua": f"AUTO-MAS/{Config.version()} ({platform.system()} {platform.release()})",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加自定义变量
|
||||||
|
if custom_vars is not None:
|
||||||
|
cvar = {}
|
||||||
|
for i, (key, value) in enumerate(custom_vars.items(), 1):
|
||||||
|
if i <= 5:
|
||||||
|
cvar[str(i)] = [str(key), str(value)]
|
||||||
|
if cvar:
|
||||||
|
params["_cvar"] = json.dumps(cvar)
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
async def send_event(
|
||||||
|
self,
|
||||||
|
category: str,
|
||||||
|
action: str,
|
||||||
|
name: Optional[str] = None,
|
||||||
|
value: Optional[float] = None,
|
||||||
|
custom_vars: Optional[Dict[str, Any]] = None,
|
||||||
|
):
|
||||||
|
"""发送事件数据到Matomo
|
||||||
|
|
||||||
|
Args:
|
||||||
|
category: 事件类别,如 "Script", "Config", "User"
|
||||||
|
action: 事件动作,如 "Execute", "Update", "Login"
|
||||||
|
name: 事件名称,如具体的脚本名称
|
||||||
|
value: 事件值,如执行时长、文件大小等数值
|
||||||
|
custom_vars: 自定义变量字典
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
session = await self._get_session()
|
||||||
|
if session is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
params = self._build_base_params(custom_vars)
|
||||||
|
params.update({"e_c": category, "e_a": action, "e_n": name, "e_v": value})
|
||||||
|
params = {k: v for k, v in params.items() if v is not None}
|
||||||
|
|
||||||
|
async with session.get(self.base_url, params=params) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
logger.debug(f"Matomo事件上报成功: {category}/{action}")
|
||||||
|
else:
|
||||||
|
logger.warning(f"Matomo事件上报失败: {response.status}")
|
||||||
|
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logger.warning("Matomo事件上报超时")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Matomo事件上报错误: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
Matomo = _MatomoHandler()
|
||||||
13
main.py
13
main.py
@@ -81,19 +81,26 @@ def main():
|
|||||||
await Config.init_config()
|
await Config.init_config()
|
||||||
await Config.get_stage(if_start=True)
|
await Config.get_stage(if_start=True)
|
||||||
await Config.clean_old_history()
|
await Config.clean_old_history()
|
||||||
main_timer = asyncio.create_task(MainTimer.second_task())
|
second_timer = asyncio.create_task(MainTimer.second_task())
|
||||||
|
hour_timer = asyncio.create_task(MainTimer.hour_task())
|
||||||
await System.set_Sleep()
|
await System.set_Sleep()
|
||||||
await System.set_SelfStart()
|
await System.set_SelfStart()
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
await TaskManager.stop_task("ALL")
|
await TaskManager.stop_task("ALL")
|
||||||
main_timer.cancel()
|
second_timer.cancel()
|
||||||
|
hour_timer.cancel()
|
||||||
try:
|
try:
|
||||||
await main_timer
|
await second_timer
|
||||||
|
await hour_timer
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger.info("主业务定时器已关闭")
|
logger.info("主业务定时器已关闭")
|
||||||
|
|
||||||
|
from app.services import Matomo
|
||||||
|
|
||||||
|
await Matomo.close()
|
||||||
|
|
||||||
logger.info("AUTO_MAA 后端程序关闭")
|
logger.info("AUTO_MAA 后端程序关闭")
|
||||||
|
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|||||||
@@ -3,12 +3,15 @@ name = "AUTO_MAA"
|
|||||||
version = "4.0.0.1"
|
version = "4.0.0.1"
|
||||||
description = "AUTO_MAA~"
|
description = "AUTO_MAA~"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"loguru==0.7.3",
|
"loguru==0.7.3",
|
||||||
"fastapi==0.116.1",
|
"fastapi==0.116.1",
|
||||||
"pydantic==2.11.7",
|
"pydantic==2.11.7",
|
||||||
"uvicorn==0.35.0",
|
"uvicorn==0.35.0",
|
||||||
|
"websockets==15.0.1",
|
||||||
|
"aiofiles==24.1.0",
|
||||||
|
"aiohttp==3.12.15",
|
||||||
"plyer==2.1.0",
|
"plyer==2.1.0",
|
||||||
"psutil==7.0.0",
|
"psutil==7.0.0",
|
||||||
"jinja2==3.1.6",
|
"jinja2==3.1.6",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ pydantic==2.11.7
|
|||||||
uvicorn==0.35.0
|
uvicorn==0.35.0
|
||||||
websockets==15.0.1
|
websockets==15.0.1
|
||||||
aiofiles==24.1.0
|
aiofiles==24.1.0
|
||||||
|
aiohttp==3.12.15
|
||||||
plyer==2.1.0
|
plyer==2.1.0
|
||||||
psutil==7.0.0
|
psutil==7.0.0
|
||||||
jinja2==3.1.6
|
jinja2==3.1.6
|
||||||
|
|||||||
Reference in New Issue
Block a user