From 32b2971a02cab9cca24b96b17d74037297fb672b Mon Sep 17 00:00:00 2001 From: EterUltimate <1831303476@qq.com> Date: Fri, 17 Apr 2026 23:18:15 +0800 Subject: [PATCH 1/7] feat: add one-line deploy script (deploy-cli.sh) and update cli.md - Add docs/scripts/deploy-cli.sh for one-command deployment (Linux/macOS/WSL) - Update docs/zh/deploy/astrbot/cli.md to reference local script - Replace non-existent https://astrbot.app/deploy.sh with raw.githubusercontent.com URL - Add local run tip and WSL-based Windows support --- docs/scripts/deploy-cli.sh | 74 +++++++++++++++++++++++++++++++++++ docs/zh/deploy/astrbot/cli.md | 45 +++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 docs/scripts/deploy-cli.sh diff --git a/docs/scripts/deploy-cli.sh b/docs/scripts/deploy-cli.sh new file mode 100644 index 0000000000..1332fef190 --- /dev/null +++ b/docs/scripts/deploy-cli.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# deploy-cli.sh — AstrBot 一行命令部署脚本 (Linux / macOS / WSL) +# 用法: bash -c "$(curl -fsSL https://raw.githubusercontent.com/AstrBotDevs/AstrBot/master/docs/scripts/deploy-cli.sh)" + +set -euo pipefail + +# ── 颜色 ── +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' + +info() { echo -e "${CYAN}[INFO]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +ok() { echo -e "${GREEN}[OK]${NC} $*"; } +err() { echo -e "${RED}[ERROR]${NC} $*"; } + +# ── 检测命令 ── +has() { command -v "$1" &>/dev/null; } + +# ── 1. 检测并安装依赖 ── +info "正在检测运行环境..." + +if ! has git; then + err "未检测到 git,请先安装: https://git-scm.com/downloads" + exit 1 +fi + +if has python3; then + PY=python3 +elif has python; then + PY=python +else + err "未检测到 Python (>=3.10),请先安装: https://www.python.org/downloads/" + exit 1 +fi + +PY_VER=$($PY -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') +if [ "$(printf '%s\n' "3.10" "$PY_VER" | sort -V | head -n1)" != "3.10" ]; then + err "Python 版本过低: $PY_VER,需要 >= 3.10" + exit 1 +fi +ok "Python $PY_VER" + +# ── 安装 uv(如未安装) ── +if ! has uv; then + info "正在安装 uv 包管理器..." + curl -LsSf https://astral.sh/uv/install.sh | sh + export PATH="$HOME/.local/bin:$PATH" + if ! has uv; then + err "uv 安装失败,请手动安装: https://docs.astral.sh/uv/getting-started/installation/" + exit 1 + fi +fi +ok "uv $(uv --version)" + +# ── 2. 克隆仓库 ── +INSTALL_DIR="${1:-AstrBot}" +if [ ! -d "$INSTALL_DIR/.git" ]; then + info "正在克隆 AstrBot 仓库到 $INSTALL_DIR ..." + git clone --depth=1 https://github.com/AstrBotDevs/AstrBot.git "$INSTALL_DIR" +else + info "目录 $INSTALL_DIR 已存在,跳过克隆" +fi +cd "$INSTALL_DIR" + +# ── 3. 安装依赖 ── +info "正在安装项目依赖 (uv sync)..." +uv sync + +# ── 4. 启动 AstrBot ── +ok "依赖安装完成,正在启动 AstrBot..." +echo "" +info "管理面板默认地址: http://localhost:6185" +info "默认用户名/密码: astrbot / astrbot" +echo "" +uv run main.py diff --git a/docs/zh/deploy/astrbot/cli.md b/docs/zh/deploy/astrbot/cli.md index 623eb583fb..444ed02381 100644 --- a/docs/zh/deploy/astrbot/cli.md +++ b/docs/zh/deploy/astrbot/cli.md @@ -6,6 +6,51 @@ > 以下教程默认您的设备上已经安装 Python,并且版本 `>=3.10` +## 🚀 一行命令快速部署 + +> [!TIP] +> 如果你希望一步到位,只需要一个命令就能完成从克隆到启动的全过程,请使用下面的脚本。 + +::: details Linux / macOS / WSL + +```bash +bash -c "$(curl -fsSL https://raw.githubusercontent.com/AstrBotDevs/AstrBot/master/docs/scripts/deploy-cli.sh)" +``` + +该脚本会自动完成以下步骤: +1. 检测并提示安装必要的依赖(git、python>=3.10、uv) +2. 克隆 AstrBot 官方仓库 +3. 使用 `uv sync` 安装项目依赖 +4. 启动 AstrBot 并打印访问链接 + +> [!TIP] +> 你也可以克隆仓库后本地运行脚本: +> ```bash +> git clone https://github.com/AstrBotDevs/AstrBot.git +> cd AstrBot +> bash docs/scripts/deploy-cli.sh +> ``` + +::: details Windows(PowerShell 7+) + +> [!WARNING] +> 建议使用 [PowerShell 7](https://github.com/PowerShell/PowerShell/releases) 以获得最佳体验。 + +```powershell +# 克隆仓库后运行部署脚本(WSL 方式) +git clone https://github.com/AstrBotDevs/AstrBot.git; cd AstrBot; wsl bash docs/scripts/deploy-cli.sh +``` + +> [!NOTE] +> Windows 原生环境建议直接按照下方「下载/克隆仓库」和「安装依赖并运行」步骤手动操作,或使用 [Docker 部署](./docker.md)。 +::: + + +--- + + + + ## 下载/克隆仓库 如果你的电脑上安装了 `git`,你可以通过以下命令来下载源码: From 0b577342b52f4f6fd40f0424b94461f138769a4f Mon Sep 17 00:00:00 2001 From: EterUltimate <1831303476@qq.com> Date: Fri, 17 Apr 2026 23:24:29 +0800 Subject: [PATCH 2/7] fix: address PR review feedback for deploy-cli.sh - Replace sort -V with Python-native version check (macOS BSD compat) - Bump Python requirement from >=3.10 to >=3.12 (match pyproject.toml) - Detect current directory as project root (avoid nested clone) - Handle existing non-git directory explicitly - Add curl dependency check - Support ASTRBOT_REPO and ASTRBOT_DIR env vars for forks/mirrors - Update cli.md version description to match --- docs/scripts/deploy-cli.sh | 44 ++++++++++++++++++++++++++--------- docs/zh/deploy/astrbot/cli.md | 2 +- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/docs/scripts/deploy-cli.sh b/docs/scripts/deploy-cli.sh index 1332fef190..6b0ae34056 100644 --- a/docs/scripts/deploy-cli.sh +++ b/docs/scripts/deploy-cli.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash # deploy-cli.sh — AstrBot 一行命令部署脚本 (Linux / macOS / WSL) # 用法: bash -c "$(curl -fsSL https://raw.githubusercontent.com/AstrBotDevs/AstrBot/master/docs/scripts/deploy-cli.sh)" +# +# 环境变量: +# ASTRBOT_REPO — 仓库地址 (默认: https://github.com/AstrBotDevs/AstrBot.git) +# ASTRBOT_DIR — 安装目录 (默认: AstrBot) set -euo pipefail @@ -15,6 +19,9 @@ err() { echo -e "${RED}[ERROR]${NC} $*"; } # ── 检测命令 ── has() { command -v "$1" &>/dev/null; } +# ── 可配置变量 ── +REPO_URL="${ASTRBOT_REPO:-https://github.com/AstrBotDevs/AstrBot.git}" + # ── 1. 检测并安装依赖 ── info "正在检测运行环境..." @@ -23,20 +30,27 @@ if ! has git; then exit 1 fi +if ! has curl; then + err "未检测到 curl,请先安装: macOS 使用 'brew install curl',Ubuntu/Debian 使用 'sudo apt install curl'" + exit 1 +fi + if has python3; then PY=python3 elif has python; then PY=python else - err "未检测到 Python (>=3.10),请先安装: https://www.python.org/downloads/" + err "未检测到 Python (>=3.12),请先安装: https://www.python.org/downloads/" exit 1 fi -PY_VER=$($PY -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') -if [ "$(printf '%s\n' "3.10" "$PY_VER" | sort -V | head -n1)" != "3.10" ]; then - err "Python 版本过低: $PY_VER,需要 >= 3.10" +# 使用 Python 自身进行版本比较,兼容 macOS BSD sort +if ! $PY -c 'import sys; exit(0 if sys.version_info >= (3, 12) else 1)'; then + PY_VER=$($PY -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') + err "Python 版本过低: $PY_VER,需要 >= 3.12" exit 1 fi +PY_VER=$($PY -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') ok "Python $PY_VER" # ── 安装 uv(如未安装) ── @@ -51,15 +65,23 @@ if ! has uv; then fi ok "uv $(uv --version)" -# ── 2. 克隆仓库 ── -INSTALL_DIR="${1:-AstrBot}" -if [ ! -d "$INSTALL_DIR/.git" ]; then - info "正在克隆 AstrBot 仓库到 $INSTALL_DIR ..." - git clone --depth=1 https://github.com/AstrBotDevs/AstrBot.git "$INSTALL_DIR" +# ── 2. 定位项目目录 ── +# 优先检测当前目录是否已是项目根目录(支持本地运行场景) +if [ -f "main.py" ] && [ -d ".git" ]; then + info "检测到已在项目目录中,跳过克隆" else - info "目录 $INSTALL_DIR 已存在,跳过克隆" + INSTALL_DIR="${ASTRBOT_DIR:-${1:-AstrBot}}" + if [ -d "$INSTALL_DIR/.git" ]; then + info "目录 $INSTALL_DIR 已存在,跳过克隆" + elif [ -d "$INSTALL_DIR" ]; then + err "目录 $INSTALL_DIR 已存在但不是 AstrBot 仓库,请指定其他目录或手动清理" + exit 1 + else + info "正在克隆 AstrBot 仓库到 $INSTALL_DIR ..." + git clone --depth=1 "$REPO_URL" "$INSTALL_DIR" + fi + cd "$INSTALL_DIR" fi -cd "$INSTALL_DIR" # ── 3. 安装依赖 ── info "正在安装项目依赖 (uv sync)..." diff --git a/docs/zh/deploy/astrbot/cli.md b/docs/zh/deploy/astrbot/cli.md index 444ed02381..7623ce9578 100644 --- a/docs/zh/deploy/astrbot/cli.md +++ b/docs/zh/deploy/astrbot/cli.md @@ -18,7 +18,7 @@ bash -c "$(curl -fsSL https://raw.githubusercontent.com/AstrBotDevs/AstrBot/mast ``` 该脚本会自动完成以下步骤: -1. 检测并提示安装必要的依赖(git、python>=3.10、uv) +1. 检测并提示安装必要的依赖(git、curl、python>=3.12、uv) 2. 克隆 AstrBot 官方仓库 3. 使用 `uv sync` 安装项目依赖 4. 启动 AstrBot 并打印访问链接 From 57b321041dda7a9dd00684fee5ae4aeb946b9551 Mon Sep 17 00:00:00 2001 From: EterUltimate <1831303476@qq.com> Date: Sat, 18 Apr 2026 15:51:33 +0800 Subject: [PATCH 3/7] feat: add Windows PowerShell deploy script and update docs - Add deploy-cli.ps1 for Windows native environment (PowerShell 7+) - Update cli.md to document Windows one-liner deployment - Add local script execution instructions for both bash and ps1 --- docs/scripts/deploy-cli.ps1 | 129 ++++++++++++++++++++++++++++++++++ docs/zh/deploy/astrbot/cli.md | 24 ++++++- 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 docs/scripts/deploy-cli.ps1 diff --git a/docs/scripts/deploy-cli.ps1 b/docs/scripts/deploy-cli.ps1 new file mode 100644 index 0000000000..d5f05f899b --- /dev/null +++ b/docs/scripts/deploy-cli.ps1 @@ -0,0 +1,129 @@ +# deploy-cli.ps1 — AstrBot 一行命令部署脚本 (Windows PowerShell 原生) +# 用法: irm https://raw.githubusercontent.com/AstrBotDevs/AstrBot/master/docs/scripts/deploy-cli.ps1 | iex +# +# 环境变量: +# $env:ASTRBOT_REPO — 仓库地址 (默认: https://github.com/AstrBotDevs/AstrBot.git) +# $env:ASTRBOT_DIR — 安装目录 (默认: AstrBot) + +#Requires -Version 7.0 + +$ErrorActionPreference = 'Stop' + +# ── 颜色 ── +function Info { Write-Host "[INFO] $args" -ForegroundColor Cyan } +function Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow } +function Ok { Write-Host "[OK] $args" -ForegroundColor Green } +function Err { Write-Host "[ERROR] $args" -ForegroundColor Red } + +# ── 检测命令 ── +function Test-Command { + param([string]$Name) + $null = Get-Command $Name -ErrorAction SilentlyContinue + return $? +} + +# ── 可配置变量 ─- +$REPO_URL = if ($env:ASTRBOT_REPO) { $env:ASTRBOT_REPO } else { "https://github.com/AstrBotDevs/AstrBot.git" } + +# ── 1. 检测并安装依赖 ── +Info "正在检测运行环境..." + +if (-not (Test-Command "git")) { + Err "未检测到 git,请先安装: https://git-scm.com/downloads" + exit 1 +} +Ok "git 已安装" + +if (-not (Test-Command "curl")) { + Err "未检测到 curl,请先安装或使用 PowerShell 7 内置 Invoke-WebRequest" + exit 1 +} +Ok "curl 已安装" + +# 检测 Python +$PythonCmd = $null +foreach ($cmd in @("python3", "python")) { + if (Test-Command $cmd) { + $PythonCmd = $cmd + break + } +} + +if (-not $PythonCmd) { + Err "未检测到 Python (>=3.12),请先安装: https://www.python.org/downloads/" + exit 1 +} + +# 检查 Python 版本 +$pyVersion = & $PythonCmd -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" +if ([int]($pyVersion.Split('.')[0]) -lt 3 -or ($pyVersion.Split('.')[0] -eq '3' -and [int]($pyVersion.Split('.')[1]) -lt 12)) { + Err "Python 版本过低: $pyVersion,需要 >= 3.12" + exit 1 +} +Ok "Python $pyVersion" + +# ── 安装 uv(如未安装) ── +if (-not (Test-Command "uv")) { + Info "正在安装 uv 包管理器..." + # 使用 PowerShell 安装 uv + $tempScript = [System.IO.Path]::GetTempFileName() + ".ps1" + try { + Invoke-WebRequest -Uri "https://astral.sh/uv/install.ps1" -OutFile $tempScript -UseBasicParsing + . $tempScript + } catch { + Err "uv 安装失败,请手动安装: https://docs.astral.sh/uv/getting-started/installation/" + exit 1 + } finally { + Remove-Item $tempScript -ErrorAction SilentlyContinue + } +} + +# 刷新 PATH 并检查 uv +$uvVersion = (Get-Command uv -ErrorAction SilentlyContinue).Version.ToString() +if (-not $uvVersion) { + # 尝试从默认安装位置检查 + $uvPath = "$env:LOCALAPPDATA\uv\uv.exe" + if (Test-Path $uvPath) { + $uvVersion = & $uvPath --version + } +} + +if ($uvVersion) { + Ok "uv $uvVersion" +} else { + Err "uv 安装失败,请手动安装: https://docs.astral.sh/uv/getting-started/installation/" + exit 1 +} + +# ── 2. 定位项目目录 ── +# 优先检测当前目录是否已是项目根目录(支持本地运行场景) +if ((Test-Path "main.py") -and (Test-Path ".git")) { + Info "检测到已在项目目录中,跳过克隆" + $ProjectDir = $PWD.Path +} else { + $INSTALL_DIR = if ($env:ASTRBOT_DIR) { $env:ASTRBOT_DIR } else { "AstrBot" } + if (Test-Path "$INSTALL_DIR\.git") { + Info "目录 $INSTALL_DIR 已存在,跳过克隆" + $ProjectDir = (Resolve-Path $INSTALL_DIR).Path + } elseif (Test-Path $INSTALL_DIR) { + Err "目录 $INSTALL_DIR 已存在但不是 AstrBot 仓库,请指定其他目录或手动清理" + exit 1 + } else { + Info "正在克隆 AstrBot 仓库到 $INSTALL_DIR ..." + git clone --depth=1 $REPO_URL $INSTALL_DIR + $ProjectDir = (Resolve-Path $INSTALL_DIR).Path + } + Set-Location $ProjectDir +} + +# ── 3. 安装依赖 ── +Info "正在安装项目依赖 (uv sync)..." +uv sync + +# ── 4. 启动 AstrBot ── +Ok "依赖安装完成,正在启动 AstrBot..." +Write-Host "" +Info "管理面板默认地址: http://localhost:6185" +Info "默认用户名/密码: astrbot / astrbot" +Write-Host "" +uv run main.py \ No newline at end of file diff --git a/docs/zh/deploy/astrbot/cli.md b/docs/zh/deploy/astrbot/cli.md index 7623ce9578..ded534c8b8 100644 --- a/docs/zh/deploy/astrbot/cli.md +++ b/docs/zh/deploy/astrbot/cli.md @@ -36,13 +36,35 @@ bash -c "$(curl -fsSL https://raw.githubusercontent.com/AstrBotDevs/AstrBot/mast > [!WARNING] > 建议使用 [PowerShell 7](https://github.com/PowerShell/PowerShell/releases) 以获得最佳体验。 +**原生 Windows(PowerShell 7+)一行命令:** + +```powershell +irm https://raw.githubusercontent.com/AstrBotDevs/AstrBot/master/docs/scripts/deploy-cli.ps1 | iex +``` + +> [!TIP] +> 你也可以克隆仓库后本地运行脚本: +> ```powershell +> git clone https://github.com/AstrBotDevs/AstrBot.git +> cd AstrBot +> .\docs\scripts\deploy-cli.ps1 +> ``` + +该脚本会自动完成以下步骤: +1. 检测并提示安装必要的依赖(git、curl、python>=3.12、uv) +2. 克隆 AstrBot 官方仓库 +3. 使用 `uv sync` 安装项目依赖 +4. 启动 AstrBot 并打印访问链接 + +**或者使用 WSL 调用 bash 脚本:** + ```powershell # 克隆仓库后运行部署脚本(WSL 方式) git clone https://github.com/AstrBotDevs/AstrBot.git; cd AstrBot; wsl bash docs/scripts/deploy-cli.sh ``` > [!NOTE] -> Windows 原生环境建议直接按照下方「下载/克隆仓库」和「安装依赖并运行」步骤手动操作,或使用 [Docker 部署](./docker.md)。 +> 如果你更喜欢在 Windows 原生环境运行,推荐使用上面的 PowerShell 脚本。WSL 方式适合需要在 Linux 环境下运行的用户,或使用 [Docker 部署](./docker.md)。 ::: From 74bfcb1a99f0e0d6268043dd5a07bbec418e9cc3 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 28 Apr 2026 14:31:20 +0800 Subject: [PATCH 4/7] refactor: standardize log messages and improve clarity in various modules Co-authored-by: Copilot --- astrbot/core/agent/tool_image_cache.py | 1 - astrbot/core/config/astrbot_config.py | 8 +- astrbot/core/core_lifecycle.py | 2 +- astrbot/core/persona_mgr.py | 2 +- astrbot/core/platform/manager.py | 6 +- astrbot/core/platform/register.py | 2 +- astrbot/core/provider/manager.py | 12 ++- astrbot/core/provider/register.py | 2 +- astrbot/core/star/star_manager.py | 10 +- astrbot/core/utils/io.py | 19 ++-- astrbot/dashboard/server.py | 16 +-- docs/scripts/deploy-cli.ps1 | 129 ------------------------- docs/scripts/deploy-cli.sh | 96 ------------------ main.py | 14 +-- scripts/deploy-cli.ps1 | 86 +++++++++++++++++ scripts/deploy-cli.sh | 68 +++++++++++++ tests/test_main.py | 14 ++- 17 files changed, 215 insertions(+), 272 deletions(-) delete mode 100644 docs/scripts/deploy-cli.ps1 delete mode 100644 docs/scripts/deploy-cli.sh create mode 100644 scripts/deploy-cli.ps1 create mode 100644 scripts/deploy-cli.sh diff --git a/astrbot/core/agent/tool_image_cache.py b/astrbot/core/agent/tool_image_cache.py index 72e22dd52e..0c7bc3c31e 100644 --- a/astrbot/core/agent/tool_image_cache.py +++ b/astrbot/core/agent/tool_image_cache.py @@ -52,7 +52,6 @@ def __init__(self) -> None: self._initialized = True self._cache_dir = os.path.join(get_astrbot_temp_path(), self.CACHE_DIR_NAME) os.makedirs(self._cache_dir, exist_ok=True) - logger.debug(f"ToolImageCache initialized, cache dir: {self._cache_dir}") def _get_file_extension(self, mime_type: str) -> str: """Get file extension from MIME type.""" diff --git a/astrbot/core/config/astrbot_config.py b/astrbot/core/config/astrbot_config.py index 77c298cac8..dde7fe1c73 100644 --- a/astrbot/core/config/astrbot_config.py +++ b/astrbot/core/config/astrbot_config.py @@ -104,7 +104,7 @@ def check_config_integrity(self, refer_conf: dict, conf: dict, path=""): if key not in conf: # 配置项不存在,插入默认值 path_ = path + "." + key if path else key - logger.info(f"检查到配置项 {path_} 不存在,已插入默认值 {value}") + logger.info("Config key missing; added default: %s", path_) new_conf[key] = value has_new = True elif conf[key] is None: @@ -134,15 +134,15 @@ def check_config_integrity(self, refer_conf: dict, conf: dict, path=""): for key in list(conf.keys()): if key not in refer_conf: path_ = path + "." + key if path else key - logger.info(f"检查到配置项 {path_} 不存在,将从当前配置中删除") + logger.info("Config key removed: %s", path_) has_new = True # 顺序不一致也算作变更 if list(conf.keys()) != list(new_conf.keys()): if path: - logger.info(f"检查到配置项 {path} 的子项顺序不一致,已重新排序") + logger.info("Config key order fixed: %s", path) else: - logger.info("检查到配置项顺序不一致,已重新排序") + logger.info("Config key order fixed") has_new = True # 更新原始配置 diff --git a/astrbot/core/core_lifecycle.py b/astrbot/core/core_lifecycle.py index fe6b1c351d..e5e34d4b54 100644 --- a/astrbot/core/core_lifecycle.py +++ b/astrbot/core/core_lifecycle.py @@ -294,7 +294,7 @@ async def start(self) -> None: 用load加载事件总线和任务并初始化, 执行启动完成事件钩子 """ self._load() - logger.info("AstrBot 启动完成。") + logger.info("AstrBot started.") # 执行启动完成事件钩子 handlers = star_handlers_registry.get_handlers_by_event_type( diff --git a/astrbot/core/persona_mgr.py b/astrbot/core/persona_mgr.py index 6320ac3bbc..290501438b 100644 --- a/astrbot/core/persona_mgr.py +++ b/astrbot/core/persona_mgr.py @@ -35,7 +35,7 @@ def __init__(self, db_helper: BaseDatabase, acm: AstrBotConfigManager) -> None: async def initialize(self) -> None: self.personas = await self.get_all_personas() self.get_v3_persona_data() - logger.info(f"已加载 {len(self.personas)} 个人格。") + logger.info("Loaded %s personas.", len(self.personas)) async def get_persona(self, persona_id: str): """获取指定 persona 的信息""" diff --git a/astrbot/core/platform/manager.py b/astrbot/core/platform/manager.py index d592eb2fbf..22409c0f83 100644 --- a/astrbot/core/platform/manager.py +++ b/astrbot/core/platform/manager.py @@ -123,7 +123,9 @@ async def load_platform(self, platform_config: dict) -> None: return logger.info( - f"载入 {platform_config['type']}({platform_config['id']}) 平台适配器 ...", + "Loading IM platform adapter %s(%s) ...", + platform_config["type"], + platform_config["id"], ) match platform_config["type"]: case "aiocqhttp": @@ -201,7 +203,7 @@ async def load_platform(self, platform_config: dict) -> None: if platform_config["type"] not in platform_cls_map: logger.error( - f"未找到适用于 {platform_config['type']}({platform_config['id']}) 平台适配器,请检查是否已经安装或者名称填写错误", + f"Platform adapter not found: {platform_config['type']}({platform_config['id']}).", ) return cls_type = platform_cls_map[platform_config["type"]] diff --git a/astrbot/core/platform/register.py b/astrbot/core/platform/register.py index 62ec5070ab..4db1b98b6c 100644 --- a/astrbot/core/platform/register.py +++ b/astrbot/core/platform/register.py @@ -57,7 +57,7 @@ def decorator(cls): ) platform_registry.append(pm) platform_cls_map[adapter_name] = cls - logger.debug(f"平台适配器 {adapter_name} 已注册") + logger.debug("Platform adapter registered: %s", adapter_name) return cls return decorator diff --git a/astrbot/core/provider/manager.py b/astrbot/core/provider/manager.py index 0dfdbdcf6d..333cf6c906 100644 --- a/astrbot/core/provider/manager.py +++ b/astrbot/core/provider/manager.py @@ -570,7 +570,9 @@ async def load_provider(self, provider_config: dict) -> None: return logger.info( - f"载入 {provider_config['type']}({provider_config['id']}) 服务提供商 ...", + "Loading model %s(%s) ...", + provider_config["type"], + provider_config["id"], ) # 动态导入 @@ -591,7 +593,7 @@ async def load_provider(self, provider_config: dict) -> None: if provider_config["type"] not in provider_cls_map: logger.error( - f"未找到适用于 {provider_config['type']}({provider_config['id']}) 的提供商适配器,请检查是否已经安装或者名称填写错误。已跳过。", + f"Provider adapter not found: {provider_config['type']}({provider_config['id']}). Skipped.", exc_info=True, ) return @@ -625,7 +627,7 @@ async def load_provider(self, provider_config: dict) -> None: ): self.curr_stt_provider_inst = inst logger.info( - f"已选择 {provider_config['type']}({provider_config['id']}) 作为当前语音转文本提供商适配器。", + f"Selected {provider_config['type']}({provider_config['id']}) as default STT provider", ) if not self.curr_stt_provider_inst: self.curr_stt_provider_inst = inst @@ -648,7 +650,7 @@ async def load_provider(self, provider_config: dict) -> None: ): self.curr_tts_provider_inst = inst logger.info( - f"已选择 {provider_config['type']}({provider_config['id']}) 作为当前文本转语音提供商适配器。", + f"Selected {provider_config['type']}({provider_config['id']}) as default TTS provider", ) if not self.curr_tts_provider_inst: self.curr_tts_provider_inst = inst @@ -674,7 +676,7 @@ async def load_provider(self, provider_config: dict) -> None: ): self.curr_provider_inst = inst logger.info( - f"已选择 {provider_config['type']}({provider_config['id']}) 作为当前提供商适配器。", + f"Selected {provider_config['type']}({provider_config['id']}) as default chat model provider", ) if not self.curr_provider_inst: self.curr_provider_inst = inst diff --git a/astrbot/core/provider/register.py b/astrbot/core/provider/register.py index 3ad83784ec..409f22f376 100644 --- a/astrbot/core/provider/register.py +++ b/astrbot/core/provider/register.py @@ -47,7 +47,7 @@ def decorator(cls): ) provider_registry.append(pm) provider_cls_map[provider_type_name] = pm - logger.debug(f"服务提供商 Provider {provider_type_name} 已注册") + logger.debug("Model provider registered: %s", provider_type_name) return cls return decorator diff --git a/astrbot/core/star/star_manager.py b/astrbot/core/star/star_manager.py index fc1fa075eb..dcaed8eba0 100644 --- a/astrbot/core/star/star_manager.py +++ b/astrbot/core/star/star_manager.py @@ -570,7 +570,7 @@ def _validate_astrbot_version_specifier( except InvalidSpecifier: return ( False, - "astrbot_version 格式无效,请使用 PEP 440 版本范围格式,例如 >=4.16,<5。", + "Invalid astrbot_version. Use a PEP 440 range, e.g. >=4.16,<5.", ) try: @@ -578,13 +578,13 @@ def _validate_astrbot_version_specifier( except InvalidVersion: return ( False, - f"AstrBot 当前版本 {VERSION} 无法被解析,无法校验插件版本范围。", + f"Invalid current AstrBot version: {VERSION}. Cannot check plugin version range.", ) if not specifier.contains(current_version, prereleases=True): return ( False, - f"当前 AstrBot 版本为 {VERSION},不满足插件要求的 astrbot_version: {normalized_spec}", + f"AstrBot {VERSION} does not satisfy plugin astrbot_version: {normalized_spec}", ) return True, None @@ -874,7 +874,7 @@ async def load( if specified_dir_name and root_dir_name != specified_dir_name: continue - logger.info(f"正在载入插件 {root_dir_name} ...") + logger.info("Loading plugin %s ...", root_dir_name) # 尝试导入模块 try: @@ -993,7 +993,7 @@ async def load( setattr(metadata.star_cls, "author", p_author) setattr(metadata.star_cls, "plugin_id", plugin_id) else: - logger.info(f"插件 {metadata.name} 已被禁用。") + logger.info("Plugin %s is disabled.", metadata.name) metadata.module = module metadata.root_dir_name = root_dir_name diff --git a/astrbot/core/utils/io.py b/astrbot/core/utils/io.py index b565926749..e118df17ea 100644 --- a/astrbot/core/utils/io.py +++ b/astrbot/core/utils/io.py @@ -136,12 +136,14 @@ async def download_file(url: str, path: str, show_progress: bool = False) -> Non ) as session: async with session.get(url, timeout=1800) as resp: if resp.status != 200: - raise Exception(f"下载文件失败: {resp.status}") + logger.error( + f"Failed to download file from {url}. HTTP status code: {resp.status}" + ) total_size = int(resp.headers.get("content-length", 0)) downloaded_size = 0 start_time = time.time() if show_progress: - print(f"文件大小: {total_size / 1024:.2f} KB | 文件地址: {url}") + print(f"Downloading: {url} | Size: {total_size / 1024:.2f} KB") with open(path, "wb") as f: while True: chunk = await resp.content.read(8192) @@ -157,13 +159,14 @@ async def download_file(url: str, path: str, show_progress: bool = False) -> Non ) speed = downloaded_size / 1024 / elapsed_time # KB/s print( - f"\r下载进度: {downloaded_size / total_size:.2%} 速度: {speed:.2f} KB/s", + f"\rProgress: {downloaded_size / total_size:.2%} Speed: {speed:.2f} KB/s", end="", ) except (aiohttp.ClientConnectorSSLError, aiohttp.ClientConnectorCertificateError): # 关闭SSL验证(仅在证书验证失败时作为fallback) logger.warning( - "SSL 证书验证失败,已关闭 SSL 验证(不安全,仅用于临时下载)。请检查目标服务器的证书配置。" + f"SSL certificate verification failed for {url}. " + "Falling back to unverified connection (CERT_NONE). " ) logger.warning( f"SSL certificate verification failed for {url}. " @@ -180,7 +183,7 @@ async def download_file(url: str, path: str, show_progress: bool = False) -> Non downloaded_size = 0 start_time = time.time() if show_progress: - print(f"文件大小: {total_size / 1024:.2f} KB | 文件地址: {url}") + print(f"Size: {total_size / 1024:.2f} KB | URL: {url}") with open(path, "wb") as f: while True: chunk = await resp.content.read(8192) @@ -192,7 +195,7 @@ async def download_file(url: str, path: str, show_progress: bool = False) -> Non elapsed_time = time.time() - start_time speed = downloaded_size / 1024 / elapsed_time # KB/s print( - f"\r下载进度: {downloaded_size / total_size:.2%} 速度: {speed:.2f} KB/s", + f"\rProgress: {downloaded_size / total_size:.2%} Speed: {speed:.2f} KB/s", end="", ) if show_progress: @@ -252,7 +255,7 @@ async def download_dashboard( ver_name = "latest" if latest else version dashboard_release_url = f"https://astrbot-registry.soulter.top/download/astrbot-dashboard/{ver_name}/dist.zip" logger.info( - f"准备下载指定发行版本的 AstrBot WebUI 文件: {dashboard_release_url}", + f"Downloading AstrBot WebUI from {dashboard_release_url}", ) try: await download_file( @@ -274,7 +277,7 @@ async def download_dashboard( ) else: url = f"https://github.com/AstrBotDevs/astrbot-release-harbour/releases/download/release-{version}/dist.zip" - logger.info(f"准备下载指定版本的 AstrBot WebUI: {url}") + logger.info(f"Downloading AstrBot WebUI from {url}") if proxy: url = f"{proxy}/{url}" await download_file(url, str(zip_path), show_progress=True) diff --git a/astrbot/dashboard/server.py b/astrbot/dashboard/server.py index b3dc759458..19aac55893 100644 --- a/astrbot/dashboard/server.py +++ b/astrbot/dashboard/server.py @@ -385,13 +385,13 @@ def run(self): scheme = "https" if ssl_enable else "http" if not enable: - logger.info("WebUI 已被禁用") + logger.info("WebUI disabled.") return None - logger.info(f"正在启动 WebUI, 监听地址: {scheme}://{host}:{port}") + logger.info("Starting WebUI at %s://%s:%s", scheme, host, port) if host == "0.0.0.0": logger.info( - "提示: WebUI 将监听所有网络接口,请注意安全。(可在 data/cmd_config.json 中配置 dashboard.host 以修改 host)", + "WebUI listens on all interfaces. Check security. Set dashboard.host in data/cmd_config.json to change it.", ) if host not in ["localhost", "127.0.0.1"]: @@ -415,16 +415,16 @@ def run(self): raise Exception(f"端口 {port} 已被占用") - parts = [f"\n ✨✨✨\n AstrBot v{VERSION} WebUI 已启动,可访问\n\n"] - parts.append(f" ➜ 本地: {scheme}://localhost:{port}\n") + parts = [f"\n ✨✨✨\n AstrBot v{VERSION} WebUI is ready\n\n"] + parts.append(f" ➜ Local: {scheme}://localhost:{port}\n") for ip in ip_addr: - parts.append(f" ➜ 网络: {scheme}://{ip}:{port}\n") - parts.append(" ➜ 默认用户名和密码: astrbot\n ✨✨✨\n") + parts.append(f" ➜ Network: {scheme}://{ip}:{port}\n") + parts.append(" ➜ Default username/password: astrbot / astrbot\n ✨✨✨\n") display = "".join(parts) if not ip_addr: display += ( - "可在 data/cmd_config.json 中配置 dashboard.host 以便远程访问。\n" + "Set dashboard.host in data/cmd_config.json to enable remote access.\n" ) logger.info(display) diff --git a/docs/scripts/deploy-cli.ps1 b/docs/scripts/deploy-cli.ps1 deleted file mode 100644 index d5f05f899b..0000000000 --- a/docs/scripts/deploy-cli.ps1 +++ /dev/null @@ -1,129 +0,0 @@ -# deploy-cli.ps1 — AstrBot 一行命令部署脚本 (Windows PowerShell 原生) -# 用法: irm https://raw.githubusercontent.com/AstrBotDevs/AstrBot/master/docs/scripts/deploy-cli.ps1 | iex -# -# 环境变量: -# $env:ASTRBOT_REPO — 仓库地址 (默认: https://github.com/AstrBotDevs/AstrBot.git) -# $env:ASTRBOT_DIR — 安装目录 (默认: AstrBot) - -#Requires -Version 7.0 - -$ErrorActionPreference = 'Stop' - -# ── 颜色 ── -function Info { Write-Host "[INFO] $args" -ForegroundColor Cyan } -function Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow } -function Ok { Write-Host "[OK] $args" -ForegroundColor Green } -function Err { Write-Host "[ERROR] $args" -ForegroundColor Red } - -# ── 检测命令 ── -function Test-Command { - param([string]$Name) - $null = Get-Command $Name -ErrorAction SilentlyContinue - return $? -} - -# ── 可配置变量 ─- -$REPO_URL = if ($env:ASTRBOT_REPO) { $env:ASTRBOT_REPO } else { "https://github.com/AstrBotDevs/AstrBot.git" } - -# ── 1. 检测并安装依赖 ── -Info "正在检测运行环境..." - -if (-not (Test-Command "git")) { - Err "未检测到 git,请先安装: https://git-scm.com/downloads" - exit 1 -} -Ok "git 已安装" - -if (-not (Test-Command "curl")) { - Err "未检测到 curl,请先安装或使用 PowerShell 7 内置 Invoke-WebRequest" - exit 1 -} -Ok "curl 已安装" - -# 检测 Python -$PythonCmd = $null -foreach ($cmd in @("python3", "python")) { - if (Test-Command $cmd) { - $PythonCmd = $cmd - break - } -} - -if (-not $PythonCmd) { - Err "未检测到 Python (>=3.12),请先安装: https://www.python.org/downloads/" - exit 1 -} - -# 检查 Python 版本 -$pyVersion = & $PythonCmd -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" -if ([int]($pyVersion.Split('.')[0]) -lt 3 -or ($pyVersion.Split('.')[0] -eq '3' -and [int]($pyVersion.Split('.')[1]) -lt 12)) { - Err "Python 版本过低: $pyVersion,需要 >= 3.12" - exit 1 -} -Ok "Python $pyVersion" - -# ── 安装 uv(如未安装) ── -if (-not (Test-Command "uv")) { - Info "正在安装 uv 包管理器..." - # 使用 PowerShell 安装 uv - $tempScript = [System.IO.Path]::GetTempFileName() + ".ps1" - try { - Invoke-WebRequest -Uri "https://astral.sh/uv/install.ps1" -OutFile $tempScript -UseBasicParsing - . $tempScript - } catch { - Err "uv 安装失败,请手动安装: https://docs.astral.sh/uv/getting-started/installation/" - exit 1 - } finally { - Remove-Item $tempScript -ErrorAction SilentlyContinue - } -} - -# 刷新 PATH 并检查 uv -$uvVersion = (Get-Command uv -ErrorAction SilentlyContinue).Version.ToString() -if (-not $uvVersion) { - # 尝试从默认安装位置检查 - $uvPath = "$env:LOCALAPPDATA\uv\uv.exe" - if (Test-Path $uvPath) { - $uvVersion = & $uvPath --version - } -} - -if ($uvVersion) { - Ok "uv $uvVersion" -} else { - Err "uv 安装失败,请手动安装: https://docs.astral.sh/uv/getting-started/installation/" - exit 1 -} - -# ── 2. 定位项目目录 ── -# 优先检测当前目录是否已是项目根目录(支持本地运行场景) -if ((Test-Path "main.py") -and (Test-Path ".git")) { - Info "检测到已在项目目录中,跳过克隆" - $ProjectDir = $PWD.Path -} else { - $INSTALL_DIR = if ($env:ASTRBOT_DIR) { $env:ASTRBOT_DIR } else { "AstrBot" } - if (Test-Path "$INSTALL_DIR\.git") { - Info "目录 $INSTALL_DIR 已存在,跳过克隆" - $ProjectDir = (Resolve-Path $INSTALL_DIR).Path - } elseif (Test-Path $INSTALL_DIR) { - Err "目录 $INSTALL_DIR 已存在但不是 AstrBot 仓库,请指定其他目录或手动清理" - exit 1 - } else { - Info "正在克隆 AstrBot 仓库到 $INSTALL_DIR ..." - git clone --depth=1 $REPO_URL $INSTALL_DIR - $ProjectDir = (Resolve-Path $INSTALL_DIR).Path - } - Set-Location $ProjectDir -} - -# ── 3. 安装依赖 ── -Info "正在安装项目依赖 (uv sync)..." -uv sync - -# ── 4. 启动 AstrBot ── -Ok "依赖安装完成,正在启动 AstrBot..." -Write-Host "" -Info "管理面板默认地址: http://localhost:6185" -Info "默认用户名/密码: astrbot / astrbot" -Write-Host "" -uv run main.py \ No newline at end of file diff --git a/docs/scripts/deploy-cli.sh b/docs/scripts/deploy-cli.sh deleted file mode 100644 index 6b0ae34056..0000000000 --- a/docs/scripts/deploy-cli.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env bash -# deploy-cli.sh — AstrBot 一行命令部署脚本 (Linux / macOS / WSL) -# 用法: bash -c "$(curl -fsSL https://raw.githubusercontent.com/AstrBotDevs/AstrBot/master/docs/scripts/deploy-cli.sh)" -# -# 环境变量: -# ASTRBOT_REPO — 仓库地址 (默认: https://github.com/AstrBotDevs/AstrBot.git) -# ASTRBOT_DIR — 安装目录 (默认: AstrBot) - -set -euo pipefail - -# ── 颜色 ── -RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' - -info() { echo -e "${CYAN}[INFO]${NC} $*"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } -ok() { echo -e "${GREEN}[OK]${NC} $*"; } -err() { echo -e "${RED}[ERROR]${NC} $*"; } - -# ── 检测命令 ── -has() { command -v "$1" &>/dev/null; } - -# ── 可配置变量 ── -REPO_URL="${ASTRBOT_REPO:-https://github.com/AstrBotDevs/AstrBot.git}" - -# ── 1. 检测并安装依赖 ── -info "正在检测运行环境..." - -if ! has git; then - err "未检测到 git,请先安装: https://git-scm.com/downloads" - exit 1 -fi - -if ! has curl; then - err "未检测到 curl,请先安装: macOS 使用 'brew install curl',Ubuntu/Debian 使用 'sudo apt install curl'" - exit 1 -fi - -if has python3; then - PY=python3 -elif has python; then - PY=python -else - err "未检测到 Python (>=3.12),请先安装: https://www.python.org/downloads/" - exit 1 -fi - -# 使用 Python 自身进行版本比较,兼容 macOS BSD sort -if ! $PY -c 'import sys; exit(0 if sys.version_info >= (3, 12) else 1)'; then - PY_VER=$($PY -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') - err "Python 版本过低: $PY_VER,需要 >= 3.12" - exit 1 -fi -PY_VER=$($PY -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') -ok "Python $PY_VER" - -# ── 安装 uv(如未安装) ── -if ! has uv; then - info "正在安装 uv 包管理器..." - curl -LsSf https://astral.sh/uv/install.sh | sh - export PATH="$HOME/.local/bin:$PATH" - if ! has uv; then - err "uv 安装失败,请手动安装: https://docs.astral.sh/uv/getting-started/installation/" - exit 1 - fi -fi -ok "uv $(uv --version)" - -# ── 2. 定位项目目录 ── -# 优先检测当前目录是否已是项目根目录(支持本地运行场景) -if [ -f "main.py" ] && [ -d ".git" ]; then - info "检测到已在项目目录中,跳过克隆" -else - INSTALL_DIR="${ASTRBOT_DIR:-${1:-AstrBot}}" - if [ -d "$INSTALL_DIR/.git" ]; then - info "目录 $INSTALL_DIR 已存在,跳过克隆" - elif [ -d "$INSTALL_DIR" ]; then - err "目录 $INSTALL_DIR 已存在但不是 AstrBot 仓库,请指定其他目录或手动清理" - exit 1 - else - info "正在克隆 AstrBot 仓库到 $INSTALL_DIR ..." - git clone --depth=1 "$REPO_URL" "$INSTALL_DIR" - fi - cd "$INSTALL_DIR" -fi - -# ── 3. 安装依赖 ── -info "正在安装项目依赖 (uv sync)..." -uv sync - -# ── 4. 启动 AstrBot ── -ok "依赖安装完成,正在启动 AstrBot..." -echo "" -info "管理面板默认地址: http://localhost:6185" -info "默认用户名/密码: astrbot / astrbot" -echo "" -uv run main.py diff --git a/main.py b/main.py index 14e0c23a81..299d4b5ec6 100644 --- a/main.py +++ b/main.py @@ -70,9 +70,9 @@ async def check_dashboard_files(webui_dir: str | None = None): # 指定webui目录 if webui_dir: if os.path.exists(webui_dir): - logger.info(f"使用指定的 WebUI 目录: {webui_dir}") + logger.info("Using WebUI directory: %s", webui_dir) return webui_dir - logger.warning(f"指定的 WebUI 目录 {webui_dir} 不存在,将使用默认逻辑。") + logger.warning("WebUI directory not found: %s. Using default.", webui_dir) data_dist_path = os.path.join(get_astrbot_data_path(), "dist") if os.path.exists(data_dist_path): @@ -80,15 +80,17 @@ async def check_dashboard_files(webui_dir: str | None = None): if v is not None: # 存在文件 if v == f"v{VERSION}": - logger.info("WebUI 版本已是最新。") + logger.info("WebUI is up to date.") else: logger.warning( - f"检测到 WebUI 版本 ({v}) 与当前 AstrBot 版本 (v{VERSION}) 不符。", + "WebUI version mismatch: %s, expected v%s.", + v, + VERSION, ) return data_dist_path logger.info( - "开始下载管理面板文件...高峰期(晚上)可能导致较慢的速度。如多次下载失败,请前往 https://github.com/AstrBotDevs/AstrBot/releases/latest 下载 dist.zip,并将其中的 dist 文件夹解压至 data 目录下。", + "Downloading WebUI. If it fails, download dist.zip from https://github.com/AstrBotDevs/AstrBot/releases/latest and extract dist to data/.", ) try: @@ -126,7 +128,7 @@ async def main_async(webui_dir_arg: str | None) -> None: parser.add_argument( "--webui-dir", type=str, - help="指定 WebUI 静态文件目录路径", + help="Specify the directory path for WebUI static files", default=None, ) args = parser.parse_args() diff --git a/scripts/deploy-cli.ps1 b/scripts/deploy-cli.ps1 new file mode 100644 index 0000000000..8b7545b56c --- /dev/null +++ b/scripts/deploy-cli.ps1 @@ -0,0 +1,86 @@ +# deploy-cli.ps1 - Install astrbot with uv on Windows PowerShell. + +#Requires -Version 7.0 + +$ErrorActionPreference = 'Stop' + +$UseColor = [string]::IsNullOrEmpty($env:NO_COLOR) -and [Console]::IsOutputRedirected -eq $false + +function Write-Status { + param( + [string]$Prefix, + [string]$Message, + [string]$Color + ) + + if ($UseColor) { + Write-Host "$Prefix $Message" -ForegroundColor $Color + } else { + Write-Host "$Prefix $Message" + } +} + +function Info { Write-Status "[INFO]" "$args" "Cyan" } +function Warn { Write-Status "[WARN]" "$args" "Yellow" } +function Ok { Write-Status "[OK]" "$args" "Green" } +function Err { Write-Status "[ERROR]" "$args" "Red" } + +function Test-Command { + param([string]$Name) + $null = Get-Command $Name -ErrorAction SilentlyContinue + return $? +} + +function Update-UvPath { + $candidateDirs = @() + + if ($HOME) { + $candidateDirs += Join-Path $HOME ".local\bin" + } + if ($env:USERPROFILE) { + $candidateDirs += Join-Path $env:USERPROFILE ".local\bin" + } + if ($env:LOCALAPPDATA) { + $candidateDirs += Join-Path $env:LOCALAPPDATA "uv" + } + + foreach ($dir in $candidateDirs) { + if ((Test-Path $dir) -and (($env:PATH -split ';') -notcontains $dir)) { + $env:PATH = "$dir;$env:PATH" + } + } +} + +function Install-Uv { + Info "uv was not found. Installing uv..." + + $tempScript = [System.IO.Path]::GetTempFileName() + ".ps1" + try { + Invoke-WebRequest -Uri "https://astral.sh/uv/install.ps1" -OutFile $tempScript -UseBasicParsing + & $tempScript + Update-UvPath + } catch { + Err "Failed to install uv." + Err "Please install uv manually: https://docs.astral.sh/uv/getting-started/installation/" + exit 1 + } finally { + Remove-Item $tempScript -ErrorAction SilentlyContinue + } +} + +if (-not (Test-Command "uv")) { + Install-Uv +} + +Update-UvPath + +if (-not (Test-Command "uv")) { + Err "uv was not found after installation." + Err "Please install uv manually: https://docs.astral.sh/uv/getting-started/installation/" + exit 1 +} + +Ok (& uv --version) +Info "Installing AstrBot with Python 3.12..." +uv tool install --python 3.12 astrbot +Ok "AstrBot has been installed." diff --git a/scripts/deploy-cli.sh b/scripts/deploy-cli.sh new file mode 100644 index 0000000000..16caa08e83 --- /dev/null +++ b/scripts/deploy-cli.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# deploy-cli.sh - Install astrbot with uv on Linux / macOS / WSL. + +set -euo pipefail + +if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[1;33m' + CYAN='\033[0;36m' + NC='\033[0m' +else + RED='' + GREEN='' + YELLOW='' + CYAN='' + NC='' +fi + +info() { echo -e "${CYAN}[INFO]${NC} $*"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } +ok() { echo -e "${GREEN}[OK]${NC} $*"; } +err() { echo -e "${RED}[ERROR]${NC} $*" >&2; } + +has() { + command -v "$1" >/dev/null 2>&1 +} + +refresh_uv_path() { + export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH" +} + +install_uv() { + info "uv was not found. Installing uv..." + + if has curl; then + curl -fsSL https://astral.sh/uv/install.sh | sh + refresh_uv_path + return + fi + + if has wget; then + wget -qO- https://astral.sh/uv/install.sh | sh + refresh_uv_path + return + fi + + err "curl or wget is required to install uv." + exit 1 +} + +if has uv; then + UV_BIN="uv" +else + install_uv + UV_BIN="uv" +fi + +if ! has "$UV_BIN"; then + err "uv was not found after installation." + err "Please install uv manually: https://docs.astral.sh/uv/getting-started/installation/" + exit 1 +fi + +ok "$("$UV_BIN" --version)" +info "Installing AstrBot with Python 3.12..." +"$UV_BIN" tool install --python 3.12 astrbot +ok "AstrBot has been installed." diff --git a/tests/test_main.py b/tests/test_main.py index 0f20016aeb..8ed21f7b10 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -67,7 +67,9 @@ def test_check_env_appends_user_site_packages_after_runtime_paths(monkeypatch): monkeypatch.setattr(sys, "version_info", _version_info(3, 12)) monkeypatch.setattr("main.get_astrbot_root", lambda: astrbot_root) - monkeypatch.setattr("main.get_astrbot_site_packages_path", lambda: site_packages_path) + monkeypatch.setattr( + "main.get_astrbot_site_packages_path", lambda: site_packages_path + ) monkeypatch.setattr("main.get_astrbot_config_path", lambda: "/tmp/config") monkeypatch.setattr("main.get_astrbot_plugin_path", lambda: "/tmp/plugins") monkeypatch.setattr("main.get_astrbot_temp_path", lambda: "/tmp/temp") @@ -89,12 +91,16 @@ def test_check_env_does_not_append_duplicate_user_site_packages(monkeypatch): monkeypatch.setattr(sys, "version_info", _version_info(3, 12)) monkeypatch.setattr("main.get_astrbot_root", lambda: astrbot_root) - monkeypatch.setattr("main.get_astrbot_site_packages_path", lambda: site_packages_path) + monkeypatch.setattr( + "main.get_astrbot_site_packages_path", lambda: site_packages_path + ) monkeypatch.setattr("main.get_astrbot_config_path", lambda: "/tmp/config") monkeypatch.setattr("main.get_astrbot_plugin_path", lambda: "/tmp/plugins") monkeypatch.setattr("main.get_astrbot_temp_path", lambda: "/tmp/temp") monkeypatch.setattr("main.get_astrbot_knowledge_base_path", lambda: "/tmp/kb") - monkeypatch.setattr(sys, "path", [astrbot_root, *original_sys_path, site_packages_path]) + monkeypatch.setattr( + sys, "path", [astrbot_root, *original_sys_path, site_packages_path] + ) with mock.patch("os.makedirs"): check_env() @@ -176,7 +182,7 @@ async def test_check_dashboard_files_exists_but_version_mismatch(monkeypatch): await check_dashboard_files() mock_logger_warning.assert_called_once() call_args, _ = mock_logger_warning.call_args - assert "不符" in call_args[0] + assert "WebUI version mismatch" in call_args[0] @pytest.mark.asyncio From 3afff5cbffa820ef6de60382614e97c272c8fdb1 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 28 Apr 2026 14:32:56 +0800 Subject: [PATCH 5/7] =?UTF-8?q?docs:=20=E7=A7=BB=E9=99=A4=E4=B8=80?= =?UTF-8?q?=E8=A1=8C=E5=91=BD=E4=BB=A4=E5=BF=AB=E9=80=9F=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E9=83=A8=E5=88=86=EF=BC=8C=E7=AE=80=E5=8C=96=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/zh/deploy/astrbot/cli.md | 70 +---------------------------------- 1 file changed, 1 insertion(+), 69 deletions(-) diff --git a/docs/zh/deploy/astrbot/cli.md b/docs/zh/deploy/astrbot/cli.md index ded534c8b8..617c230289 100644 --- a/docs/zh/deploy/astrbot/cli.md +++ b/docs/zh/deploy/astrbot/cli.md @@ -5,80 +5,12 @@ > > 以下教程默认您的设备上已经安装 Python,并且版本 `>=3.10` - -## 🚀 一行命令快速部署 - -> [!TIP] -> 如果你希望一步到位,只需要一个命令就能完成从克隆到启动的全过程,请使用下面的脚本。 - -::: details Linux / macOS / WSL - -```bash -bash -c "$(curl -fsSL https://raw.githubusercontent.com/AstrBotDevs/AstrBot/master/docs/scripts/deploy-cli.sh)" -``` - -该脚本会自动完成以下步骤: -1. 检测并提示安装必要的依赖(git、curl、python>=3.12、uv) -2. 克隆 AstrBot 官方仓库 -3. 使用 `uv sync` 安装项目依赖 -4. 启动 AstrBot 并打印访问链接 - -> [!TIP] -> 你也可以克隆仓库后本地运行脚本: -> ```bash -> git clone https://github.com/AstrBotDevs/AstrBot.git -> cd AstrBot -> bash docs/scripts/deploy-cli.sh -> ``` - -::: details Windows(PowerShell 7+) - -> [!WARNING] -> 建议使用 [PowerShell 7](https://github.com/PowerShell/PowerShell/releases) 以获得最佳体验。 - -**原生 Windows(PowerShell 7+)一行命令:** - -```powershell -irm https://raw.githubusercontent.com/AstrBotDevs/AstrBot/master/docs/scripts/deploy-cli.ps1 | iex -``` - -> [!TIP] -> 你也可以克隆仓库后本地运行脚本: -> ```powershell -> git clone https://github.com/AstrBotDevs/AstrBot.git -> cd AstrBot -> .\docs\scripts\deploy-cli.ps1 -> ``` - -该脚本会自动完成以下步骤: -1. 检测并提示安装必要的依赖(git、curl、python>=3.12、uv) -2. 克隆 AstrBot 官方仓库 -3. 使用 `uv sync` 安装项目依赖 -4. 启动 AstrBot 并打印访问链接 - -**或者使用 WSL 调用 bash 脚本:** - -```powershell -# 克隆仓库后运行部署脚本(WSL 方式) -git clone https://github.com/AstrBotDevs/AstrBot.git; cd AstrBot; wsl bash docs/scripts/deploy-cli.sh -``` - -> [!NOTE] -> 如果你更喜欢在 Windows 原生环境运行,推荐使用上面的 PowerShell 脚本。WSL 方式适合需要在 Linux 环境下运行的用户,或使用 [Docker 部署](./docker.md)。 -::: - - ---- - - - - ## 下载/克隆仓库 如果你的电脑上安装了 `git`,你可以通过以下命令来下载源码: ```bash -git clone https://github.com/AstrBotDevs/AstrBot.git +git clone https://github.com/AstrBotDevs/AstrBot # 上面的代码默认会拉取最新的提交的源码,如果你需要拉取最新稳定发行版本的源码,可以使用以下命令: # git clone --depth=1 --branch $(git ls-remote --tags --sort='-v:refname' https://github.com/AstrBotDevs/AstrBot.git | head -n1 | awk -F/ '{print $3}') https://github.com/AstrBotDevs/AstrBot.git cd AstrBot From 2254e48e43286a79cf362e969ddebd551bc9511d Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 28 Apr 2026 14:41:24 +0800 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E4=BB=A5=E5=A4=8D=E5=88=B6=E9=83=A8=E7=BD=B2=20CLI=20?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=B9=B6=E6=9B=B4=E6=96=B0=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.gitignore | 2 ++ docs/package.json | 2 +- docs/scripts/copy-deploy-cli.mjs | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 docs/scripts/copy-deploy-cli.mjs diff --git a/docs/.gitignore b/docs/.gitignore index 3562259c05..3501354b26 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -4,3 +4,5 @@ venv/ node_modules/ .vitepress/cache *dist +scripts/deploy-cli.sh +scripts/deploy-cli.ps1 diff --git a/docs/package.json b/docs/package.json index 8d39e96738..48fdb55028 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,7 +1,7 @@ { "scripts": { "docs:dev": "vitepress dev --host", - "docs:build": "vitepress build", + "docs:build": "node scripts/copy-deploy-cli.mjs && vitepress build", "docs:preview": "vitepress preview" }, "devDependencies": { diff --git a/docs/scripts/copy-deploy-cli.mjs b/docs/scripts/copy-deploy-cli.mjs new file mode 100644 index 0000000000..087b1ae3c7 --- /dev/null +++ b/docs/scripts/copy-deploy-cli.mjs @@ -0,0 +1,21 @@ +import { chmod, copyFile, mkdir } from "node:fs/promises"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(scriptDir, "../.."); + +const files = [ + { name: "deploy-cli.sh", mode: 0o755 }, + { name: "deploy-cli.ps1", mode: 0o644 }, +]; + +await mkdir(scriptDir, { recursive: true }); + +for (const file of files) { + const source = resolve(repoRoot, "scripts", file.name); + const target = resolve(scriptDir, file.name); + await copyFile(source, target); + await chmod(target, file.mode); + console.log(`Copied ${file.name} to docs/scripts/`); +} From 0947ab6bea214ff7312c2065f945731df0cf1821 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 28 Apr 2026 14:45:19 +0800 Subject: [PATCH 7/7] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E6=B6=88=E6=81=AF=E4=BB=A5=E6=8F=90=E9=AB=98=E5=8F=AF?= =?UTF-8?q?=E8=AF=BB=E6=80=A7=EF=BC=8C=E7=BB=9F=E4=B8=80=E8=8B=B1=E6=96=87?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/config/astrbot_config.py | 2 +- astrbot/dashboard/server.py | 8 ++++---- tests/test_dashboard.py | 15 +++++++++++---- tests/unit/test_config.py | 7 +++++-- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/astrbot/core/config/astrbot_config.py b/astrbot/core/config/astrbot_config.py index dde7fe1c73..63236c1d8b 100644 --- a/astrbot/core/config/astrbot_config.py +++ b/astrbot/core/config/astrbot_config.py @@ -104,7 +104,7 @@ def check_config_integrity(self, refer_conf: dict, conf: dict, path=""): if key not in conf: # 配置项不存在,插入默认值 path_ = path + "." + key if path else key - logger.info("Config key missing; added default: %s", path_) + logger.info("Config key missing; added default.") new_conf[key] = value has_new = True elif conf[key] is None: diff --git a/astrbot/dashboard/server.py b/astrbot/dashboard/server.py index 19aac55893..280c83fe5f 100644 --- a/astrbot/dashboard/server.py +++ b/astrbot/dashboard/server.py @@ -322,7 +322,7 @@ def _resolve_dashboard_ssl_config( if not cert_file or not key_file: logger.warning( - "dashboard.ssl.enable 已启用,但未同时配置 cert_file 和 key_file,SSL 配置将不会生效。", + "dashboard.ssl.enable is set, but cert_file or key_file is missing. SSL disabled.", ) return False, {} @@ -330,12 +330,12 @@ def _resolve_dashboard_ssl_config( key_path = Path(key_file).expanduser() if not cert_path.is_file(): logger.warning( - f"dashboard.ssl.enable 已启用,但 SSL 证书文件不存在: {cert_path},SSL 配置将不会生效。", + f"dashboard.ssl.enable is set, but cert file is missing: {cert_path}. SSL disabled.", ) return False, {} if not key_path.is_file(): logger.warning( - f"dashboard.ssl.enable 已启用,但 SSL 私钥文件不存在: {key_path},SSL 配置将不会生效。", + f"dashboard.ssl.enable is set, but key file is missing: {key_path}. SSL disabled.", ) return False, {} @@ -348,7 +348,7 @@ def _resolve_dashboard_ssl_config( ca_path = Path(ca_certs).expanduser() if not ca_path.is_file(): logger.warning( - f"dashboard.ssl.enable 已启用,但 SSL CA 证书文件不存在: {ca_path},SSL 配置将不会生效。", + f"dashboard.ssl.enable is set, but CA cert file is missing: {ca_path}. SSL disabled.", ) return False, {} resolved_ssl_config["ca_certs"] = str(ca_path.resolve()) diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py index 6dc352057c..63ffbf4d8d 100644 --- a/tests/test_dashboard.py +++ b/tests/test_dashboard.py @@ -124,6 +124,12 @@ async def test_dashboard_ssl_missing_cert_and_key_falls_back_to_http( async def fake_serve(app, config, shutdown_trigger): return config + def capture(messages): + def append(message, *args): + messages.append(message % args if args else message) + + return append + try: core_lifecycle_td.astrbot_config["dashboard"]["ssl"] = { "enable": True, @@ -134,21 +140,22 @@ async def fake_serve(app, config, shutdown_trigger): monkeypatch.setattr("astrbot.dashboard.server.serve", fake_serve) monkeypatch.setattr( "astrbot.dashboard.server.logger.warning", - lambda message: warning_messages.append(message), + capture(warning_messages), ) monkeypatch.setattr( "astrbot.dashboard.server.logger.info", - lambda message: info_messages.append(message), + capture(info_messages), ) config = await server.run() assert getattr(config, "certfile", None) is None assert getattr(config, "keyfile", None) is None - assert any("cert_file 和 key_file" in message for message in warning_messages) assert any( - "正在启动 WebUI, 监听地址: http://" in message for message in info_messages + "cert_file or key_file is missing" in message + for message in warning_messages ) + assert any("Starting WebUI at http://" in message for message in info_messages) finally: core_lifecycle_td.astrbot_config["dashboard"] = original_dashboard_config diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index c49e97ca0f..c8f383a29f 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -303,14 +303,17 @@ def test_integrity_log_does_not_include_inserted_secret_value( with open(temp_config_path, "w", encoding="utf-8-sig") as f: json.dump(existing_config, f) - monkeypatch.setattr(astrbot_config.logger, "info", messages.append) + def capture_info(message, *args): + messages.append(message % args if args else message) + + monkeypatch.setattr(astrbot_config.logger, "info", capture_info) AstrBotConfig(config_path=temp_config_path, default_config=default_config) assert messages assert all("secret-value" not in message for message in messages) assert all("api_key" not in message for message in messages) - assert any("配置项不存在" in message for message in messages) + assert any("Config key missing" in message for message in messages) class TestConfigHotReload: