Skip to content

Commit f33ab5b

Browse files
committed
优化win10以下启动脚本兼容
1 parent 8b0966e commit f33ab5b

2 files changed

Lines changed: 146 additions & 59 deletions

File tree

modules/image_hosting/beds/cnb.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ async def upload(self, image_data, filename='image.png', **kwargs):
116116
'verification': verification,
117117
}
118118
except Exception as exc:
119-
log.warning(f'CNB 上传失败: {exc}')
120119
return (False, str(exc))
121120

122121
async def upload_url(self, image_data, filename='image.png', **kwargs):

start.bat

Lines changed: 146 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,45 @@
11
@echo off
2-
setlocal
3-
cd /d "%~dp0"
4-
set "ELAINABOT_WINDOWS_LAUNCHER=%~f0"
5-
set "ELAINABOT_ROOT=%~dp0"
6-
set "ELAINABOT_FIRST_ARGUMENT=%~1"
7-
set "ELAINABOT_SECOND_ARGUMENT=%~2"
8-
chcp 65001 >nul
9-
set "PYTHONUTF8=1"
10-
set "PYTHONIOENCODING=utf-8"
11-
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -OutputFormat Text -EncodedCommand JABsAD0ARwBlAHQALQBDAG8AbgB0AGUAbgB0ACAALQBMAGkAdABlAHIAYQBsAFAAYQB0AGgAIAAkAGUAbgB2ADoARQBMAEEASQBOAEEAQgBPAFQAXwBXAEkATgBEAE8AVwBTAF8ATABBAFUATgBDAEgARQBSACAALQBFAG4AYwBvAGQAaQBuAGcAIABVAFQARgA4ADsAJABtAD0AWwBhAHIAcgBhAHkAXQA6ADoASQBuAGQAZQB4AE8AZgAoACQAbAAsACcAIwAgAIVRTF0gAFAAbwB3AGUAcgBTAGgAZQBsAGwAIADjTgF4JwApADsAaQBmACgAJABtAC0AbAB0ADAAKQB7AHQAaAByAG8AdwAgACcAKmd+YjBShVFMXYR2IABQAG8AdwBlAHIAUwBoAGUAbABsACAA404BeAIwJwB9ADsAJABwAD0AJABsAFsAKAAkAG0AKwAxACkALgAuACgAJABsAC4AQwBvAHUAbgB0AC0AMQApAF0ALQBqAG8AaQBuAFsARQBuAHYAaQByAG8AbgBtAGUAbgB0AF0AOgA6AE4AZQB3AEwAaQBuAGUAOwAmACgAWwBzAGMAcgBpAHAAdABiAGwAbwBjAGsAXQA6ADoAQwByAGUAYQB0AGUAKAAkAHAAKQApAA==
12-
set "ELAINABOT_EXIT_CODE=%ERRORLEVEL%"
13-
exit /b %ELAINABOT_EXIT_CODE%
2+
@setlocal
3+
@cd /d "%~dp0"
4+
@set "ELAINABOT_WINDOWS_LAUNCHER=%~f0"
5+
@set "ELAINABOT_ROOT=%~dp0"
6+
@set "ELAINABOT_FIRST_ARGUMENT=%~1"
7+
@set "ELAINABOT_SECOND_ARGUMENT=%~2"
8+
@set "PYTHONUTF8=1"
9+
@set "PYTHONIOENCODING=utf-8"
10+
@powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -OutputFormat Text -EncodedCommand JABsAD0ARwBlAHQALQBDAG8AbgB0AGUAbgB0ACAALQBMAGkAdABlAHIAYQBsAFAAYQB0AGgAIAAkAGUAbgB2ADoARQBMAEEASQBOAEEAQgBPAFQAXwBXAEkATgBEAE8AVwBTAF8ATABBAFUATgBDAEgARQBSACAALQBFAG4AYwBvAGQAaQBuAGcAIABVAFQARgA4ADsAJABtAD0AWwBhAHIAcgBhAHkAXQA6ADoASQBuAGQAZQB4AE8AZgAoACQAbAAsACcAIwAgAIVRTF0gAFAAbwB3AGUAcgBTAGgAZQBsAGwAIADjTgF4JwApADsAaQBmACgAJABtAC0AbAB0ADAAKQB7AHQAaAByAG8AdwAgACcAKmd+YjBShVFMXYR2IABQAG8AdwBlAHIAUwBoAGUAbABsACAA404BeAIwJwB9ADsAJABwAD0AJABsAFsAKAAkAG0AKwAxACkALgAuACgAJABsAC4AQwBvAHUAbgB0AC0AMQApAF0ALQBqAG8AaQBuAFsARQBuAHYAaQByAG8AbgBtAGUAbgB0AF0AOgA6AE4AZQB3AEwAaQBuAGUAOwAmACgAWwBzAGMAcgBpAHAAdABiAGwAbwBjAGsAXQA6ADoAQwByAGUAYQB0AGUAKAAkAHAAKQApAA==
11+
@set "ELAINABOT_EXIT_CODE=%ERRORLEVEL%"
12+
@if not "%ELAINABOT_EXIT_CODE%"=="0" (
13+
@echo(
14+
@echo [ElainaBot] Startup failed with exit code %ELAINABOT_EXIT_CODE%.
15+
@echo [ElainaBot] Review the error above, then press any key to close this window...
16+
@pause >nul
17+
)
18+
@exit /b %ELAINABOT_EXIT_CODE%
1419

1520
# 内嵌 PowerShell 代码
1621
$Utf8Encoding = New-Object Text.UTF8Encoding($false)
17-
[Console]::InputEncoding = $Utf8Encoding
18-
[Console]::OutputEncoding = $Utf8Encoding
19-
$OutputEncoding = $Utf8Encoding
22+
$WindowsVersion = [Environment]::OSVersion.Version
23+
$UseLegacyWindowsPath = $WindowsVersion.Major -lt 10
24+
$UseSystemBrowserPanel = $UseLegacyWindowsPath
25+
$ProgressPreference = 'SilentlyContinue'
2026
$env:PYTHONUTF8 = '1'
21-
$env:PYTHONIOENCODING = 'utf-8'
27+
$env:PYTHONIOENCODING = [Console]::OutputEncoding.WebName
28+
[Net.ServicePointManager]::SecurityProtocol =
29+
[Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
2230

2331
function Write-ConsoleLine {
2432
param(
2533
[Parameter(Mandatory = $true)][string]$Message,
2634
[ConsoleColor]$Color = [Console]::ForegroundColor
2735
)
2836

29-
if ([Console]::IsOutputRedirected) {
30-
[Console]::WriteLine($Message)
31-
return
32-
}
33-
34-
$previousColor = [Console]::ForegroundColor
3537
try {
36-
[Console]::ForegroundColor = $Color
37-
[Console]::WriteLine($Message)
38-
} finally {
39-
[Console]::ForegroundColor = $previousColor
38+
[Console]::Out.WriteLine($Message)
39+
} catch {
40+
# Some legacy consoles cannot accept Unicode writes; keep the failure readable.
41+
$asciiMessage = [Text.RegularExpressions.Regex]::Replace($Message, '[^\x00-\x7F]', '?')
42+
try { [Console]::Out.WriteLine($asciiMessage) } catch { }
4043
}
4144
}
4245

@@ -71,7 +74,8 @@ Set-StrictMode -Version Latest
7174
$ErrorActionPreference = 'Stop'
7275

7376
$BootstrapVersion = '5'
74-
$PythonVersion = '3.13'
77+
$MinimumPythonVersion = '3.11'
78+
$ManagedPythonVersion = '3.13'
7579
$DefaultPythonInstallMirror = 'https://registry.npmmirror.com/-/binary/python-build-standalone'
7680
$PythonInstallMirror = if (-not [string]::IsNullOrWhiteSpace($env:ELAINABOT_PYTHON_MIRROR)) {
7781
$env:ELAINABOT_PYTHON_MIRROR.Trim().TrimEnd('/')
@@ -99,7 +103,7 @@ function Invoke-Checked {
99103
[Parameter(Mandatory = $true)][string[]]$Arguments
100104
)
101105

102-
& $FilePath @Arguments | Out-Host
106+
& $FilePath @Arguments
103107
if ($LASTEXITCODE -ne 0) {
104108
throw "命令执行失败,退出代码 ${LASTEXITCODE}:$FilePath $($Arguments -join ' ')"
105109
}
@@ -109,7 +113,7 @@ function Invoke-PipInstall {
109113
param([Parameter(Mandatory = $true)][string[]]$Arguments)
110114

111115
Write-Step '正在优先使用清华 PyPI 镜像安装依赖...'
112-
& $VenvPython -m pip install --disable-pip-version-check --index-url $PipMirror @Arguments | Out-Host
116+
& $VenvPython -m pip install --disable-pip-version-check --index-url $PipMirror @Arguments
113117
if ($LASTEXITCODE -eq 0) {
114118
return
115119
}
@@ -133,20 +137,16 @@ function Get-CommandPath {
133137
function Test-PythonCandidate {
134138
param(
135139
[string]$FilePath,
136-
[string[]]$BaseArguments = @(),
137-
[string]$RequiredVersion = ''
140+
[string[]]$BaseArguments = @()
138141
)
139142

140143
if (-not $FilePath) {
141144
return $null
142145
}
143146
$previousPreference = $ErrorActionPreference
144147
$ErrorActionPreference = 'Continue'
145-
$versionCheck = "import sys; raise SystemExit(0 if sys.version_info >= (3, 11) else 1)"
146-
if ($RequiredVersion) {
147-
$parts = $RequiredVersion.Split('.')
148-
$versionCheck = "import sys; raise SystemExit(0 if sys.version_info[:2] == ($($parts[0]), $($parts[1])) else 1)"
149-
}
148+
$minimumParts = $MinimumPythonVersion.Split('.')
149+
$versionCheck = "import sys; raise SystemExit(0 if sys.version_info >= ($($minimumParts[0]), $($minimumParts[1])) else 1)"
150150
try {
151151
& $FilePath @BaseArguments -c $versionCheck *> $null
152152
$probeExitCode = $LASTEXITCODE
@@ -169,15 +169,15 @@ function Test-PythonCandidate {
169169
function Find-PreferredPython {
170170
$pyLauncher = Get-CommandPath 'py'
171171
if ($pyLauncher) {
172-
$candidate = Test-PythonCandidate -FilePath $pyLauncher -BaseArguments @('-3.13') -RequiredVersion $PythonVersion
172+
$candidate = Test-PythonCandidate -FilePath $pyLauncher -BaseArguments @('-3')
173173
if ($candidate) {
174174
return $candidate
175175
}
176176
}
177177

178-
foreach ($name in @('python3.13', 'python3', 'python')) {
178+
foreach ($name in @('python', 'python3', 'python3.14', 'python3.13', 'python3.12', 'python3.11')) {
179179
$path = Get-CommandPath $name
180-
$candidate = Test-PythonCandidate -FilePath $path -RequiredVersion $PythonVersion
180+
$candidate = Test-PythonCandidate -FilePath $path
181181
if ($candidate) {
182182
return $candidate
183183
}
@@ -220,12 +220,26 @@ function Ensure-Uv {
220220
New-Item -ItemType Directory -Path $ToolsDir -Force | Out-Null
221221
$installerPath = Join-Path ([IO.Path]::GetTempPath()) 'elainabot-uv-install.ps1'
222222
try {
223-
Invoke-WebRequest -Uri 'https://astral.sh/uv/install.ps1' -OutFile $installerPath
223+
Invoke-WebRequest -UseBasicParsing -Uri 'https://astral.sh/uv/install.ps1' -OutFile $installerPath
224224
$env:UV_INSTALL_DIR = $ToolsDir
225225
$env:UV_NO_MODIFY_PATH = '1'
226-
Invoke-Checked 'powershell.exe' @(
227-
'-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $installerPath
228-
)
226+
# The uv installer redraws progress in place. Capturing its output avoids
227+
# duplicated Chinese status text in both Windows 11 and legacy cmd.exe.
228+
$previousPreference = $ErrorActionPreference
229+
try {
230+
$ErrorActionPreference = 'Continue'
231+
$installerOutput = @(& powershell.exe @(
232+
'-NoLogo', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $installerPath
233+
) 2>&1)
234+
$installerExitCode = $LASTEXITCODE
235+
} finally {
236+
$ErrorActionPreference = $previousPreference
237+
}
238+
if ($installerExitCode -ne 0) {
239+
$details = ($installerOutput | Select-Object -Last 5 | ForEach-Object { $_.ToString() }) -join ' '
240+
if ([string]::IsNullOrWhiteSpace($details)) { $details = '未返回详细错误信息' }
241+
throw "项目专用的 Python 环境引导工具安装失败,退出代码 ${installerExitCode}:${details}"
242+
}
229243
} finally {
230244
Remove-Item -LiteralPath $installerPath -Force -ErrorAction SilentlyContinue
231245
}
@@ -249,17 +263,32 @@ function Install-ManagedPython {
249263
$null
250264
}
251265
try {
252-
Remove-Item Env:UV_NO_PROGRESS -ErrorAction SilentlyContinue
253-
Write-Step "[1/6] 正在通过镜像下载项目专用的 Python $PythonVersion(将显示实时进度)..."
254-
& $UvPath --color always python install --no-bin --no-registry --mirror $PythonInstallMirror $PythonVersion | Out-Host
255-
if ($LASTEXITCODE -eq 0) {
266+
$env:UV_NO_PROGRESS = '1'
267+
Write-Step "[1/6] 正在通过镜像下载项目专用的 Python $ManagedPythonVersion..."
268+
$previousPreference = $ErrorActionPreference
269+
try {
270+
$ErrorActionPreference = 'Continue'
271+
$mirrorOutput = @(& $UvPath --color never python install --no-bin --no-registry --mirror $PythonInstallMirror $ManagedPythonVersion 2>&1)
272+
$mirrorExitCode = $LASTEXITCODE
273+
} finally {
274+
$ErrorActionPreference = $previousPreference
275+
}
276+
if ($mirrorExitCode -eq 0) {
256277
return
257278
}
258279

259280
Write-Step 'Python 镜像下载失败,正在切换到官方源...'
260-
& $UvPath --color always python install --no-bin --no-registry $PythonVersion | Out-Host
261-
if ($LASTEXITCODE -ne 0) {
262-
throw "Python $PythonVersion 下载失败,镜像源和官方源均不可用。"
281+
try {
282+
$ErrorActionPreference = 'Continue'
283+
$officialOutput = @(& $UvPath --color never python install --no-bin --no-registry $ManagedPythonVersion 2>&1)
284+
$officialExitCode = $LASTEXITCODE
285+
} finally {
286+
$ErrorActionPreference = $previousPreference
287+
}
288+
if ($officialExitCode -ne 0) {
289+
$details = (($mirrorOutput + $officialOutput) | Select-Object -Last 5 | ForEach-Object { $_.ToString() }) -join ' '
290+
if ([string]::IsNullOrWhiteSpace($details)) { $details = '未返回详细错误信息' }
291+
throw "Python $ManagedPythonVersion 下载失败,镜像源和官方源均不可用:${details}"
263292
}
264293
} finally {
265294
if ($hadNoProgress) {
@@ -280,7 +309,7 @@ function Backup-InvalidVenv {
280309
}
281310

282311
function Ensure-VirtualEnvironment {
283-
Write-Step '[1/6] 正在检查 Python 3.11 或更高版本...'
312+
Write-Step "[1/6] 正在检查 Python $MinimumPythonVersion 或更高版本..."
284313
if (Test-Path -LiteralPath $VenvPython) {
285314
$existing = Test-PythonCandidate $VenvPython
286315
if ($existing) {
@@ -293,7 +322,7 @@ function Ensure-VirtualEnvironment {
293322
Backup-InvalidVenv
294323
$python = Find-PreferredPython
295324
if ($python) {
296-
Write-Step "[1/6] 已找到 Python 3.13:$($python.Version)"
325+
Write-Step "[1/6] 已找到兼容的 Python:$($python.Version)"
297326
Write-Step '[2/6] 正在创建项目虚拟环境:.venv...'
298327
& $python.FilePath @($python.Arguments) -m venv $VenvDir
299328
if ($LASTEXITCODE -eq 0 -and (Test-Path -LiteralPath $VenvPython)) {
@@ -311,7 +340,7 @@ function Ensure-VirtualEnvironment {
311340
Install-ManagedPython -UvPath $uvPath
312341
Write-Step '[2/6] 正在创建项目虚拟环境:.venv...'
313342
Invoke-Checked $uvPath @(
314-
'venv', '--python', $PythonVersion, '--managed-python', '--no-python-downloads', $VenvDir
343+
'venv', '--python', $ManagedPythonVersion, '--managed-python', '--no-python-downloads', $VenvDir
315344
)
316345
if (-not (Test-Path -LiteralPath $VenvPython)) {
317346
throw '虚拟环境创建结束,但未找到可用的 python.exe。'
@@ -425,6 +454,11 @@ function Test-WebPanelDependency {
425454
}
426455

427456
function Ensure-WebPanelDependency {
457+
if ($UseSystemBrowserPanel) {
458+
Write-Step '[5/6] 当前 Windows 不支持内嵌桌面窗口,将使用外部浏览器打开管理面板。'
459+
return
460+
}
461+
428462
Write-Step '[5/6] 正在检查 Windows 桌面窗口组件...'
429463
if (Test-WebPanelDependency) {
430464
Write-Step '[5/6] Windows 桌面窗口组件已经安装,无需重复安装。'
@@ -528,7 +562,58 @@ function Test-WebPanelAvailable {
528562
function Start-WebPanelWindow {
529563
param([Parameter(Mandatory = $true)][string]$Url)
530564

531-
$windowSource = @'
565+
$windowSource = if ($UseSystemBrowserPanel) {
566+
@'
567+
import os
568+
import shutil
569+
import subprocess
570+
import sys
571+
import time
572+
import urllib.request
573+
574+
panel_url = sys.argv[1]
575+
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
576+
for _ in range(120):
577+
try:
578+
with opener.open(panel_url, timeout=2):
579+
pass
580+
break
581+
except Exception:
582+
time.sleep(1)
583+
else:
584+
raise SystemExit(1)
585+
586+
browser_candidates = (
587+
'msedge.exe',
588+
'chrome.exe',
589+
'firefox.exe',
590+
'iexplore.exe',
591+
)
592+
browser_paths = []
593+
for name in browser_candidates:
594+
resolved = shutil.which(name)
595+
if resolved:
596+
browser_paths.append(resolved)
597+
598+
for base_name in ('PROGRAMFILES', 'PROGRAMFILES(X86)', 'LOCALAPPDATA'):
599+
base_path = os.environ.get(base_name)
600+
if not base_path:
601+
continue
602+
browser_paths.extend((
603+
os.path.join(base_path, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
604+
os.path.join(base_path, 'Google', 'Chrome', 'Application', 'chrome.exe'),
605+
os.path.join(base_path, 'Mozilla Firefox', 'firefox.exe'),
606+
))
607+
608+
for browser_path in browser_paths:
609+
if os.path.isfile(browser_path):
610+
subprocess.Popen([browser_path, panel_url])
611+
raise SystemExit(0)
612+
613+
raise SystemExit(2)
614+
'@
615+
} else {
616+
@'
532617
import sys
533618
import time
534619
import urllib.request
@@ -556,6 +641,7 @@ webview.create_window(
556641
)
557642
webview.start()
558643
'@
644+
}
559645

560646
$encodedSource = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($windowSource))
561647
$launcherSource = "import base64;exec(base64.b64decode('$encodedSource'))"
@@ -587,18 +673,20 @@ try {
587673
if (-not (Test-WebPanelAvailable -Url $panelUrl)) {
588674
throw "配置端口 $panelPort 已被占用,但未检测到 ElainaBot 管理面板。请检查端口占用情况。"
589675
}
590-
Write-Step "[6/6] 检测到框架已经运行,仅重新打开桌面面板窗口"
676+
Write-Step "[6/6] 检测到框架已经运行,仅重新打开管理面板"
591677
$existingPanelWindow = Start-WebPanelWindow -Url $panelUrl
592678
if ($existingPanelWindow) {
593679
$existingPanelWindow.Dispose()
594680
}
595-
Write-Step '桌面面板窗口已打开,无需重新启动框架。'
681+
Write-Step '管理面板已打开,无需重新启动框架。'
596682
exit 0
597683
}
598684

599685
Write-Step "[6/6] 配置端口 $panelPort 尚未开启,正在启动 ElainaBot 框架..."
600-
Write-Step '面板就绪后将自动打开 ElainaBot 桌面窗口。'
601-
$panelWindow = Start-WebPanelWindow -Url $panelUrl
686+
Write-Step '面板就绪后将自动打开 ElainaBot 管理面板。'
687+
# Keep the framework in the foreground on every supported Windows version
688+
# so this console remains available for runtime logs and diagnostics.
689+
$panelWindow = Start-WebPanelWindow -Url $panelUrl
602690
try {
603691
& $VenvPython (Join-Path $RootDir 'main.py')
604692
$frameworkExitCode = $LASTEXITCODE

0 commit comments

Comments
 (0)