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