diff --git a/eve_orehold_watcher.py b/eve_orehold_watcher.py index 5db5e05..5568958 100644 --- a/eve_orehold_watcher.py +++ b/eve_orehold_watcher.py @@ -82,28 +82,56 @@ def save_region(cp, left, top, width, height): cp.write(f) +RATE_PATH = os.path.join(HERE, ".mining_rate") + + +def write_rate(m3_per_min, cur, cap): + """Publish the live ore-hold fill rate so the rock watcher can estimate how long + until the asteroid you're on is depleted. Written every OCR tick.""" + try: + with open(RATE_PATH, "w") as f: + json.dump({"rate_m3min": round(m3_per_min, 1), "cur": cur, "cap": cap, + "ts": int(time.time())}, f) + except Exception: + pass + + # --------------------------------------------------------------------------- # # Delivery # --------------------------------------------------------------------------- # -_BOT_STATE = {"t": 0, "muted": False} +_BOT_STATE = {"t": 0, "muted": False, "mining": False} -def bot_muted(cp): - """Coordinate with the Discord bot: poll the shared alert-state it publishes, so a - `!mute` in Discord also silences these local watchers. Cached 30s.""" - url = cp.get("coordination", "bot_state_url", fallback="").strip() \ - if cp.has_section("coordination") else "" - if not url: - return False +DEFAULT_STATE_URL = ("https://git.armoredarmadillo.com/brockdarnold/eve-watcher/" + "raw/branch/main/alert_state.json") + + +def _bot_state(cp): + """Poll the shared alert-state the Discord bot publishes (mute/quiet/mining), + cached 30s, so a Discord `!mute` / `!mining` also drives the local watchers. + Defaults to the public alert_state.json (no config needed).""" + url = (cp.get("coordination", "bot_state_url", fallback="").strip() + if cp.has_section("coordination") else "") or DEFAULT_STATE_URL if time.time() - _BOT_STATE["t"] < 30: - return _BOT_STATE["muted"] + return _BOT_STATE try: - d = json.loads(urllib.request.urlopen(url, timeout=5).read()) + req = urllib.request.Request(url, headers={"User-Agent": "eve-watcher"}) + d = json.loads(urllib.request.urlopen(req, timeout=5).read()) _BOT_STATE["muted"] = bool(d.get("muted")) + _BOT_STATE["mining"] = bool(d.get("mining")) _BOT_STATE["t"] = time.time() except Exception: pass - return _BOT_STATE["muted"] + return _BOT_STATE + + +def bot_muted(cp): + return _bot_state(cp)["muted"] + + +def bot_mining(cp): + """True when a mining session is active (started via `!mining on` in Discord).""" + return _bot_state(cp)["mining"] def notify(cp, title, message, priority="high", tags="rock,bell"): @@ -287,6 +315,7 @@ def run_ocr(cp): last_grow = time.time() last_stall_alert = 0.0 misses = 0 + samples = [] # recent (t, cur) for the live fill-rate estimate while True: try: img = grab_region(region) @@ -297,6 +326,14 @@ def run_ocr(cp): cur, cap = parsed pct = 100.0 * cur / cap print(f"[ocr] {cur}/{cap} m3 ({pct:.1f}%) armed={armed}") + # --- live fill rate (m3/min) over a ~90s window -> .mining_rate --- + now = time.time() + samples.append((now, cur)) + samples = [(t, c) for (t, c) in samples if now - t <= 90 and c <= cur] + if len(samples) >= 2 and (samples[-1][0] - samples[0][0]) >= 20: + dc = samples[-1][1] - samples[0][1] + dt = samples[-1][0] - samples[0][0] + write_rate(dc / dt * 60 if dc > 0 else 0.0, cur, cap) if pct < reset_pct: armed = True # --- still mining? (hold should be growing) ---