Files
Claw e1fbd31c64 完整 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 等)
2026-05-04 16:23:59 +08:00

117 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""MC RCON 客户端"""
import socket
import struct
class RCONClient:
MAX_RETRIES = 3
RETRY_DELAY = 2
CONNECT_TIMEOUT = 5
SOCKET_TIMEOUT = 10
def __init__(self, host, port, password):
self.host = host
self.port = port
self.password = password
self.sock = None
self.packet_id = 1
self.authenticated = False
def connect(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(self.CONNECT_TIMEOUT)
self.sock.connect((self.host, self.port))
self.authenticate()
def authenticate(self):
packet = self._build_packet(self.packet_id, 3, self.password)
self.sock.sendall(packet)
response = self._read_response()
if not response or response.get("id") == -1:
raise Exception("RCON authentication failed")
self.authenticated = True
self.packet_id += 1
def is_connected(self):
if not self.sock or not self.authenticated:
return False
try:
self.sock.send(b"")
return True
except Exception:
return False
def send_command(self, command, max_retries=None):
if max_retries is None:
max_retries = self.MAX_RETRIES
last_error = None
for attempt in range(max_retries):
try:
if not self.is_connected():
self.close()
self.connect()
packet = self._build_packet(self.packet_id, 2, command)
self.sock.sendall(packet)
self.packet_id += 1
response = self._read_response()
return response
except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError, OSError) as e:
last_error = e
self.close()
if attempt < max_retries - 1:
import time
time.sleep(self.RETRY_DELAY)
except Exception as e:
last_error = e
self.close()
if attempt < max_retries - 1:
import time
time.sleep(self.RETRY_DELAY)
return None
def _build_packet(self, packet_id, packet_type, body_str):
body = body_str.encode("utf-8") + b"\x00\x00"
payload = struct.pack("<ii", packet_id, packet_type) + body
length = struct.pack("<i", len(payload))
return length + payload
def _read_response(self):
try:
self.sock.settimeout(self.SOCKET_TIMEOUT)
length_data = self.sock.recv(4)
except (ConnectionResetError, ConnectionAbortedError, OSError, socket.timeout):
return None
if not length_data:
return None
length = struct.unpack("<i", length_data)[0]
if length <= 0:
return None
remaining = b""
while len(remaining) < length:
try:
chunk = self.sock.recv(length - len(remaining))
if not chunk:
break
remaining += chunk
except (ConnectionResetError, ConnectionAbortedError, OSError, socket.timeout):
break
if len(remaining) < 8:
return None
packet_id, packet_type = struct.unpack("<ii", remaining[:8])
body = remaining[8:-2] if len(remaining) > 8 else b""
return {"id": packet_id, "type": packet_type, "body": body.decode("utf-8", errors="replace")}
def close(self):
if self.sock:
self.sock.close()
self.sock = None
self.authenticated = False
def __enter__(self):
self.connect()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()