新增 Minecraft 渠道扩展,支持在 Yuxi 平台中集成 Minecraft 游戏服务器渠道。 包含以下功能模块: - client: Minecraft 客户端封装 - config: 渠道配置管理 - gateway: SSE/WebSocket 网关接入 - outbound: 外发消息管理 - streaming: 流式消息处理 - pairing: 用户配对与绑定 - security: 安全校验 - auth: 认证管理 - accounts: 账户管理 - dedupe: 消息去重 - monitor: 渠道状态监控 - status: 会话状态管理 - protocol: Minecraft 协议处理 - rcon: RCON 远程控制 - keepalive: 连接保活 - version_adapter: 版本适配 - setup: 初始化设置 - types: 类型定义
148 lines
5.4 KiB
Python
148 lines
5.4 KiB
Python
from yuxi.channel.protocols import SetupWizardStep
|
||
|
||
|
||
class MinecraftSetupWizard:
|
||
steps = [
|
||
SetupWizardStep(
|
||
key="host",
|
||
label="服务器地址",
|
||
description="Minecraft 服务器 IP 或域名(如 localhost 或 mc.example.com)",
|
||
input_type="text",
|
||
required=True,
|
||
default="localhost",
|
||
),
|
||
SetupWizardStep(
|
||
key="port",
|
||
label="服务器端口",
|
||
description="Minecraft 服务器端口(默认 25565)",
|
||
input_type="text",
|
||
required=True,
|
||
default="25565",
|
||
),
|
||
SetupWizardStep(
|
||
key="username",
|
||
label="Bot 用户名",
|
||
description="Bot 在游戏中显示的用户名",
|
||
input_type="text",
|
||
required=True,
|
||
default="ForcePilotBot",
|
||
),
|
||
SetupWizardStep(
|
||
key="auth_mode",
|
||
label="认证模式",
|
||
description="选择认证方式:offline(离线模式)或 microsoft(正版验证)",
|
||
input_type="select",
|
||
required=True,
|
||
default="offline",
|
||
options=[
|
||
{"label": "离线模式(offline)", "value": "offline"},
|
||
{"label": "正版验证(microsoft)", "value": "microsoft"},
|
||
],
|
||
),
|
||
SetupWizardStep(
|
||
key="version",
|
||
label="游戏版本",
|
||
description="选择 Minecraft 版本,auto 表示自动探测",
|
||
input_type="select",
|
||
required=True,
|
||
default="auto",
|
||
options=[
|
||
{"label": "自动探测(auto)", "value": "auto"},
|
||
{"label": "1.20.4", "value": "1.20.4"},
|
||
{"label": "1.21-1.21.1", "value": "1.21"},
|
||
{"label": "1.21.2-1.21.3", "value": "1.21.2"},
|
||
{"label": "1.21.4", "value": "1.21.4"},
|
||
{"label": "1.21.5", "value": "1.21.5"},
|
||
],
|
||
),
|
||
SetupWizardStep(
|
||
key="rcon_port",
|
||
label="RCON 端口",
|
||
description="RCON 远程管理端口(默认 25575,不需要可留空)",
|
||
input_type="text",
|
||
required=False,
|
||
default="25575",
|
||
),
|
||
SetupWizardStep(
|
||
key="rcon_password",
|
||
label="RCON 密码",
|
||
description="RCON 认证密码(不需要远程管理可留空)",
|
||
input_type="password",
|
||
required=False,
|
||
),
|
||
]
|
||
|
||
def setup_wizard_steps(self) -> list[SetupWizardStep]:
|
||
return self.steps
|
||
|
||
def validate_wizard_input(self, step_key: str, value: str) -> str | None:
|
||
if step_key == "host":
|
||
if not value or not value.strip():
|
||
return "服务器地址不能为空"
|
||
stripped = value.strip()
|
||
if len(stripped) > 253:
|
||
return "服务器地址过长(最大 253 字符)"
|
||
|
||
elif step_key == "port":
|
||
try:
|
||
port = int(value)
|
||
if not (1 <= port <= 65535):
|
||
return "端口必须在 1-65535 之间"
|
||
except ValueError:
|
||
return "端口必须是有效的整数"
|
||
|
||
elif step_key == "username":
|
||
if not value or not value.strip():
|
||
return "用户名不能为空"
|
||
stripped = value.strip()
|
||
if len(stripped) > 16:
|
||
return "Minecraft 用户名不能超过 16 个字符"
|
||
if not stripped.replace("_", "").isalnum():
|
||
return "用户名只能包含字母、数字和下划线"
|
||
|
||
elif step_key == "auth_mode":
|
||
if value not in ("offline", "microsoft"):
|
||
return "认证模式必须是 offline 或 microsoft"
|
||
|
||
elif step_key == "version":
|
||
valid_versions = ("auto", "1.20.4", "1.21", "1.21.2", "1.21.4", "1.21.5")
|
||
if value not in valid_versions:
|
||
return f"版本必须是以下之一: {', '.join(valid_versions)}"
|
||
|
||
elif step_key == "rcon_port":
|
||
if value:
|
||
try:
|
||
port = int(value)
|
||
if not (1 <= port <= 65535):
|
||
return "RCON 端口必须在 1-65535 之间"
|
||
except ValueError:
|
||
return "RCON 端口必须是有效的整数"
|
||
|
||
return None
|
||
|
||
async def finalize(self, config: dict) -> dict:
|
||
result = dict(config)
|
||
|
||
result.setdefault("host", "localhost")
|
||
result.setdefault("port", 25565)
|
||
result.setdefault("username", "ForcePilotBot")
|
||
result.setdefault("auth_mode", "offline")
|
||
result.setdefault("version", "auto")
|
||
result.setdefault("rcon_port", 25575)
|
||
|
||
if "port" in result and isinstance(result["port"], str):
|
||
result["port"] = int(result["port"])
|
||
if "rcon_port" in result and isinstance(result["rcon_port"], str):
|
||
result["rcon_port"] = int(result["rcon_port"])
|
||
|
||
result.setdefault("group_policy", "mention")
|
||
result.setdefault("require_mention", True)
|
||
result.setdefault("dm_policy", "pairing")
|
||
result.setdefault("allow_from", [])
|
||
result.setdefault("text_chunk_limit", 256)
|
||
result.setdefault("rate_limit_window", 5.0)
|
||
result.setdefault("rate_limit_max", 10)
|
||
result.setdefault("reconnect_max_retries", 10)
|
||
|
||
return result
|