OpenSSL 3.x + corrupted Windows cert store causes aiohttp to crash
at import time. Monkey-patch ssl.SSLContext.load_default_certs to
fall back to certifi when Windows store fails.
traverse_nodes previously gave up after one random-guess move when a node
template wasn't on screen, failing every travel path where the char was
mis-positioned (the A5-after-Pindle stranding was the worst case). Now,
on timeout during travel pathing (timeout > 3.1s, so boss approaches keep
their fast fail), it walks an expanding 12-step sweep, re-scanning for the
node each step, and only fails once the sweep is exhausted. Makes ALL
node pathing self-healing, not just A5.
Co-Authored-By: Claude Fable 5 <[email protected]>
The A5 waypoint was unopenable after a Pindle return: the char lands in
the NE near Anya, where the town_start->WP node templates are off-screen,
so the pather random-guessed and timed out. Pathing to NPC spots
(Qual-Kehk/Malah) DOES work from there, and each has a defined path to
the WP. New open_wp: direct -> re-anchor via Qual-Kehk/town_start/Malah
-> directed sweep that re-attempts node pathing + full-screen WP scan
each step. This is the linchpin: it unblocks the A5->A4 vendor redirect,
maintenance, stash, and the Diablo run's travel out of A5.
Co-Authored-By: Claude Fable 5 <[email protected]>
CRITICAL loot-loss bug: buy_consumables failure called end_game and
returned BEFORE the stash step, so Pindle runes/items piled in inventory
and were never banked (zero stash events across a whole session). Buying
pots is optional (belt refills from drops); stashing loot is the point.
Now warn + re-anchor + fall through to stash. Also raise
max_maintenance_time_s 120->240 so the A5 vendor thrash completes and
reaches stash instead of timing out first (transitional — disappears once
a Diablo run shifts spawns to A4).
Co-Authored-By: Claude Fable 5 <[email protected]>
A5-spawn games burn on the Malah vendor step (stale patch templates);
A4-spawn games sail through Jamella. The Diablo run ends with a TP to
A4 town, so running it LAST makes every next game spawn in A4.
Co-Authored-By: Claude Fable 5 <[email protected]>
The blind ESC after a failed sweep click opens the ESC game menu when no
dialogue was actually open (seen covering the screen during the Tyrael
hunt). Detect SaveAndExit after the ESC and close the menu again.
Co-Authored-By: Claude Fable 5 <[email protected]>
Diablo died in 22s but picked_up_items was false: his death animation /
lingering effects register as visible targets, so the mobs-alive guard
skipped his drops. pick_up_items(force=True) at the Diablo loot site.
Co-Authored-By: Claude Fable 5 <[email protected]>
The sweep for Tyrael clicked Deckard Cain (stands in the same zone) and
left his TALK dialogue open, blinding the rest of the sweep. Also sweep
the full search area as a second pass since NPCs can stand outside the
stored ROI, with a total time budget.
Co-Authored-By: Claude Fable 5 <[email protected]>
- OCR reads FISTMAN as "fabiman" — SequenceMatcher >= 0.6 on the first
word of the row handles the mangling
- start_bot_detached.bat kills existing main.py instances first: F11/F12
are global hotkeys, so duplicate bots receive every press and fight
each other (one starts while the other pauses/exits)
Co-Authored-By: Claude Fable 5 <[email protected]>
Body templates went stale with the June 2026 patch (Malah, Cain,
Tyrael, Larzuk, Qual-Kehk all undetectable), but name tags on hover
score 0.9+ reliably. When the body-template hunt times out, sweep a
coarse hover grid over the NPC ROI and click wherever the name tag
appears.
Co-Authored-By: Claude Fable 5 <[email protected]>
User-tuned detection thresholds and a safety guard against teleporting
into live packs during loot phases.
Co-Authored-By: Claude Fable 5 <[email protected]>
A misclick on the stash chest (Cain stands beside it) opens the stash
UI which covers most of the screen and blinds every template search:
NPC hunts, A5_WP selection, act detection. The old guard pressed the
inventory key, which leaves a stash panel open — ESC closes both.
Panels are now closed at hunt start, per hover-loop iteration, and
before WP traversal.
Co-Authored-By: Claude Fable 5 <[email protected]>
Malah wanders and her body templates predate the patch; the hunt fails
most games and the failed-hunt position strands the char. Jamella is
static and detects at 0.99+. Travel cost ~30s vs a lost game.
Co-Authored-By: Claude Fable 5 <[email protected]>
A full CS clear takes ~10 min; the 600s watchdog force-quit a game that
had all three seals open and was waiting for Diablo to spawn.
Co-Authored-By: Claude Fable 5 <[email protected]>
After a failed NPC hunt the char can be in a corner where no path nodes
anchor; both node traverses fail and the game dies. Walk a search
pattern across the small town scanning the whole screen for the WP
template directly each step.
Co-Authored-By: Claude Fable 5 <[email protected]>
- Seal 36 has two visual variants; dia_a2y4_36b_closed.png existed in
assets but was never searched, so the B variant could never be clicked
(8 sealdance tries then aborted the run)
- info_failed_seal_ screenshot name contained ": " (illegal on Windows)
producing 0-byte files
Co-Authored-By: Claude Fable 5 <[email protected]>
After Vizier/De Seis are dead, a failed recalibration traverse aborted
the whole Diablo run (threw away two boss kills at 13:43). The seal
stage re-anchors at the pentagram via template loop immediately after,
so just skip the extra loot pass and continue.
Co-Authored-By: Claude Fable 5 <[email protected]>
- town_manager.identify: return the reached A5 location when Cain fails
after successful travel (returning False made bot.py fall back to the
pre-travel act and run wrong-act pathing all maintenance long)
- town_manager.resurrect: leave last_known_loc breadcrumb when the A4
revive fails after travel; bot.py consumes it to re-anchor
- bot.py: alternate vendor retry prefers A4 Jamella (static, reliable)
over A1; Malah wanders and is the main A5 vendor failure
Co-Authored-By: Claude Fable 5 <[email protected]>
- diablo.py _layoutcheck: ambiguous/lost layout check now loops back to
the pentagram and retries the approach once before aborting the run;
template checks bumped from single-frame 0.1s to 0.5s
- hammerdin.py _cast_hammers: an unbound aura (e.g. conviction in
kill_diablo on a non-Infinity build) silently cast NOTHING, so Diablo
was never attacked with the damage aura. Fall back to concentration
and always cast hammers.
Co-Authored-By: Claude Fable 5 <[email protected]>
First encounter at char select blindly saved whichever character was
highlighted, locking in a wrong character for the whole session. Now,
when char_name is configured, select it via OCR first and warn if the
saved template name does not match.
Co-Authored-By: Claude Fable 5 <[email protected]>
Root causes from the 2026-06-09 session (51 games, 45 failed):
- Act desync: add TownManager.detect_current_act() and
Bot._verify_town_location(); every maintenance/end-run retry and
fallback now verifies the physical act instead of hardcoding town
starts. open_wp/go_to_act self-heal act mismatches. Never run A1
pathing when travel to A1 failed.
- NPC dialogue: poll action buttons up to 2.5s after click instead of
a single-frame check (premature retry click was closing the dialog).
- Stats integrity: log_end_game skips duplicate calls (phantom 0s
"successful" games were resetting the consecutive-fail breaker);
clear stale failure reason at game start; set chicken flag before
bot.stop() so chickens are no longer labeled "Bot stopped".
- Repair: prefer in-act Larzuk over cross-act Halbu trip (Halbu
detection failed 100% last session and desynced the act state).
Documented as Bugs 9-12 in CLAUDE.md.
Co-Authored-By: Claude Fable 5 <[email protected]>
NPC body templates find Akara/Malah correctly but the name tag templates
score ~0.28, below the 0.35 threshold, causing 20s spinning when the NPC
is detected but the hover confirmation never passes.
Three targeted changes in open_npc_menu():
- Use a 240x140px ROI directly above the hover cursor for name tag search
instead of the full screen, containing false-positive risk
- Lower name tag threshold from 0.35 to 0.26 so Akara at ~0.28 now passes
- Check ScreenObjects.NPCDialogue after click as primary confirmation;
this is UI-state based and immune to stale template images
- Reduce per-NPC search timeout from 20s to 8s for faster failure recovery
Also lower max_maintenance_time_s from 120 to 60 to cap total stuck time.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Reliability
- All run approach() methods now set approach_fail_step before every return False;
bot.py _run_wrapper reads it and includes [step: X] in Discord/log failure reasons
- on_maintenance() sets _maintenance_step before each town step (heal, identify,
buy_consumables, stash_items, repair, resurrect_merc, gamble) for the same coverage
- on_init() logs startup line: char=X | difficulty=Y | routes=[...] each game
Per-run disable (bot runs as long as possible)
- GameStats tracks per-run failure counts across Bot instances (previously reset each game)
- After disable_run_after_failures consecutive failures a run is disabled for the session;
game-level consecutive-fail counter resets so the bot continues on remaining routes
- game_controller no longer quits on max_consecutive_fails if active routes remain;
only exits when all routes are disabled
Maintenance timeout (params.ini: max_maintenance_time_s=120)
- Hard 120 s wall on the entire town maintenance loop; checked between each major step
and before every retry (buy_consumables, stash, repair, resurrect_merc, gamble)
- On timeout: error screenshot + Discord, then trigger end_game → save-and-exit → rejoin
Bug fixes
- Win11 mouse overshoot: mouse_move() uses SetCursorPos + zero-delta MOUSEEVENTF_MOVE
- _curr_loc = True propagation: TownManager.identify() now returns the act Location enum
- DAMAGED KeyError in pickit: added ItemQualityKeyword.Damaged.value to NTIP_ALIAS_QUALITY_MAP
- A4 WP interaction range: force-move character to WP stone before select_by_template
- NPC click blocked by equipped-area guard: open_npc_menu() closes inventory if open
Docs
- Added CLAUDE.md: AI working guide with step tables, bug history, debugging tips
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
open_npc_menu timeout reduced 35s → 20s and now saves a screenshot when
it gives up. buy_consumables and stash failures after their retry now
trigger end_game (save & exit) instead of silently continuing with a bad
location, preventing the bot from running the next game in a broken state.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
When restart_d2r_when_stuck is enabled, spawn a fresh Python process
(same main.py) and exit immediately rather than killing and relaunching
D2R. The new bot detects D2R is already running and skips launching it,
preserving the game session. D2R is only killed on deliberate exits
(safe_exit) and the initial auto_login launch.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
After using Cain to identify items, self._curr_loc was set to True (boolean)
instead of the actual location. Every subsequent get_act_from_location(True)
returned None, causing buy_consumables and stash to bail immediately and
fall back to A1 even when the character was physically in A5.
Now returns curr_loc when identification succeeds in the current act, or
new_loc (the A5 WP location) when the A5 Cain fallback is used — so the
bot correctly routes to Malah and the A5 stash instead of navigating back
to Akara.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Adds Messenger.send_error across the Discord (red embed with screenshot attached)
and generic (text-only) APIs. Bot._save_error_screenshot now also pushes the
failure to the configured messenger after saving the screenshot to disk, so each
approach/battle/exception failure is reviewable in Discord with the visual.
Gated by new config discord_log_errors ([general], default 1) and the
[discord_events] error toggle. Both optional/backward-compatible. Verified wiring
and the suppression path via a stubbed messenger.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
When every run has been auto-disabled there are no routes left to run, so save a
session report and shut down via safe_exit() instead of calling restart_or_exit
(which would needlessly restart D2R into empty games when restart_d2r_when_stuck
is enabled).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Recovery: each run now has its own consecutive-failure counter. After
disable_run_after_failures (default 5) consecutive failures, that single run is
disabled for the rest of the session and the bot keeps doing the other runs
instead of stopping. A success resets the counter. If every run is disabled the
bot stops for investigation. Disable is in-memory only (restart re-enables).
Diagnostics: on every run failure (approach, battle, or exception) the bot saves
a timestamped screenshot to log/screenshots/error/ named with the run, reason,
and game/run counters so logs and visuals can be cross-referenced. Gated by the
new error_screenshots config (falls back to info_screenshots). The error/ dir is
routed through log rotation.
Adds config keys error_screenshots and disable_run_after_failures (both optional,
backward compatible) and docs/recovery_and_error_logging.md. Verified bot startup,
config parsing, Bot construction, the disable/reset logic, and screenshot writing.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Every silent return False in a1-a5 town act files (resurrect, heal, identify,
open_wp, open_trade_menu, open_stash, open_trade_and_repair_menu, gamble) now
emits Logger.error with the specific step that failed (traverse, NPC menu open,
button press, panel visibility). Also adds Logger.error to all run approach()
methods (arcane, shenk_eld, trav, andariel, countess, mephisto, baal, pindle,
nihlathak) for open_wp, use_wp, go_to_act, and traverse failures. Fixes a4.py
open_trade_menu to use LeftPanel check instead of unreliable GoldBtnVendor.
town_manager fallback go_to_act calls (resurrect, identify, open_stash, gamble,
stash, heal) now log when they fail.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Problems:
1. use_wp() fired immediately after open_wp() — WP panel animation
still playing, WaypointTabs template not yet visible → silent fail
2. vizier.py + diablo.py ignored use_wp() return value → approach()
returned a truthy Location even when the WP was never used, causing
battle() to run from the wrong map location
3. go_to_act() also ignored use_wp() return value
Fixes:
- waypoint.use_wp(): retry WaypointTabs detection up to 4x (1.6s total)
before giving up; log each retry + the target WP and act being switched
- vizier/diablo approach(): check use_wp() return, return False on failure
- town_manager.go_to_act(): check use_wp() return, log + return False
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>