完整 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:
Claw
2026-05-04 16:23:59 +08:00
parent d7d33c39a4
commit e1fbd31c64
35 changed files with 5058 additions and 276 deletions

191
scripts/mc_status.py Normal file
View 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))