Every fatal message told the user THAT something failed but not what to do
about it. The worst was "conda env create failed. See output above." --
useless when run_install_capture.bat redirects that output to a 56 KB log.
Each error now names the likely cause and the concrete next step:
- download failed -> the URL tried, firewall/proxy hint, manual-install
fallback that install.bat will detect on re-run
- truncated download -> got N bytes vs expected ~78 MB, bad file deleted
- installer failed -> antivirus/UAC hint, how to run it by hand
- conda found but dead -> the exact command to reproduce the real error
- env create failed -> disk/network/antivirus causes, plus the
"env remove -n botty -y" recovery for a half
finished install
- pip install failed -> notes the env itself is fine and a re-run resumes
- python.exe missing -> explains partial env, gives the recovery commands
- find_python.bat -> distinguishes "never installed" from "install.bat
did not finish", pointing at the capture log
Added a shared ":fail" exit so every fatal path ends with how to produce a
full log for a bug report, and states that nothing else was changed.
Also added a disk-space pre-flight before env creation: under 3 GB now
fails immediately with a clear message instead of letting conda die halfway
through with an opaque error; 3-6 GB warns. The environment needs ~4 GB
plus ~1 GB of downloads.
Verified: install.bat still completes with exit 0; the pre-flight was
exercised at real, simulated-2 GB and simulated-5 GB levels and all three
branches render and exit correctly; find_python.bat still resolves; 140
tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
install.bat had 9 pause statements on failure paths and none on the
success path. The README tells users to double-click install.bat, so on a
successful install the console vanished the moment it finished -- a new
user never saw "Installation complete", the OCR verdict, or the dependency
verification, and had no way to tell whether it had worked.
Added pause to the success path.
run_install_capture.bat redirects stdout to install_log.txt, so that new
pause would have blocked behind the redirect: an empty window silently
waiting on a keypress the user cannot see. It now feeds stdin from nul,
reports success/failure with the log path, and pauses itself.
Verified non-interactive: run_install_capture.bat completes in ~28s with
exit 0 and no hang.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tesserocr never loaded -- install.bat always reported "tesserocr: not
available (DLL issue)" and the bot ran on the pytesseract fallback, which
shells out to tesseract.exe per OCR call instead of using the in-process
C++ API.
Root cause, found by walking the import table with pefile:
tesserocr.pyd -> tesseract52.dll -> leptonica-1.78.0.dll -> tiff.dll
-> libdeflate.dll <- MISSING
Current conda-forge libdeflate (>=1.20) installs the library as
"deflate.dll", but the older tiff.dll from the tesseract=4.x stack still
imports the previous name "libdeflate.dll". Nothing provided that name, so
tiff.dll failed to load and every DLL above it failed with WinError 126
("The specified module could not be found") -- which is why the error
looked like a missing module even though every file was present.
Fix: install libdeflate explicitly alongside tesseract=4.*, then copy
deflate.dll to the legacy name libdeflate.dll when that name is absent.
Same library, same exports.
Verified: removing the alias reproduces the failure exactly; running
install.bat recreates it and the installer now reports "tesserocr: OK
(fast path)". tesserocr initialises and performs real OCR with both bundled
models, and the bot logs "OCR backend: tesserocr (primary)" at startup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Testing the direct-download fallback with Tesseract absent and winget
unavailable showed the official installer self-elevates and its elevated
relaunch discards /D=, so it always installs machine-wide to
"C:\Program Files\Tesseract-OCR" regardless of TS_DEST.
The previous comment claimed this path gave a per-user install needing no
admin rights, which is not true: there is no per-user install path with
the official Tesseract installer, and it requires admin/UAC. Corrected the
comment rather than the code -- /D= is harmless as best-effort, and both
find_tesseract and src\d2r_image\ocr.py already search the machine-wide
and per-user locations, so either outcome works at runtime.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Miniforge direct-download fallback -- the only path available on a
clean machine without winget -- could never complete. install.bat aborted
with ". was unexpected at this time." immediately after running the
Miniforge installer, so conda was installed but the botty env was never
created and the bot was unusable.
Cause: line 133 echoed "(exit code %errorlevel%)" inside a parenthesised
if-block. An unescaped ")" inside a block terminates the block, leaving
"." as a stray token. cmd parses the entire if-block when it reaches it,
so this fired even when the installer SUCCEEDED and the block body was
never meant to run -- verified with a minimal repro: the unescaped form
exits 255 on a false condition, the escaped form exits 0.
Fix: escape as ^(exit code %errorlevel%^), matching the convention the
rest of the file already uses ("^(fast path^)"). Audited every echo
inside a block; this was the only remaining unescaped instance.
Found by running install.bat with winget removed from PATH to simulate a
clean Windows 10 machine.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Live clean-install test caught a syntax bug in the new Tesseract setup
block: install.bat died with "so was unexpected at this time." right
after "Setting up OCR...", so OCR setup and the whole dependency
verification stage never ran.
Cause: "::" comment lines placed INSIDE parenthesised if-blocks. Two
problems compound there -- a "::" line inside a ( ) block is itself a
parse error, and any parenthesis in the comment text closes the block
early. The text "(non-zero when already installed), so after each" left
"so" as a stray token.
Fix: move every comment out of the parenthesised blocks, in both the
Tesseract block and the conda winget block added earlier. The conda one
had survived only because its text happened to contain no parentheses.
Verified: install.bat now runs to completion with exit 0 --
Tesseract: C:\Program Files\Tesseract-OCR\tesseract.exe
pytesseract: OK (tesserocr: DLL issue, expected)
cv2/mss/numpy/transitions/rapidfuzz/pydantic/pytesseract/yaml/discord: OK
All dependencies verified.
140 tests pass against the freshly created env.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow-up to the conda scope fix: the same admin/winget assumptions broke
the OCR backend, which is what actually carries item text reading since
tesserocr's MSVC DLL chain commonly fails to load.
- install.bat installed Tesseract via `winget install` with no --scope,
i.e. machine-wide into "C:\Program Files", which requires admin. On a
clean non-admin box this failed and left NO working OCR backend at all
(tesserocr already fails), so OCR_READY=0 and item reading was dead.
Now: winget machine scope -> winget --scope user -> direct download of
the official NSIS installer with a per-user /D= target. Also stops
trusting winget's exit code (non-zero when already installed) and
re-resolves tesseract.exe after each attempt.
- The downloaded installer is size-checked (~50 MB; <20 MB = failed
download) before being executed, matching the Miniforge handling.
- Added a :find_tesseract subroutine that resolves tesseract.exe from
Program Files, Program Files (x86), %LOCALAPPDATA%\Programs,
%ProgramData% and PATH. Verification now uses the resolved path instead
of the hardcoded "C:\Program Files" one.
- ocr.py: added Program Files (x86) and the per-user
%LOCALAPPDATA%\Programs\Tesseract-OCR location to the runtime search
order, since per-user installs are not on PATH.
- run_botty.bat: only export PYTESSERACT_TESSERACT_CMD when the file
exists, falling back to the per-user path, so a stale machine-wide
value cannot shadow a valid per-user install.
Verified on this machine: all install.bat dependency imports OK
(cv2/mss/numpy/transitions/rapidfuzz/pydantic/pytesseract/yaml/discord),
pytesseract resolves tesseract 5.5.0, osdetect reports the win11 profile,
config loads, 140 tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
A single bot session produced a 22 GB log. The file logger used daily-only
rotation (TimedRotatingFileHandler when='midnight') with NO size cap, so a
long/spammy session grew log.txt unbounded within a day. Its archiver also
looked for .1/.2 backups that the timed handler never produced.
- logger.py: switch to size-based RotatingFileHandler — log.txt rotates at
50 MB (override via BOTTY_LOG_MAX_MB), keeps 5 zipped backups, and prunes
log/archive/ to 30 zips. Hard cap on both the live file and total disk.
The .1/.2 naming now matches what the handler emits, so archiving works.
- install.bat: pip --progress-bar off. The progress bar redraws via \r;
redirected to a file (run_install_capture.bat) those redraws became
millions of lines — the other way an install log balloons to GBs.
- params.ini: document the log.txt cap + BOTTY_LOG_MAX_MB.
Verified: with a tiny cap, log.txt stayed under the limit while rotated
files zipped to archive; full suite 80 passed / 2 skipped.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- win_input.py: detect OS build via platform.win32_ver(), use
MOUSEEVENTF_ABSOLUTE only on Win10 (build < 22000)
- install.bat: detect and display Windows version on install
The custom tesserocr-2.5.2 wheel was compiled on a specific machine
against DLL versions that do not match a fresh conda-forge installation.
This caused persistent ImportError: DLL load failed regardless of PATH
or LoadLibraryExW approach.
conda-forge's tesserocr package is compiled against the exact same
conda-forge tesseract/leptonica binaries, so all DLL dependencies
are automatically satisfied within the conda environment — no manual
DLL path manipulation needed.
Changes:
- environment.yml: add tesserocr + tesseract as conda-forge packages,
remove leptonica pin (no longer needed), remove custom wheel from pip
- install.bat: replace wheel force-reinstall with pip uninstall cleanup
- src/*.py: simplify DLL fix to os.add_dll_directory only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Every approach to manually replicating conda's DLL environment from
Python code or batch PATH manipulation has failed. conda run activates
the environment exactly like "conda activate botty" — setting PATH,
running activate.d scripts, and properly resolving all transitive DLL
dependencies for tesseract51.dll.
run_botty.bat now derives conda.exe from the botty python.exe path
(two levels up: envs/botty -> envs -> miniforge3 -> Scripts/conda.exe)
and uses "conda run -n botty --no-capture-output python src/main.py".
install.bat smoke test now uses "%CONDA_EXE% run -n botty python -c ..."
which already has CONDA_EXE set from the install step.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
os.add_dll_directory alone is not enough — LOAD_LIBRARY_SEARCH_USER_DIRS
does not propagate to transitive deps of deps when loaded automatically
by the OS (e.g. tesseract51.dll's deps like mingw runtimes, leptonica).
LoadLibraryExW with LOAD_LIBRARY_SEARCH_DEFAULT_DIRS (0x1000) explicitly
propagates user DLL dir search to the entire transitive dep chain, so
leptonica, libgcc, libstdc++, zlib etc. are all found in Library\bin
and Library\mingw-w64\bin without conda activate.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ctypes.WinDLL uses LoadLibraryW which does NOT search user DLL dirs
registered via os.add_dll_directory/AddDllDirectory. It was throwing
FileNotFoundError and blocking the import before tesserocr was ever tried.
Python 3.8+ loads .pyd files with LOAD_LIBRARY_SEARCH_USER_DIRS which
DOES search user-registered dirs for the pyd and all its transitive DLL
dependencies. os.add_dll_directory(Library\bin) alone is sufficient.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
os.environ['PATH'] set from inside Python does not affect the Windows
DLL loader used by ctypes.WinDLL — the loader reads the process PATH
at load time, not from Python's env dict. Set PATH in the .bat files
before python.exe is launched so tesseract51.dll's transitive deps
(leptonica, zlib, libpng, etc.) are findable by the loader.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When ctypes.WinDLL loads tesseract51.dll by absolute path, Windows
resolves that DLL's own transitive deps using the standard search order:
app-dir → System32 → Windows → cwd → PATH. Library\bin is in none of
those (conda activate was not run), so leptonica, zlib, libpng, etc.
are invisible and the load fails even though the DLLs are all present.
Fix: prepend all conda DLL dirs to os.environ['PATH'] before the
ctypes.WinDLL call so the standard DLL search finds them. os.add_dll_directory
is still called for Python's LOAD_LIBRARY_SEARCH_USER_DIRS path.
Together the three steps guarantee the import works without conda activate:
1. os.add_dll_directory - for .pyd loading
2. os.environ PATH - for ctypes transitive dep resolution
3. ctypes.WinDLL(abs) - pre-cache tesseract so .pyd reuses it
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
os.add_dll_directory alone is not enough on Windows. When Python loads
the tesserocr .pyd via LOAD_LIBRARY_SEARCH_USER_DIRS, Windows finds
tesseract51.dll in the added directory but then resolves tesseract's own
transitive deps (leptonica, zlib, libpng etc.) using only the standard
system search path -- not the user DLL dirs. Those libs live in
Library\bin, not System32, so they're invisible and the load fails even
though every DLL is present.
Fix: call ctypes.WinDLL(absolute_path_to_tesseract51.dll) before the
tesserocr import. LoadLibraryW with a full path anchors tesseract51.dll
to Library\bin, so Windows searches that directory for its transitive
deps. The already-loaded DLL is then returned from cache when the .pyd
requests it, making the import succeed.
Applied to ocr.py (test entry point), main.py, and shopper.py.
Also updated install.bat smoke test and diagnostic to use the same fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The custom tesserocr wheel links against leptonica-1.78.0.dll at
compile time. Unpinned leptonica on conda-forge resolves to 1.82+
which installs leptonica-1.82.0.dll — a different filename — so
Windows DLL loader cannot find it regardless of os.add_dll_directory.
Also force-reinstall the wheel in install.bat to guarantee the
correct binary is used (not a stale cached version), and add a
diagnostic that prints which DLLs are actually present when the
smoke test fails so the root cause is visible instead of a vague
warning.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- README: add step-by-step Installation section (Miniforge → download
ZIP → install.bat → config → run_botty.bat) so a non-technical user
can follow it without reading development.md
- config/params.ini: reset personal fields (name, char_name,
saved_games_folder) to generic defaults so the downloaded zip
works out of the box for anyone
- install.bat: apply os.add_dll_directory before the tesserocr smoke
test so it stops emitting a false warning on every install
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
install.bat:
- Add conda self-test (conda --version) before env create
- Verify botty python.exe exists after env creation
- Smoke-test key imports (cv2, tesserocr, discord, etc.)
CI (.github/workflows/ci.yml):
- Add test_setup_bat_files.py to test matrix
test/test_setup_bat_files.py (7 tests):
- All expected .bat files exist
- No hardcoded usernames (alex, alexpolo, ultimate) in run scripts
- No absolute home paths in run scripts (must use %~dp0 / %USERNAME%)
- All run_*.bat source find_python.bat (no duplicated conda detection)
- find_python.bat checks >= 6 conda locations
- install.bat has conda self-test and python verification
Without cd /d "%~dp0", conda resolves environment.yml relative to
wherever the user launched the bat from (e.g. C:\Windows\system32).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>