完整 AgentSkill:词库外部化 + 全部脚本/Skill 脱敏上传
- 高危词库从 process.py 抽到 config.toml [security] section - process.py 改为 _get_high_risk_words() 动态加载(配置优先) - 新增 15 个 QQ 操作脚本(已脱敏:QQ号/IP/Token → 占位符) - 新增 8 个 SKILL.md(qq-messenger/management/resolver/napcat-extras 等) - 新增 SKILL.md 入口(完整部署方案文档) - 保留 SDK 框架(plugin_modules.py, file_store_api.py, package.py 等)
This commit is contained in:
191
scripts/mc_status.py
Normal file
191
scripts/mc_status.py
Normal file
@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env python3
|
||||
"""获取Minecraft服务器运行状态(JSON输出)"""
|
||||
|
||||
import sys, os, subprocess, re
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from mc_config import RCON_HOST, RCON_PORT, RCON_PASSWORD, MC_SERVER_DIR
|
||||
from mc_rcon import RCONClient
|
||||
|
||||
def get_mc_pid():
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["pgrep", "-f", "java.*(minecraft|forge|neoforge)"],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
return int(result.stdout.strip().split()[0])
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def get_memory(pid):
|
||||
if not pid:
|
||||
return None
|
||||
try:
|
||||
with open(f"/proc/{pid}/status", "r") as f:
|
||||
for line in f:
|
||||
if line.startswith("VmRSS:"):
|
||||
kb = int(line.split()[1])
|
||||
return {"mb": round(kb/1024), "gb": round(kb/1024/1024, 1)}
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def get_cpu(pid):
|
||||
if not pid:
|
||||
return None
|
||||
try:
|
||||
with open(f"/proc/{pid}/stat", "r") as f:
|
||||
stat = f.read().split()
|
||||
utime, stime = int(stat[13]), int(stat[14])
|
||||
clk_tck = os.sysconf(os.sysconf_names['SC_CLK_TCK'])
|
||||
starttime = int(stat[21])
|
||||
with open("/proc/uptime", "r") as f:
|
||||
uptime = float(f.read().split()[0])
|
||||
proc_uptime = uptime - (starttime / clk_tck)
|
||||
if proc_uptime > 0:
|
||||
return round(((utime + stime) / clk_tck) / proc_uptime * 100, 1)
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def get_uptime(pid):
|
||||
if not pid:
|
||||
return None
|
||||
try:
|
||||
with open(f"/proc/{pid}/stat", "r") as f:
|
||||
stat = f.read().split()
|
||||
clk_tck = os.sysconf(os.sysconf_names['SC_CLK_TCK'])
|
||||
starttime = int(stat[21])
|
||||
with open("/proc/uptime", "r") as f:
|
||||
uptime = float(f.read().split()[0])
|
||||
sec = uptime - (starttime / clk_tck)
|
||||
d, h, m = int(sec//86400), int((sec%86400)//3600), int((sec%3600)//60)
|
||||
parts = []
|
||||
if d: parts.append(f"{d}天")
|
||||
if h: parts.append(f"{h}小时")
|
||||
parts.append(f"{m}分钟")
|
||||
return "".join(parts)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_tps():
|
||||
try:
|
||||
with RCONClient(RCON_HOST, RCON_PORT, RCON_PASSWORD) as client:
|
||||
client.send_command("spark tps")
|
||||
import time
|
||||
time.sleep(3)
|
||||
log_path = os.path.join(MC_SERVER_DIR, "logs", "latest.log")
|
||||
if not os.path.exists(log_path):
|
||||
return None
|
||||
with open(log_path, "r", errors="ignore") as f:
|
||||
lines = f.readlines()
|
||||
for line in reversed(lines[-500:]):
|
||||
if "tps" not in line.lower():
|
||||
continue
|
||||
m = re.search(r'\*?([\d.]+),\s*\*?([\d.]+),\s*\*?([\d.]+),\s*\*?([\d.]+),\s*\*?([\d.]+)', line)
|
||||
if m:
|
||||
return {
|
||||
"tps_1m": float(m.group(3)),
|
||||
"raw": f"5s:{m.group(1)} 10s:{m.group(2)} 1m:{m.group(3)} 5m:{m.group(4)} 15m:{m.group(5)}"
|
||||
}
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def get_version():
|
||||
try:
|
||||
for vendor, name in [("neoforged", "NeoForge"), ("minecraftforge", "Forge")]:
|
||||
vdir = os.path.join(MC_SERVER_DIR, "libraries", "net", vendor, name.lower())
|
||||
if os.path.exists(vdir):
|
||||
vers = [d for d in os.listdir(vdir) if os.path.isdir(os.path.join(vdir, d))]
|
||||
if vers:
|
||||
return f"{name} {vers[0]}"
|
||||
except Exception:
|
||||
pass
|
||||
return "未知"
|
||||
|
||||
def get_world_sizes():
|
||||
sizes = {}
|
||||
for w in ["world", "world_nether", "world_the_end"]:
|
||||
p = os.path.join(MC_SERVER_DIR, w)
|
||||
if os.path.exists(p):
|
||||
try:
|
||||
r = subprocess.run(["du", "-sh", p], capture_output=True, text=True, timeout=10)
|
||||
if r.returncode == 0:
|
||||
sizes[w] = r.stdout.split()[0]
|
||||
except Exception:
|
||||
sizes[w] = "?"
|
||||
try:
|
||||
r = subprocess.run(["du", "-sh", MC_SERVER_DIR], capture_output=True, text=True, timeout=30)
|
||||
if r.returncode == 0:
|
||||
sizes["total"] = r.stdout.split()[0]
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
st = os.statvfs("/")
|
||||
total = st.f_frsize * st.f_blocks
|
||||
used = st.f_frsize * (st.f_blocks - st.f_bfree)
|
||||
sizes["disk_total_gb"] = round(total/1024**3, 1)
|
||||
sizes["disk_used_gb"] = round(used/1024**3, 1)
|
||||
sizes["disk_pct"] = round((used/total)*100, 1)
|
||||
except Exception:
|
||||
pass
|
||||
return sizes
|
||||
|
||||
def get_system_load():
|
||||
try:
|
||||
with open("/proc/loadavg", "r") as f:
|
||||
parts = f.read().split()
|
||||
cpu_count = os.cpu_count() or 1
|
||||
return {
|
||||
"load_1": float(parts[0]),
|
||||
"load_5": float(parts[1]),
|
||||
"load_15": float(parts[2]),
|
||||
"cpu_count": cpu_count,
|
||||
"load_pct": round((float(parts[0])/cpu_count)*100, 1)
|
||||
}
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_player_list():
|
||||
try:
|
||||
with RCONClient(RCON_HOST, RCON_PORT, RCON_PASSWORD) as client:
|
||||
resp = client.send_command("list")
|
||||
body = resp.get("body", "") if resp else ""
|
||||
m = re.search(r'players online:\s*(.+)?$', body, re.IGNORECASE)
|
||||
if m and m.group(1):
|
||||
return [p.strip() for p in m.group(1).split(",") if p.strip()]
|
||||
except Exception:
|
||||
pass
|
||||
return []
|
||||
|
||||
def get_player_count():
|
||||
try:
|
||||
with RCONClient(RCON_HOST, RCON_PORT, RCON_PASSWORD) as client:
|
||||
resp = client.send_command("list")
|
||||
body = resp.get("body", "") if resp else ""
|
||||
m = re.search(r'There are (\d+) of a max of (\d+) players', body)
|
||||
if m:
|
||||
return {"online": int(m.group(1)), "max": int(m.group(2))}
|
||||
except Exception:
|
||||
pass
|
||||
return {"online": 0, "max": 0}
|
||||
|
||||
if __name__ == "__main__":
|
||||
import json
|
||||
pid = get_mc_pid()
|
||||
result = {
|
||||
"online": bool(pid),
|
||||
"pid": pid,
|
||||
"players": get_player_count(),
|
||||
"player_list": get_player_list(),
|
||||
"memory_mb": get_memory(pid),
|
||||
"cpu_percent": get_cpu(pid),
|
||||
"uptime": get_uptime(pid),
|
||||
"tps": get_tps(),
|
||||
"version": get_version(),
|
||||
"world_sizes": get_world_sizes(),
|
||||
"system_load": get_system_load(),
|
||||
}
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
Reference in New Issue
Block a user