eve-watcher/update.py
2026-06-14 07:23:24 +00:00

161 lines
6.1 KiB
Python

#!/usr/bin/env python3
"""Self-installing / self-updating entry point — `python update.py`.
Run it once (the install one-liner does this for you). From then on a Windows
Scheduled Task runs it automatically **at logon and once a day**, so the watchers
stay up to date and running with zero further thought. Each run it:
1. makes config.ini on first run (webhook prefilled; gitignored so your edits
and OCR snip survive every update),
2. `git pull`s the latest watcher code,
3. installs/upgrades deps only when something changed,
4. (re)starts the watchers ONLY if the code changed or they aren't running —
so the daily run never interrupts an active session for nothing,
5. ensures the daily+logon Scheduled Task exists (idempotent).
First time on a new PC (or just paste the install one-liner):
git clone https://git.armoredarmadillo.com/brockdarnold/eve-watcher.git
cd eve-watcher
python update.py
python eve_orehold_watcher.py --snip # one-time GUI step for OCR hold alerts
"""
import configparser
import os
import shutil
import subprocess
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
WIN = os.name == "nt"
TASK = "EveWatcher"
WATCHER_SCRIPTS = ("eve_combat_watcher.py", "eve_chat_watcher.py", "eve_orehold_watcher.py")
def run(cmd, **kw):
print(f" $ {' '.join(cmd)}")
return subprocess.run(cmd, cwd=HERE, **kw)
def ps(script):
"""Run a PowerShell snippet, return (rc, stdout)."""
r = subprocess.run(["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass",
"-Command", script], cwd=HERE, capture_output=True, text=True)
return r.returncode, (r.stdout or "").strip()
def ensure_config():
cfg = os.path.join(HERE, "config.ini")
first = not os.path.exists(cfg)
if first:
shutil.copyfile(os.path.join(HERE, "config.ini.example"), cfg)
print("• created config.ini from example (webhook prefilled).")
else:
print("• config.ini exists — your settings + OCR snip left untouched.")
return cfg, first
def git_pull():
"""Return True if new commits were pulled."""
if not os.path.isdir(os.path.join(HERE, ".git")):
print("• not a git clone — can't auto-update. Reinstall via:\n"
" git clone https://git.armoredarmadillo.com/brockdarnold/eve-watcher.git")
return False
r = subprocess.run(["git", "pull", "--ff-only"], cwd=HERE,
capture_output=True, text=True)
out = (r.stdout + r.stderr).strip()
print(f" git: {out.splitlines()[-1] if out else '(no output)'}")
if r.returncode != 0:
print("! git pull failed (local edits to tracked files?). config.ini is safe.")
return False
return "Already up to date" not in out and "Already up-to-date" not in out
def deps():
req = os.path.join(HERE, "requirements.txt")
if os.path.exists(req):
run([sys.executable, "-m", "pip", "install", "--quiet", "-r", req])
def watchers_running():
if not WIN:
return 0
_, out = ps("(Get-CimInstance Win32_Process -Filter \"Name='pythonw.exe'\" "
"| Where-Object { $_.CommandLine -like '*watcher*' }).Count")
try:
return int(out or "0")
except ValueError:
return 0
def stop_watchers():
ps("Get-CimInstance Win32_Process -Filter \"Name='pythonw.exe'\" | "
"Where-Object { $_.CommandLine -like '*watcher*' } | "
"ForEach-Object { Stop-Process -Id $_.ProcessId -Force }")
def start_watchers(cfg_path):
if not WIN:
print("• non-Windows — start watchers manually (this is a Windows tool).")
return
stop_watchers()
run(["powershell", "-ExecutionPolicy", "Bypass", "-File",
os.path.join(HERE, "start-all.ps1")])
cp = configparser.ConfigParser()
cp.read(cfg_path)
if cp.get("watcher", "mode", fallback="ocr") == "ocr" and \
not cp.get("ocr", "region", fallback="").strip():
print("\n >> ONE-TIME (per PC) for hold/compress/stall alerts, run once:\n"
" python eve_orehold_watcher.py --snip\n"
" (drag a box around the Ore Hold fill bar; saved forever after.)")
def ensure_task():
"""Register a daily + at-logon Scheduled Task so this runs itself forever.
Idempotent: only creates it if missing."""
if not WIN:
return
rc, _ = ps(f"Get-ScheduledTask -TaskName '{TASK}' -ErrorAction SilentlyContinue")
if rc == 0:
print(f"• Scheduled Task '{TASK}' already set (auto-update at logon + daily).")
return
pyw = os.path.join(os.path.dirname(sys.executable), "pythonw.exe")
if not os.path.exists(pyw):
pyw = "pythonw.exe"
here = HERE.replace("'", "''")
pyw_e = pyw.replace("'", "''")
snippet = (
f"$a = New-ScheduledTaskAction -Execute '{pyw_e}' "
f"-Argument '\"{here}\\update.py\"' -WorkingDirectory '{here}';"
"$t1 = New-ScheduledTaskTrigger -AtLogOn;"
"$t2 = New-ScheduledTaskTrigger -Daily -At 5am;"
"$s = New-ScheduledTaskSettingsSet -StartWhenAvailable "
"-AllowStartIfOnBatteries -DontStopIfGoingOnBatteries "
"-ExecutionTimeLimit (New-TimeSpan -Hours 1);"
"$p = New-ScheduledTaskPrincipal -UserId $env:USERNAME "
"-LogonType Interactive -RunLevel Limited;"
f"Register-ScheduledTask -TaskName '{TASK}' -Action $a -Trigger $t1,$t2 "
"-Settings $s -Principal $p -Force | Out-Null")
rc, out = ps(snippet)
if rc == 0:
print(f"• Scheduled Task '{TASK}' created — auto-updates at logon + daily 5am. "
"You never have to run this again.")
else:
print(f"! couldn't register Scheduled Task: {out}")
def main():
print("=== Eve watcher setup / auto-update ===")
cfg, first = ensure_config()
changed = git_pull()
if changed or first:
deps()
if changed or first or watchers_running() == 0:
start_watchers(cfg)
else:
print("• up to date and watchers already running — nothing to restart.")
ensure_task()
print("\nDone. Hands-off from here: it updates + keeps itself running automatically.")
if __name__ == "__main__":
main()