feat(bootstrap): 自动更新可禁用
This commit is contained in:
parent
b377b8445e
commit
3f88c3a6c4
|
@ -1,16 +1,17 @@
|
||||||
import importlib.metadata
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import subprocess
|
import json
|
||||||
import ctypes
|
import ctypes
|
||||||
import codecs
|
import codecs
|
||||||
import locale
|
import locale
|
||||||
from typing import Optional, Dict, Any
|
import logging
|
||||||
|
import subprocess
|
||||||
|
import importlib.metadata
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from time import sleep
|
||||||
|
from typing import Optional, Dict, Any, TypedDict, Literal, List
|
||||||
|
|
||||||
from request import head, HTTPError, NetworkError
|
from request import head, HTTPError, NetworkError
|
||||||
from terminal import (
|
from terminal import (
|
||||||
|
@ -18,6 +19,29 @@ from terminal import (
|
||||||
get_terminal_width, get_display_width, truncate_string,
|
get_terminal_width, get_display_width, truncate_string,
|
||||||
hide_cursor, show_cursor, move_cursor_up, wait_key, get_terminal_height
|
hide_cursor, show_cursor, move_cursor_up, wait_key, get_terminal_height
|
||||||
)
|
)
|
||||||
|
from repo import Version
|
||||||
|
|
||||||
|
# 配置文件的类型定义
|
||||||
|
class BackendConfig(TypedDict, total=False):
|
||||||
|
type: Literal['custom', 'mumu12', 'leidian', 'dmm']
|
||||||
|
screenshot_impl: Literal['adb', 'adb_raw', 'uiautomator2', 'windows', 'remote_windows', 'nemu_ipc']
|
||||||
|
|
||||||
|
class MiscConfig(TypedDict, total=False):
|
||||||
|
check_update: Literal['never', 'startup']
|
||||||
|
auto_install_update: bool
|
||||||
|
|
||||||
|
class UserConfig(TypedDict, total=False):
|
||||||
|
name: str
|
||||||
|
id: str
|
||||||
|
category: str
|
||||||
|
description: str
|
||||||
|
backend: BackendConfig
|
||||||
|
keep_screenshots: bool
|
||||||
|
options: Dict[str, Any] # 这里包含 misc 等配置
|
||||||
|
|
||||||
|
class Config(TypedDict, total=False):
|
||||||
|
version: int
|
||||||
|
user_configs: List[UserConfig]
|
||||||
|
|
||||||
# 获取当前Python解释器路径
|
# 获取当前Python解释器路径
|
||||||
python_executable = sys.executable
|
python_executable = sys.executable
|
||||||
|
@ -296,40 +320,123 @@ def run_command(command: str, check: bool = True, verbatim: bool = False, scroll
|
||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
def install_pip_and_ksaa(pip_server: str) -> bool:
|
def check_ksaa_update_available(pip_server: str, current_version: Version) -> tuple[bool, Version | None, Version | None]:
|
||||||
|
"""
|
||||||
|
检查ksaa包是否有新版本可用。
|
||||||
|
|
||||||
|
:param pip_server: pip服务器URL
|
||||||
|
:type pip_server: str
|
||||||
|
:param current_version: 当前版本
|
||||||
|
:type current_version: Version
|
||||||
|
:return: (是否有更新, 当前版本, 最新版本)
|
||||||
|
:rtype: tuple[bool, Optional[Version], Optional[Version]]
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 使用repo.py中的list_versions函数和Version类获取最新版本信息
|
||||||
|
from repo import list_versions, Version
|
||||||
|
|
||||||
|
try:
|
||||||
|
versions = list_versions("ksaa", server_url=pip_server)
|
||||||
|
if versions and len(versions) > 0:
|
||||||
|
latest_version = versions[0].version
|
||||||
|
|
||||||
|
# 使用Version类的比较功能
|
||||||
|
if latest_version > current_version:
|
||||||
|
return True, current_version, latest_version
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f"从服务器 {pip_server} 获取版本信息失败: {e}")
|
||||||
|
print_status(f"从服务器 {pip_server} 获取版本信息失败: {e}", status='error')
|
||||||
|
# 如果指定服务器失败,尝试使用默认PyPI服务器
|
||||||
|
try:
|
||||||
|
versions = list_versions("ksaa")
|
||||||
|
if versions and len(versions) > 0:
|
||||||
|
latest_version = versions[0].version
|
||||||
|
|
||||||
|
# 使用Version类的比较功能
|
||||||
|
if latest_version > current_version:
|
||||||
|
return True, current_version, latest_version
|
||||||
|
except Exception as e2:
|
||||||
|
logging.warning(f"从PyPI获取版本信息也失败: {e2}")
|
||||||
|
|
||||||
|
return False, current_version, latest_version if 'latest_version' in locals() else None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f"检查ksaa更新时发生错误: {e}")
|
||||||
|
return False, None, None
|
||||||
|
|
||||||
|
def print_update_notice(current_version: str, latest_version: str):
|
||||||
|
"""
|
||||||
|
打印更新提示信息。
|
||||||
|
|
||||||
|
:param current_version: 当前版本
|
||||||
|
:type current_version: str
|
||||||
|
:param latest_version: 最新版本
|
||||||
|
:type latest_version: str
|
||||||
|
"""
|
||||||
|
clear_screen()
|
||||||
|
print()
|
||||||
|
print(f"{Color.YELLOW}{Color.BOLD}" + "=" * 60)
|
||||||
|
print(f"{Color.YELLOW}{Color.BOLD}⚠️ 发现新版本可用!")
|
||||||
|
print(f"{Color.YELLOW}{Color.BOLD}" + "=" * 60)
|
||||||
|
print(f"{Color.YELLOW}当前版本: {current_version}")
|
||||||
|
print(f"{Color.YELLOW}最新版本: {latest_version}")
|
||||||
|
print(f"{Color.YELLOW}建议开启自动更新或在设置中手动安装新版本。")
|
||||||
|
print(f"{Color.YELLOW}5s 后继续启动")
|
||||||
|
print(f"{Color.YELLOW}{Color.BOLD}" + "=" * 60 + f"{Color.RESET}")
|
||||||
|
print()
|
||||||
|
sleep(5)
|
||||||
|
|
||||||
|
def install_pip_and_ksaa(pip_server: str, check_update: bool = True, install_update: bool = True) -> bool:
|
||||||
"""
|
"""
|
||||||
安装和更新pip以及ksaa包。
|
安装和更新pip以及ksaa包。
|
||||||
|
|
||||||
:param pip_server: pip服务器URL
|
:param pip_server: pip服务器URL
|
||||||
:type pip_server: str
|
:type pip_server: str
|
||||||
|
:param check_update: 是否检查更新
|
||||||
|
:type check_update: bool
|
||||||
|
:param install_update: 是否安装更新
|
||||||
|
:type install_update: bool
|
||||||
:return: 安装是否成功
|
:return: 安装是否成功
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
print_header("安装与更新依赖", color=Color.BLUE)
|
print_header("安装与更新小助手", color=Color.BLUE)
|
||||||
|
|
||||||
# 定义信任的主机列表
|
# 定义信任的主机列表
|
||||||
trusted_hosts = "pypi.org files.pythonhosted.org pypi.python.org mirrors.aliyun.com mirrors.cloud.tencent.com mirrors.tuna.tsinghua.edu.cn"
|
trusted_hosts = "pypi.org files.pythonhosted.org pypi.python.org mirrors.aliyun.com mirrors.cloud.tencent.com mirrors.tuna.tsinghua.edu.cn"
|
||||||
|
|
||||||
# 升级pip
|
# 升级pip
|
||||||
print_status("检查并更新 pip", status='info')
|
print_status("更新 pip", status='info')
|
||||||
upgrade_pip_command = f'"{python_executable}" -m pip install -i {pip_server} --trusted-host "{trusted_hosts}" --upgrade pip'
|
upgrade_pip_command = f'"{python_executable}" -m pip install -i {pip_server} --trusted-host "{trusted_hosts}" --upgrade pip'
|
||||||
if not run_command(upgrade_pip_command):
|
if not run_command(upgrade_pip_command):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 安装ksaa,通过命令行参数传递配置
|
install_command = f'"{python_executable}" -m pip install --upgrade --index-url {pip_server} --trusted-host "{trusted_hosts}" --no-warn-script-location ksaa'
|
||||||
print_status("安装或更新 ksaa", status='info')
|
ksaa_version_str = package_version("ksaa")
|
||||||
install_command = f'"{python_executable}" -m pip install --upgrade --index-url {pip_server} --trusted-host "{trusted_hosts}" ksaa'
|
# 未安装
|
||||||
if not run_command(install_command):
|
if not ksaa_version_str:
|
||||||
return False
|
print_status("安装琴音小助手", status='info')
|
||||||
|
return run_command(install_command)
|
||||||
|
# 已安装,检查更新
|
||||||
|
else:
|
||||||
|
ksaa_version = Version(ksaa_version_str)
|
||||||
|
if check_update:
|
||||||
|
has_update, current_version, latest_version = check_ksaa_update_available(pip_server, ksaa_version)
|
||||||
|
if has_update:
|
||||||
|
if install_update:
|
||||||
|
print_status("更新琴音小助手", status='info')
|
||||||
|
return run_command(install_command)
|
||||||
|
else:
|
||||||
|
print_update_notice(str(current_version), str(latest_version))
|
||||||
|
else:
|
||||||
|
print_status("已是最新版本", status='success')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def load_config() -> Optional[Dict[str, Any]]:
|
def load_config() -> Optional[Config]:
|
||||||
"""
|
"""
|
||||||
加载config.json配置文件。
|
加载config.json配置文件。
|
||||||
|
|
||||||
:return: 配置字典,如果加载失败返回None
|
:return: 配置字典,如果加载失败返回None
|
||||||
:rtype: Optional[Dict[str, Any]]
|
:rtype: Optional[Config]
|
||||||
"""
|
"""
|
||||||
config_path = Path("./config.json")
|
config_path = Path("./config.json")
|
||||||
if not config_path.exists():
|
if not config_path.exists():
|
||||||
|
@ -351,6 +458,38 @@ def load_config() -> Optional[Dict[str, Any]]:
|
||||||
logging.error(msg, exc_info=True)
|
logging.error(msg, exc_info=True)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_update_settings(config: Config) -> tuple[bool, bool]:
|
||||||
|
"""
|
||||||
|
从配置中获取更新设置。
|
||||||
|
|
||||||
|
:param config: 配置字典
|
||||||
|
:type config: Config
|
||||||
|
:return: (是否检查更新, 是否自动安装更新)
|
||||||
|
:rtype: tuple[bool, bool]
|
||||||
|
"""
|
||||||
|
# 默认值
|
||||||
|
check_update = True
|
||||||
|
auto_install_update = True
|
||||||
|
|
||||||
|
# 检查是否有用户配置
|
||||||
|
user_configs = config.get("user_configs", [])
|
||||||
|
if user_configs:
|
||||||
|
first_config = user_configs[0]
|
||||||
|
options = first_config.get("options", {})
|
||||||
|
misc = options.get("misc", {})
|
||||||
|
|
||||||
|
# 获取检查更新设置
|
||||||
|
check_update_setting = misc.get("check_update", "startup")
|
||||||
|
check_update = check_update_setting == "startup"
|
||||||
|
|
||||||
|
# 获取自动安装更新设置
|
||||||
|
auto_install_update = misc.get("auto_install_update", True)
|
||||||
|
|
||||||
|
msg = f"更新设置: 检查更新={check_update}, 自动安装={auto_install_update}"
|
||||||
|
logging.info(msg)
|
||||||
|
|
||||||
|
return check_update, auto_install_update
|
||||||
|
|
||||||
def restart_as_admin() -> None:
|
def restart_as_admin() -> None:
|
||||||
"""
|
"""
|
||||||
以管理员身份重启程序。
|
以管理员身份重启程序。
|
||||||
|
@ -382,24 +521,25 @@ def restart_as_admin() -> None:
|
||||||
logging.error(msg, exc_info=True)
|
logging.error(msg, exc_info=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
def check_admin(config: Dict[str, Any]) -> bool:
|
def check_admin(config: Config) -> bool:
|
||||||
"""
|
"""
|
||||||
检查Windows截图权限(管理员权限)。
|
检查Windows截图权限(管理员权限)。
|
||||||
|
|
||||||
:param config: 配置字典
|
:param config: 配置字典
|
||||||
:type config: Dict[str, Any]
|
:type config: Config
|
||||||
:return: 权限检查是否通过
|
:return: 权限检查是否通过
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
# 检查是否有用户配置
|
# 检查是否有用户配置
|
||||||
if not config.get("user_configs"):
|
user_configs = config.get("user_configs", [])
|
||||||
|
if not user_configs:
|
||||||
msg = "配置文件中没有用户配置"
|
msg = "配置文件中没有用户配置"
|
||||||
print_status(msg, status='warning')
|
print_status(msg, status='warning')
|
||||||
logging.warning(msg)
|
logging.warning(msg)
|
||||||
return True # Not a fatal error, allow to continue
|
return True # Not a fatal error, allow to continue
|
||||||
|
|
||||||
# 检查第一个用户配置的截图方式
|
# 检查第一个用户配置的截图方式
|
||||||
first_config = config["user_configs"][0]
|
first_config = user_configs[0]
|
||||||
backend = first_config.get("backend", {})
|
backend = first_config.get("backend", {})
|
||||||
screenshot_impl = backend.get("screenshot_impl")
|
screenshot_impl = backend.get("screenshot_impl")
|
||||||
|
|
||||||
|
@ -457,27 +597,31 @@ def main_launch():
|
||||||
logging.info("启动器已启动。")
|
logging.info("启动器已启动。")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 1. 获取可用的pip服务器
|
# 1. 加载配置文件(提前加载以获取更新设置)
|
||||||
|
print_header("加载配置", color=Color.BLUE)
|
||||||
|
logging.info("加载配置。")
|
||||||
|
config = load_config()
|
||||||
|
|
||||||
|
# 2. 获取更新设置
|
||||||
|
check_update, auto_install_update = get_update_settings(config if config else {"version": 5, "user_configs": []})
|
||||||
|
|
||||||
|
# 3. 根据配置决定是否检查更新
|
||||||
print_status("正在寻找最快的 PyPI 镜像源...", status='info')
|
print_status("正在寻找最快的 PyPI 镜像源...", status='info')
|
||||||
logging.info("正在寻找最快的 PyPI 镜像源...")
|
logging.info("正在寻找最快的 PyPI 镜像源...")
|
||||||
pip_server = get_working_pip_server()
|
pip_server = get_working_pip_server()
|
||||||
if not pip_server:
|
if not pip_server:
|
||||||
raise RuntimeError("没有找到可用的pip服务器,请检查网络连接。")
|
raise RuntimeError("没有找到可用的pip服务器,请检查网络连接。")
|
||||||
|
|
||||||
# 2. 安装和更新pip以及ksaa包
|
# 4. 安装和更新pip以及ksaa包
|
||||||
if not install_pip_and_ksaa(pip_server):
|
if not install_pip_and_ksaa(pip_server, check_update, auto_install_update):
|
||||||
raise RuntimeError("依赖安装失败,请检查上面的错误日志。")
|
raise RuntimeError("依赖安装失败,请检查上面的错误日志。")
|
||||||
|
|
||||||
# 3. 加载配置文件
|
# 5. 检查Windows截图权限
|
||||||
print_header("加载配置", color=Color.BLUE)
|
|
||||||
logging.info("加载配置。")
|
|
||||||
config = load_config()
|
|
||||||
if config:
|
if config:
|
||||||
# 4. 检查Windows截图权限
|
|
||||||
if not check_admin(config):
|
if not check_admin(config):
|
||||||
raise RuntimeError("权限检查失败。")
|
raise RuntimeError("权限检查失败。")
|
||||||
|
|
||||||
# 5. 运行KAA
|
# 6. 运行KAA
|
||||||
if not run_kaa():
|
if not run_kaa():
|
||||||
raise RuntimeError("KAA 主程序运行失败。")
|
raise RuntimeError("KAA 主程序运行失败。")
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,8 @@ class Version:
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"Version('{self.version_str}')"
|
return f"Version('{self.version_str}')"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.version_str
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PackageVersion:
|
class PackageVersion:
|
||||||
|
|
3
justfile
3
justfile
|
@ -125,6 +125,7 @@ publish-test: package
|
||||||
#
|
#
|
||||||
build-bootstrap:
|
build-bootstrap:
|
||||||
#!{{shebang_pwsh}}
|
#!{{shebang_pwsh}}
|
||||||
|
echo "Building bootstrap"...
|
||||||
# 构建 Python
|
# 构建 Python
|
||||||
cd bootstrap
|
cd bootstrap
|
||||||
python -m zipapp kaa-bootstrap
|
python -m zipapp kaa-bootstrap
|
||||||
|
@ -134,7 +135,7 @@ build-bootstrap:
|
||||||
$msbuild = &"${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe
|
$msbuild = &"${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe
|
||||||
if ($msbuild) {
|
if ($msbuild) {
|
||||||
& $msbuild kaa-wrapper/kaa-wrapper.sln /p:Configuration=Release
|
& $msbuild kaa-wrapper/kaa-wrapper.sln /p:Configuration=Release
|
||||||
mv kaa-wrapper/x64/Release/kaa-wrapper.exe ../dist/bootstrap.exe -fo
|
mv kaa-wrapper/x64/Release/kaa-wrapper.exe ../dist/kaa.exe -fo
|
||||||
} else {
|
} else {
|
||||||
Write-Host "MSBuild not found. Please install Visual Studio or build kaa-wrapper manually."
|
Write-Host "MSBuild not found. Please install Visual Studio or build kaa-wrapper manually."
|
||||||
}
|
}
|
|
@ -461,6 +461,21 @@ class EndGameConfig(ConfigBaseModel):
|
||||||
(目前仅对 DMM 版有效。)
|
(目前仅对 DMM 版有效。)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class MiscConfig(ConfigBaseModel):
|
||||||
|
check_update: Literal['never', 'startup'] = 'startup'
|
||||||
|
"""
|
||||||
|
检查更新时机。
|
||||||
|
|
||||||
|
* never: 从不检查更新。
|
||||||
|
* startup: 启动时检查更新。
|
||||||
|
"""
|
||||||
|
auto_install_update: bool = True
|
||||||
|
"""
|
||||||
|
是否自动安装更新。
|
||||||
|
|
||||||
|
若启用,则每次自动检查更新时若有新版本会自动安装,否则只是会提示。
|
||||||
|
"""
|
||||||
|
|
||||||
class BaseConfig(ConfigBaseModel):
|
class BaseConfig(ConfigBaseModel):
|
||||||
purchase: PurchaseConfig = PurchaseConfig()
|
purchase: PurchaseConfig = PurchaseConfig()
|
||||||
"""商店购买配置"""
|
"""商店购买配置"""
|
||||||
|
@ -501,6 +516,9 @@ class BaseConfig(ConfigBaseModel):
|
||||||
end_game: EndGameConfig = EndGameConfig()
|
end_game: EndGameConfig = EndGameConfig()
|
||||||
"""关闭游戏配置"""
|
"""关闭游戏配置"""
|
||||||
|
|
||||||
|
misc: MiscConfig = MiscConfig()
|
||||||
|
"""杂项配置"""
|
||||||
|
|
||||||
|
|
||||||
def conf() -> BaseConfig:
|
def conf() -> BaseConfig:
|
||||||
"""获取当前配置数据"""
|
"""获取当前配置数据"""
|
||||||
|
|
|
@ -22,7 +22,7 @@ from kotonebot.kaa.common import (
|
||||||
BaseConfig, APShopItems, CapsuleToysConfig, ClubRewardConfig, PurchaseConfig, ActivityFundsConfig,
|
BaseConfig, APShopItems, CapsuleToysConfig, ClubRewardConfig, PurchaseConfig, ActivityFundsConfig,
|
||||||
PresentsConfig, AssignmentConfig, ContestConfig, ProduceConfig,
|
PresentsConfig, AssignmentConfig, ContestConfig, ProduceConfig,
|
||||||
MissionRewardConfig, DailyMoneyShopItems, ProduceAction,
|
MissionRewardConfig, DailyMoneyShopItems, ProduceAction,
|
||||||
RecommendCardDetectionMode, TraceConfig, StartGameConfig, EndGameConfig, UpgradeSupportCardConfig,
|
RecommendCardDetectionMode, TraceConfig, StartGameConfig, EndGameConfig, UpgradeSupportCardConfig, MiscConfig,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -88,6 +88,9 @@ ConfigKey = Literal[
|
||||||
'presents_enabled',
|
'presents_enabled',
|
||||||
'trace_recommend_card_detection',
|
'trace_recommend_card_detection',
|
||||||
|
|
||||||
|
# misc
|
||||||
|
'check_update', 'auto_install_update',
|
||||||
|
|
||||||
'_selected_backend_index'
|
'_selected_backend_index'
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -1469,6 +1472,35 @@ class KotoneBotUI:
|
||||||
'trace_recommend_card_detection': trace_recommend_card_detection
|
'trace_recommend_card_detection': trace_recommend_card_detection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _create_misc_settings(self) -> ConfigBuilderReturnValue:
|
||||||
|
with gr.Column():
|
||||||
|
gr.Markdown("### 杂项设置")
|
||||||
|
check_update = gr.Dropdown(
|
||||||
|
choices=[
|
||||||
|
("启动时检查更新", "startup"),
|
||||||
|
("从不检查更新", "never")
|
||||||
|
],
|
||||||
|
value=self.current_config.options.misc.check_update,
|
||||||
|
label="检查更新时机",
|
||||||
|
info=MiscConfig.model_fields['check_update'].description,
|
||||||
|
interactive=True
|
||||||
|
)
|
||||||
|
auto_install_update = gr.Checkbox(
|
||||||
|
label="自动安装更新",
|
||||||
|
value=self.current_config.options.misc.auto_install_update,
|
||||||
|
info=MiscConfig.model_fields['auto_install_update'].description,
|
||||||
|
interactive=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_config(config: BaseConfig, data: dict[ConfigKey, Any]) -> None:
|
||||||
|
config.misc.check_update = data['check_update']
|
||||||
|
config.misc.auto_install_update = data['auto_install_update']
|
||||||
|
|
||||||
|
return set_config, {
|
||||||
|
'check_update': check_update,
|
||||||
|
'auto_install_update': auto_install_update
|
||||||
|
}
|
||||||
|
|
||||||
def _create_settings_tab(self) -> None:
|
def _create_settings_tab(self) -> None:
|
||||||
with gr.Tab("设置"):
|
with gr.Tab("设置"):
|
||||||
gr.Markdown("## 设置")
|
gr.Markdown("## 设置")
|
||||||
|
@ -1506,6 +1538,9 @@ class KotoneBotUI:
|
||||||
# 跟踪设置
|
# 跟踪设置
|
||||||
trace_settings = self._create_trace_settings()
|
trace_settings = self._create_trace_settings()
|
||||||
|
|
||||||
|
# 杂项设置
|
||||||
|
misc_settings = self._create_misc_settings()
|
||||||
|
|
||||||
# 启动游戏设置
|
# 启动游戏设置
|
||||||
start_game_settings = self._create_start_game_settings()
|
start_game_settings = self._create_start_game_settings()
|
||||||
|
|
||||||
|
@ -1529,7 +1564,8 @@ class KotoneBotUI:
|
||||||
capsule_toys_settings,
|
capsule_toys_settings,
|
||||||
start_game_settings,
|
start_game_settings,
|
||||||
end_game_settings,
|
end_game_settings,
|
||||||
trace_settings
|
trace_settings,
|
||||||
|
misc_settings
|
||||||
] # list of (set_func, { 'key': component, ... })
|
] # list of (set_func, { 'key': component, ... })
|
||||||
all_components = [list(ret[1].values()) for ret in all_return_values] # [[c1, c2], [c3], ...]
|
all_components = [list(ret[1].values()) for ret in all_return_values] # [[c1, c2], [c3], ...]
|
||||||
all_components = list(chain(*all_components)) # [c1, c2, c3, ...]
|
all_components = list(chain(*all_components)) # [c1, c2, c3, ...]
|
||||||
|
|
Loading…
Reference in New Issue