Files
my-botty/install.bat
alexpolo1 a2e2acfbde fix(install): install conda without admin + verify git download
install.bat could fail to auto-install conda on a fresh, non-admin
machine:

- winget install used the default (machine) scope, landing conda in
  %ProgramData% and requiring elevation. A normal double-click without
  admin failed silently and conda never installed. Add --scope user so
  it installs to %USERPROFILE%\miniforge3 with no admin needed.
- winget returns non-zero when the package is already present, so its
  exit code was unreliable. Rescan for conda.exe after winget and only
  fall through to the direct download when it is genuinely missing.
- The GitHub (git) download fallback never validated the file before
  running it: a truncated download or an HTML error page served with a
  200 would be launched as the "installer" and silently do nothing. Add
  a size check (<40 MB => failed download, clear error + bail) plus a
  pre-download cleanup of any stale temp file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-05 17:07:50 +02:00

417 lines
15 KiB
Batchfile

@echo off
setlocal enabledelayedexpansion
:: Always run from the folder this script lives in
cd /d "%~dp0"
echo ============================================
echo Botty - Dependency Installer
echo ============================================
echo.
:: --- Detect Windows version ---
for /f "tokens=3" %%A in ('reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" /v CurrentBuild 2^>nul') do set "WIN_BUILD=%%A"
if not defined WIN_BUILD (
echo WARNING: Could not detect Windows build number.
set "WIN_BUILD=0"
)
echo Detected Windows build: %WIN_BUILD%
set /a "WIN_BUILD_NUM=%WIN_BUILD%" 2>nul || set "WIN_BUILD_NUM=0"
if %WIN_BUILD_NUM% GEQ 22000 (
echo Detected Windows 11 -- using relative mouse mode ^(no ABSOLUTE flag^)
set "WIN11=1"
set "REQ_FILE=requirements-win11.txt"
set "ENV_FILE=environment-win11.yml"
) else (
echo Detected Windows 10 or earlier — using absolute mouse mode
set "WIN11=0"
set "REQ_FILE=requirements-win10.txt"
set "ENV_FILE=environment-win10.yml"
)
if not exist "%REQ_FILE%" set "REQ_FILE=requirements.txt"
if not exist "%ENV_FILE%" set "ENV_FILE=environment.yml"
echo Requirements profile: %REQ_FILE%
echo Environment profile: %ENV_FILE%
echo.
:: --- Find conda ---
:: Miniforge3 JustMe default: %LOCALAPPDATA%\miniforge3 (newer) or
:: %USERPROFILE%\miniforge3 (older)
set "CONDA_EXE="
for %%C in (
"%LOCALAPPDATA%\miniforge3\Scripts\conda.exe"
"%USERPROFILE%\miniforge3\Scripts\conda.exe"
"%LOCALAPPDATA%\miniconda3\Scripts\conda.exe"
"%USERPROFILE%\miniconda3\Scripts\conda.exe"
"%USERPROFILE%\anaconda3\Scripts\conda.exe"
"%ProgramData%\miniforge3\Scripts\conda.exe"
"%ProgramData%\miniconda3\Scripts\conda.exe"
"%ProgramData%\anaconda3\Scripts\conda.exe"
) do (
if exist %%C (
set "CONDA_EXE=%%~C"
goto :found_conda
)
)
echo conda not found — installing Miniforge3 automatically...
echo This is a one-time setup (~100 MB). Please wait.
echo.
:: --- Method 1: winget (cleanest; available on Win10 1709+ and all Win11) ---
:: --scope user is critical: without it winget defaults to a machine-wide
:: install (lands in %ProgramData%) which REQUIRES admin. A normal double-click
:: (no elevation) then fails silently and conda never installs. --scope user
:: installs to %USERPROFILE%\miniforge3 with no admin needed — which is also one
:: of the locations :rescan_conda searches.
winget --version >nul 2>&1
if %errorlevel% equ 0 (
echo Installing via winget...
winget install --id CondaForge.Miniforge3 --exact --silent --scope user ^
--accept-package-agreements --accept-source-agreements
:: winget returns non-zero when the package is already installed, so don't
:: trust the exit code — rescan for conda and only fall through to the
:: direct download if it's genuinely still missing.
for %%C in (
"%LOCALAPPDATA%\miniforge3\Scripts\conda.exe"
"%USERPROFILE%\miniforge3\Scripts\conda.exe"
"%ProgramData%\miniforge3\Scripts\conda.exe"
) do if exist %%C goto :rescan_conda
echo winget did not produce a usable conda — falling back to direct download.
echo.
)
:: --- Method 2: direct download + silent NSIS install ---
:: Uses curl (built into Win10/11) with PowerShell as fallback.
:: $ProgressPreference=SilentlyContinue is critical — without it PowerShell's
:: progress bar drops download speed from ~50 MB/s to ~1 MB/s.
:: start /wait "" is required: plain "installer /S" can return before install
:: finishes. /D= intentionally omitted — NSIS /D= breaks on paths with spaces.
set "MF_INSTALLER=%TEMP%\Miniforge3-installer.exe"
set "MF_URL=https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Windows-x86_64.exe"
del /q "%MF_INSTALLER%" >nul 2>&1
echo Downloading Miniforge3...
curl -Lk --progress-bar "%MF_URL%" -o "%MF_INSTALLER%" 2>&1
if %errorlevel% neq 0 (
echo curl failed, trying PowerShell...
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"$ProgressPreference='SilentlyContinue'; try { Invoke-WebRequest -Uri '%MF_URL%' -OutFile '%MF_INSTALLER%' -UseBasicParsing; exit 0 } catch { Write-Host $_.Exception.Message; exit 1 }"
)
if not exist "%MF_INSTALLER%" (
echo.
echo ERROR: Could not download Miniforge3. Check your internet connection, or
echo install it manually then re-run install.bat:
echo https://github.com/conda-forge/miniforge/releases/latest
echo.
pause
exit /b 1
)
:: Verify the download is a real installer, not a truncated file or an HTML
:: error page served with a 200. The Miniforge installer is ~78 MB; anything
:: under 40 MB means the download failed even though a file exists.
for %%A in ("%MF_INSTALLER%") do set "MF_SIZE=%%~zA"
if not defined MF_SIZE set "MF_SIZE=0"
if %MF_SIZE% LSS 41943040 (
echo.
echo ERROR: Downloaded Miniforge3 installer is only %MF_SIZE% bytes — the
echo download was incomplete or blocked. Check your internet/proxy, or install
echo manually then re-run install.bat:
echo https://github.com/conda-forge/miniforge/releases/latest
echo.
del /q "%MF_INSTALLER%" >nul 2>&1
pause
exit /b 1
)
echo Installing Miniforge3 (this takes ~1 minute)...
start /wait "" "%MF_INSTALLER%" /S /InstallationType=JustMe /AddToPath=0 /RegisterPython=0 /NoShortcuts=1 /NoRegistry=1
if %errorlevel% neq 0 (
echo.
echo ERROR: Miniforge3 installer failed (exit code %errorlevel%).
echo Try running it manually: %MF_INSTALLER%
echo.
pause
exit /b 1
)
del /q "%MF_INSTALLER%" >nul 2>&1
echo Miniforge3 installed.
:rescan_conda
echo.
:: Re-scan for conda now that Miniforge is installed
for %%C in (
"%LOCALAPPDATA%\miniforge3\Scripts\conda.exe"
"%USERPROFILE%\miniforge3\Scripts\conda.exe"
"%LOCALAPPDATA%\miniconda3\Scripts\conda.exe"
"%USERPROFILE%\miniconda3\Scripts\conda.exe"
"%USERPROFILE%\anaconda3\Scripts\conda.exe"
"%ProgramData%\miniforge3\Scripts\conda.exe"
"%ProgramData%\miniconda3\Scripts\conda.exe"
"%ProgramData%\anaconda3\Scripts\conda.exe"
) do (
if exist %%C (
set "CONDA_EXE=%%~C"
goto :found_conda
)
)
echo ERROR: Miniforge3 installed but conda.exe still not found. Please restart
echo the installer or contact support.
pause
exit /b 1
:found_conda
echo Found conda: %CONDA_EXE%
echo.
:: --- Test conda works ---
echo Testing conda...
"%CONDA_EXE%" --version >nul 2>&1
if %errorlevel% neq 0 (
echo.
echo ERROR: conda found but failed to run. Your conda installation may be broken.
echo Try re-installing Miniforge: https://github.com/conda-forge/miniforge
echo.
pause
exit /b 1
)
echo conda is working.
echo.
:: --- Find botty Python ---
set "PYTHON="
for %%C in (
"%LOCALAPPDATA%\miniforge3\envs\botty\python.exe"
"%LOCALAPPDATA%\miniconda3\envs\botty\python.exe"
"%USERPROFILE%\miniforge3\envs\botty\python.exe"
"%USERPROFILE%\miniconda3\envs\botty\python.exe"
"%USERPROFILE%\anaconda3\envs\botty\python.exe"
"%ProgramData%\miniforge3\envs\botty\python.exe"
"%ProgramData%\miniconda3\envs\botty\python.exe"
"%ProgramData%\anaconda3\envs\botty\python.exe"
) do (
if exist %%C (
set "PYTHON=%%~C"
goto :check_env
)
)
:: --- Create botty env ---
echo Creating 'botty' conda environment...
"%CONDA_EXE%" env create -f "%ENV_FILE%"
if %errorlevel% neq 0 (
echo.
echo ERROR: conda env create failed. See output above.
pause
exit /b 1
)
:check_env
:: Re-scan for Python after env creation
set "PYTHON="
for %%C in (
"%LOCALAPPDATA%\miniforge3\envs\botty\python.exe"
"%LOCALAPPDATA%\miniconda3\envs\botty\python.exe"
"%USERPROFILE%\miniforge3\envs\botty\python.exe"
"%USERPROFILE%\miniconda3\envs\botty\python.exe"
"%USERPROFILE%\anaconda3\envs\botty\python.exe"
"%ProgramData%\miniforge3\envs\botty\python.exe"
"%ProgramData%\miniconda3\envs\botty\python.exe"
"%ProgramData%\anaconda3\envs\botty\python.exe"
) do (
if exist %%C (
set "PYTHON=%%~C"
goto :env_ready
)
)
echo.
echo ERROR: botty env was created but python.exe was not found.
pause
exit /b 1
:env_ready
echo Botty Python: %PYTHON%
:: --- Install pip requirements ---
:: --progress-bar off: pip's progress bar uses \r to redraw in place; when this
:: script's output is redirected to a file (e.g. run_install_capture.bat) those
:: redraws become millions of lines and the log balloons to many GB. Turning the
:: bar off keeps the install log small without hiding warnings/errors.
echo.
echo Installing Botty requirements from %REQ_FILE%...
"%CONDA_EXE%" run -n botty python -m pip install --progress-bar off -r "%REQ_FILE%"
if %errorlevel% neq 0 (
echo.
echo ERROR: pip requirements install failed for %REQ_FILE%.
pause
exit /b 1
)
echo.
echo Botty osdetect:
"%CONDA_EXE%" run -n botty python -c "import sys; sys.path.insert(0, 'src'); from utils.os_detect import describe_current_os; print(describe_current_os())"
:: --- OCR setup ---
:: Two OCR backends, tried in order at runtime:
:: 1. tesserocr — C extension calling tesseract DLLs directly (fast)
:: 2. pytesseract — subprocess calling tesseract.exe (always works if exe exists)
:: At least one must work. Install.bat sets up both and reports which is ready.
echo.
echo Setting up OCR...
:: Find the botty env directory
set "BOTTY_ENV_DIR="
for %%C in (
"%LOCALAPPDATA%\miniforge3\envs\botty"
"%LOCALAPPDATA%\miniconda3\envs\botty"
"%USERPROFILE%\miniforge3\envs\botty"
"%USERPROFILE%\miniconda3\envs\botty"
"%USERPROFILE%\anaconda3\envs\botty"
"%ProgramData%\miniforge3\envs\botty"
"%ProgramData%\miniconda3\envs\botty"
"%ProgramData%\anaconda3\envs\botty"
) do (
if exist %%C (
set "BOTTY_ENV_DIR=%%~C"
goto :env_found
)
)
echo ERROR: Could not find botty env directory.
pause
exit /b 1
:env_found
set "TESS_PATH=%BOTTY_ENV_DIR%\Library\bin;%BOTTY_ENV_DIR%\Library\lib;%BOTTY_ENV_DIR%\DLLs;%BOTTY_ENV_DIR%\Scripts"
:: --- Backend 1: tesserocr (fast path, best-effort) ---
:: tesserocr uses the C++ API of tesseract and needs an MSVC-compiled tesseract DLL.
:: The bundled dependencies folder provides:
:: tesseract52.dll — MSVC tesseract 5.2.0 (conda-forge, compiled vs2019)
:: tesserocr.cp310-...pyd — MSVC tesserocr 2.5.2 linked against tesseract52.dll
:: (both require leptonica-1.78.0.dll from the conda tesseract=4.x install below)
:: If any step fails the bot still works via pytesseract below.
:: Remove any existing pip/wheel tesserocr
"%PYTHON%" -m pip uninstall tesserocr -y >nul 2>&1
:: conda tesseract 4.x provides leptonica-1.78.0.dll (MSVC) which tesseract52.dll needs
"%CONDA_EXE%" install -n botty "tesseract=4.*" -c conda-forge -y >nul 2>&1
:: conda tesseract.exe crashes on Win10 and Win11 — disable it, keep the DLLs
if exist "%BOTTY_ENV_DIR%\Library\bin\tesseract.exe" (
rename "%BOTTY_ENV_DIR%\Library\bin\tesseract.exe" "tesseract_disabled.exe" >nul 2>&1
)
:: Deploy the MSVC tesseract52.dll (tesserocr.pyd imports exactly this name)
if exist "dependencies\tesseract52.dll" (
copy /y "dependencies\tesseract52.dll" "%BOTTY_ENV_DIR%\Library\bin\tesseract52.dll" >nul
)
:: Deploy the conda-forge tesserocr .pyd (compiled against tesseract52.dll)
if exist "dependencies\tesserocr.cp310-win_amd64.pyd" (
copy /y "dependencies\tesserocr.cp310-win_amd64.pyd" "%BOTTY_ENV_DIR%\lib\site-packages\tesserocr.cp310-win_amd64.pyd" >nul
) else if exist "dependencies\tesserocr-2.5.2-cp310-cp310-win_amd64.whl" (
"%CONDA_EXE%" run -n botty python -m pip install --quiet --force-reinstall --no-deps "dependencies\tesserocr-2.5.2-cp310-cp310-win_amd64.whl" >nul 2>&1
)
:: --- Backend 2: pytesseract (reliable fallback) ---
:: Needs tesseract.exe from winget. Works on any Python version.
:: On Win10: winget may not be available -- if install fails, offer manual link.
"%CONDA_EXE%" run -n botty python -m pip install --progress-bar off pytesseract >nul 2>&1
if %errorlevel% neq 0 (
echo WARNING: Could not install pytesseract Python wrapper.
)
if not exist "C:\Program Files\Tesseract-OCR\tesseract.exe" (
echo Installing Tesseract OCR via winget...
winget install --id tesseract-ocr.tesseract --silent --accept-package-agreements --accept-source-agreements 2>nul
if !errorlevel! neq 0 (
echo winget failed -- on Windows 10 you may need to install manually:
echo https://github.com/tesseract-ocr/tesseract/releases
echo Download the win64 installer, run it, keep the default install path.
)
)
:: --- Verify OCR: at least one backend must work ---
echo.
echo Checking OCR backends...
set "PATH=%TESS_PATH%;%PATH%"
set "OCR_READY=0"
"%CONDA_EXE%" run -n botty python -c "import os; os.add_dll_directory(r'%BOTTY_ENV_DIR%\Library\bin'); from tesserocr import PyTessBaseAPI, OEM; PyTessBaseAPI(psm=7,oem=OEM.LSTM_ONLY,path='assets/tessdata',lang='hover-eng_inconsolata_inv_th_fast').End()" >nul 2>&1
if %errorlevel% == 0 (
echo tesserocr: OK ^(fast path^)
set "OCR_READY=1"
) else (
echo tesserocr: not available ^(DLL issue -- bot will use pytesseract instead^)
)
if exist "C:\Program Files\Tesseract-OCR\tesseract.exe" (
"%CONDA_EXE%" run -n botty python -c "import pytesseract; pytesseract.pytesseract.tesseract_cmd=r'C:\Program Files\Tesseract-OCR\tesseract.exe'; pytesseract.get_tesseract_version()" >nul 2>&1
if !errorlevel! == 0 (
echo pytesseract: OK ^(reliable fallback^)
set "OCR_READY=1"
) else (
echo pytesseract: tesseract.exe found but call failed
)
) else (
echo pytesseract: tesseract.exe not found -- install from link above if needed
)
if "%OCR_READY%"=="0" (
echo.
echo WARNING: No OCR backend is working. Item hover text will not be read.
echo Install Tesseract OCR: https://github.com/tesseract-ocr/tesseract/releases
echo Then re-run install.bat.
)
:: --- Verify all dependencies ---
echo.
echo Verifying all dependencies...
set "ALL_OK=1"
for %%M in (cv2 mss numpy transitions rapidfuzz pydantic pytesseract yaml) do (
set "PATH=%TESS_PATH%;%PATH%"
"%CONDA_EXE%" run -n botty python -c "import %%M" >nul 2>&1
if !errorlevel! neq 0 (
echo FAILED: %%M
set "ALL_OK=0"
) else (
echo %%M: OK
)
)
:: discord.py imports aiohttp which calls ssl.create_default_context() at import
:: time. A malformed cert in the Windows store triggers ASN1 NOT_ENOUGH_DATA.
:: The bot patches this at runtime via _patch_ssl(); check import with the patch.
set "PATH=%TESS_PATH%;%PATH%"
"%CONDA_EXE%" run -n botty python -c "import ssl; ssl.SSLContext.load_default_certs = lambda *a, **kw: None; import discord" >nul 2>&1
if !errorlevel! neq 0 (
echo FAILED: discord
set "ALL_OK=0"
) else (
echo discord: OK
)
if "%OCR_READY%"=="0" set "ALL_OK=0"
if "%ALL_OK%"=="1" (
echo.
echo All dependencies verified.
) else (
echo.
echo WARNING: Some dependencies failed above.
echo This is often a PATH issue. Try running from a fresh CMD/PowerShell:
echo conda activate botty
echo python -c "import cv2"
echo.
)
:install_done
echo.
echo ============================================
echo Installation complete!
echo Run botty with: run_botty.bat
echo ============================================
echo.