388 lines
14 KiB
Python
388 lines
14 KiB
Python
import os
|
||
import json
|
||
import time
|
||
import requests
|
||
import logging
|
||
from datetime import datetime, timedelta
|
||
from threading import Lock
|
||
import signal
|
||
import sys
|
||
import threading
|
||
|
||
# 配置日志
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||
handlers=[
|
||
logging.FileHandler('inspect_monitor.log'),
|
||
logging.StreamHandler()
|
||
]
|
||
)
|
||
|
||
# 增加log函数,兼容自定义日志调用
|
||
def log(message):
|
||
try:
|
||
logging.info(f"[INSPECT] {message}")
|
||
except Exception as e:
|
||
print(f"日志记录失败: {str(e)}")
|
||
print(f"原始消息: {message}")
|
||
|
||
# 常量定义
|
||
INSPECT_API_URL = "https://api.cc.163.com/v1/inspectapi/forumtrack/tracklog/searchtracklog"
|
||
COEFFICIENT = 1.5 # CC审核平台的系数
|
||
HOURLY_DATA_FILE = "inspect_hourly.json"
|
||
DAILY_DATA_FILE = "inspect_daily.json"
|
||
COEFFICIENTS_CONFIG_FILE = "inspect_coefficients.json"
|
||
DEFAULT_COEFFICIENTS = {"default": 1.5}
|
||
COEFFICIENTS = DEFAULT_COEFFICIENTS.copy()
|
||
coefficients_lock = Lock()
|
||
|
||
def handle_exit(signum, frame):
|
||
logging.info(f"收到退出信号({signum}),即将退出")
|
||
sys.exit(0)
|
||
|
||
signal.signal(signal.SIGTERM, handle_exit)
|
||
signal.signal(signal.SIGINT, handle_exit)
|
||
|
||
def get_inspect_stats(username, cookie, start_time, end_time):
|
||
"""获取CC审核平台的统计数据"""
|
||
try:
|
||
# 每次统计前主动加载系数,确保用到最新
|
||
load_coefficients()
|
||
# 准备请求数据
|
||
data = {
|
||
"data": {
|
||
"form": {
|
||
"operator": f"{username.lower()}@mail.go",
|
||
"startTime": start_time,
|
||
"endTime": end_time,
|
||
"tid": "",
|
||
"pid": ""
|
||
},
|
||
"page": 1,
|
||
"pageSize": 500
|
||
}
|
||
}
|
||
|
||
# 设置请求头
|
||
headers = {
|
||
"accept": "application/json, text/plain, */*",
|
||
"content-type": "text/plain",
|
||
"cookie": cookie,
|
||
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
|
||
}
|
||
|
||
# 发送请求
|
||
response = requests.post(INSPECT_API_URL, headers=headers, json=data)
|
||
response.raise_for_status()
|
||
|
||
# 解析响应
|
||
result = response.json()
|
||
if result.get("code") == "OK":
|
||
total = result.get("data", {}).get("result", {}).get("total", 0)
|
||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||
|
||
# 使用从在线链接获取的系数
|
||
with coefficients_lock:
|
||
current_coefficient = COEFFICIENTS.get('default', 1.5)
|
||
|
||
return {
|
||
"total": total,
|
||
"weighted_total": total * current_coefficient,
|
||
"timestamp": int(time.time()),
|
||
"update_time": current_time
|
||
}
|
||
else:
|
||
logging.error(f"获取CC审核平台数据失败: {result.get('msg')}")
|
||
return None
|
||
|
||
except Exception as e:
|
||
logging.error(f"获取CC审核平台数据时发生错误: {str(e)}")
|
||
return None
|
||
|
||
def save_stats(stats, file_path):
|
||
"""保存统计数据到文件"""
|
||
try:
|
||
with open(file_path, 'w') as f:
|
||
json.dump(stats, f)
|
||
except Exception as e:
|
||
logging.error(f"保存统计数据时发生错误: {str(e)}")
|
||
|
||
def load_stats(file_path):
|
||
"""从文件加载统计数据"""
|
||
try:
|
||
if os.path.exists(file_path):
|
||
with open(file_path, 'r') as f:
|
||
return json.load(f)
|
||
except Exception as e:
|
||
logging.error(f"加载统计数据时发生错误: {str(e)}")
|
||
return {"stats": []}
|
||
|
||
def update_hourly_stats(username, cookie):
|
||
"""更新小时统计数据"""
|
||
now = datetime.now()
|
||
start_time = now.strftime("%Y/%m/%d %H:00:00")
|
||
end_time = now.strftime("%Y/%m/%d %H:59:59")
|
||
|
||
stats = get_inspect_stats(username, cookie, start_time, end_time)
|
||
if stats:
|
||
hourly_data = load_stats(HOURLY_DATA_FILE)
|
||
|
||
# 移除超过24小时的数据
|
||
current_time = int(time.time())
|
||
hourly_data["stats"] = [
|
||
stat for stat in hourly_data["stats"]
|
||
if current_time - stat["timestamp"] <= 24 * 3600
|
||
]
|
||
|
||
# 添加新数据
|
||
hourly_data["stats"].append(stats)
|
||
save_stats(hourly_data, HOURLY_DATA_FILE)
|
||
logging.info(f"成功更新小时统计数据: {stats}")
|
||
|
||
def update_daily_stats(username, cookie):
|
||
"""更新日统计数据"""
|
||
now = datetime.now()
|
||
start_time = now.strftime("%Y/%m/%d 00:00:00")
|
||
end_time = now.strftime("%Y/%m/%d 23:59:59")
|
||
|
||
stats = get_inspect_stats(username, cookie, start_time, end_time)
|
||
if stats:
|
||
daily_data = load_stats(DAILY_DATA_FILE)
|
||
|
||
# 移除超过7天的数据
|
||
current_time = int(time.time())
|
||
daily_data["stats"] = [
|
||
stat for stat in daily_data["stats"]
|
||
if current_time - stat["timestamp"] <= 7 * 24 * 3600
|
||
]
|
||
|
||
# 添加新数据
|
||
daily_data["stats"].append(stats)
|
||
save_stats(daily_data, DAILY_DATA_FILE)
|
||
logging.info(f"成功更新日统计数据: {stats}")
|
||
|
||
def fetch_and_save_coefficients(url):
|
||
"""从在线服务器获取系数配置并保存到本地"""
|
||
max_retries = 3
|
||
retry_delay = 5 # 重试间隔(秒)
|
||
|
||
for attempt in range(max_retries):
|
||
try:
|
||
# 尝试不使用代理直接获取
|
||
try:
|
||
response = requests.get(url, timeout=10, proxies={'http': None, 'https': None}, verify=False)
|
||
except:
|
||
# 如果直接获取失败,尝试使用系统代理
|
||
proxies = {
|
||
'http': os.environ.get('HTTP_PROXY', ''),
|
||
'https': os.environ.get('HTTPS_PROXY', '')
|
||
}
|
||
response = requests.get(url, timeout=10, proxies=proxies, verify=False)
|
||
|
||
if response.status_code == 200:
|
||
loaded_coefficients = response.json()
|
||
# 更新系数
|
||
COEFFICIENTS.update(loaded_coefficients)
|
||
# 保存到本地文件
|
||
with open(COEFFICIENTS_CONFIG_FILE, 'w', encoding='utf-8') as f:
|
||
json.dump(loaded_coefficients, f, indent=4, ensure_ascii=False)
|
||
log(f"从服务器获取系数: {loaded_coefficients}")
|
||
return True
|
||
else:
|
||
log(f"获取在线系数配置失败,HTTP状态码: {response.status_code}")
|
||
if attempt < max_retries - 1:
|
||
log(f"第{attempt + 1}次重试失败,{retry_delay}秒后重试...")
|
||
time.sleep(retry_delay)
|
||
continue
|
||
return False
|
||
except Exception as e:
|
||
log(f"获取在线系数配置时发生错误: {str(e)}")
|
||
if attempt < max_retries - 1:
|
||
log(f"第{attempt + 1}次重试失败,{retry_delay}秒后重试...")
|
||
time.sleep(retry_delay)
|
||
continue
|
||
return False
|
||
|
||
def load_coefficients():
|
||
"""从在线链接获取系数配置,如果获取失败则使用默认配置"""
|
||
global COEFFICIENTS
|
||
try:
|
||
with coefficients_lock:
|
||
url = "http://scripts.ui-beam.com:5000/NetEaseDSMonitor/config/inspect_coefficients.json"
|
||
|
||
# 检查本地文件是否存在
|
||
local_config_exists = os.path.exists(COEFFICIENTS_CONFIG_FILE)
|
||
|
||
# 如果是首次运行或需要强制更新
|
||
if not local_config_exists:
|
||
log("首次运行,从在线服务器获取系数配置")
|
||
if not fetch_and_save_coefficients(url):
|
||
log("首次获取系数配置失败,使用默认配置")
|
||
COEFFICIENTS = DEFAULT_COEFFICIENTS.copy()
|
||
return
|
||
|
||
# 读取本地配置
|
||
try:
|
||
with open(COEFFICIENTS_CONFIG_FILE, 'r', encoding='utf-8') as f:
|
||
local_coefficients = json.load(f)
|
||
except Exception as e:
|
||
log(f"读取本地系数配置失败: {str(e)}")
|
||
local_coefficients = None
|
||
|
||
# 尝试获取在线配置
|
||
try:
|
||
response = requests.get(url, timeout=10, proxies={'http': None, 'https': None}, verify=False)
|
||
if response.status_code == 200:
|
||
online_coefficients = response.json()
|
||
|
||
# 比较配置是否发生变化
|
||
if local_coefficients != online_coefficients:
|
||
log("检测到系数配置发生变化,更新本地配置")
|
||
if fetch_and_save_coefficients(url):
|
||
log("系数配置更新成功")
|
||
else:
|
||
log("系数配置更新失败,继续使用本地配置")
|
||
else:
|
||
log("系数配置未发生变化,使用本地配置")
|
||
else:
|
||
log(f"获取在线系数配置失败,HTTP状态码: {response.status_code}")
|
||
except Exception as e:
|
||
log(f"检查在线系数配置时发生错误: {str(e)}")
|
||
|
||
# 使用本地配置
|
||
if local_coefficients:
|
||
COEFFICIENTS.update(local_coefficients)
|
||
log(f"从本地从加载系数: {local_coefficients}")
|
||
else:
|
||
COEFFICIENTS = DEFAULT_COEFFICIENTS.copy()
|
||
|
||
except Exception as e:
|
||
log(f"加载系数配置失败: {str(e)}")
|
||
COEFFICIENTS = DEFAULT_COEFFICIENTS.copy()
|
||
|
||
def monitor_config_thread():
|
||
"""监控配置文件变化线程"""
|
||
log("配置监控线程启动")
|
||
last_check_time = 0
|
||
check_interval = 1800 # 30分钟
|
||
|
||
while True:
|
||
try:
|
||
current_time = time.time()
|
||
|
||
# 检查是否需要更新配置
|
||
if current_time - last_check_time >= check_interval:
|
||
log("开始检查系数配置更新")
|
||
load_coefficients()
|
||
last_check_time = current_time
|
||
|
||
time.sleep(60) # 每分钟检查一次时间
|
||
|
||
except Exception as e:
|
||
log(f"配置监控线程异常: {str(e)}")
|
||
time.sleep(60) # 发生异常时等待1分钟后重试
|
||
|
||
def send_global_message(message):
|
||
"""发送全局消息提示"""
|
||
try:
|
||
# 将消息写入到全局消息文件
|
||
message_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'global_messages.json')
|
||
messages = []
|
||
if os.path.exists(message_file):
|
||
with open(message_file, 'r', encoding='utf-8') as f:
|
||
messages = json.load(f)
|
||
|
||
# 添加新消息
|
||
messages.append({
|
||
'timestamp': int(time.time()),
|
||
'message': message,
|
||
'type': 'error'
|
||
})
|
||
|
||
# 只保留最近100条消息
|
||
if len(messages) > 100:
|
||
messages = messages[-100:]
|
||
|
||
# 保存消息
|
||
with open(message_file, 'w', encoding='utf-8') as f:
|
||
json.dump(messages, f, ensure_ascii=False)
|
||
except Exception as e:
|
||
log(f"发送全局消息失败: {str(e)}")
|
||
|
||
def main():
|
||
"""主函数"""
|
||
logging.info("CC审核平台监控系统启动")
|
||
|
||
# 解析命令行参数
|
||
check_now = False
|
||
for arg in sys.argv:
|
||
if arg == "--check-now":
|
||
check_now = True
|
||
logging.info("收到立即检查参数")
|
||
|
||
# 从环境变量获取凭据
|
||
username = os.environ.get('INSPECT_USERNAME')
|
||
cookie = os.environ.get('INSPECT_COOKIE')
|
||
if not username or not cookie:
|
||
logging.error("未设置CC审核平台凭据")
|
||
return
|
||
|
||
if check_now:
|
||
logging.info("开始执行手动检查")
|
||
update_hourly_stats(username, cookie)
|
||
logging.info("手动检查完成")
|
||
sys.exit(0)
|
||
|
||
# 启动小时和日数据监控线程
|
||
def hourly_update_thread():
|
||
while True:
|
||
try:
|
||
update_hourly_stats(username, cookie)
|
||
logging.info("已完成小时统计数据更新")
|
||
time.sleep(120)
|
||
except Exception as e:
|
||
logging.error(f"小时数据更新线程错误: {str(e)}")
|
||
time.sleep(60)
|
||
|
||
def daily_update_thread():
|
||
last_daily_check = None
|
||
while True:
|
||
try:
|
||
now = datetime.now()
|
||
if last_daily_check is None or (now - last_daily_check).total_seconds() >= 3600:
|
||
update_daily_stats(username, cookie)
|
||
last_daily_check = now
|
||
logging.info("已完成日统计数据更新")
|
||
time.sleep(300)
|
||
except Exception as e:
|
||
logging.error(f"日数据更新线程错误: {str(e)}")
|
||
time.sleep(60)
|
||
|
||
hourly_thread = threading.Thread(target=hourly_update_thread)
|
||
hourly_thread.daemon = True
|
||
hourly_thread.start()
|
||
|
||
daily_thread = threading.Thread(target=daily_update_thread)
|
||
daily_thread.daemon = True
|
||
daily_thread.start()
|
||
|
||
# 启动配置监控线程
|
||
config_thread = threading.Thread(target=monitor_config_thread)
|
||
config_thread.daemon = True
|
||
config_thread.start()
|
||
|
||
# 保持主线程运行
|
||
try:
|
||
while True:
|
||
time.sleep(10)
|
||
except KeyboardInterrupt:
|
||
logging.info("程序被用户中断")
|
||
except Exception as e:
|
||
logging.error(f"主线程异常: {str(e)}")
|
||
finally:
|
||
logging.info("CC审核平台监控系统关闭")
|
||
sys.exit(0)
|
||
|
||
if __name__ == "__main__":
|
||
main() |