107 lines
3.3 KiB
Python
107 lines
3.3 KiB
Python
"""Re-validate all keys whose status claims WORKING/FREE/LIMITED.
|
|
|
|
Root cause: validator_loop only re-checks NEW keys; stale live statuses rot.
|
|
This script reconciles DB with reality using eternal_v10's m.validate().
|
|
No inner threads — m.validate already has per-request timeouts.
|
|
"""
|
|
import importlib.util
|
|
import sqlite3
|
|
import sys
|
|
import time
|
|
from collections import Counter
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
from pathlib import Path
|
|
import threading
|
|
|
|
PROJ = Path(__file__).resolve().parent
|
|
DB_PATH = PROJ / "data" / "keys.db"
|
|
EV10 = PROJ / "src" / "eternal_v10.py"
|
|
THREADS = 10 # conservative to avoid WAL contention with harvesters
|
|
LIVE = {"WORKING", "LIMITED", "FREE"}
|
|
|
|
_lock = threading.Lock()
|
|
_statuses = Counter()
|
|
_prov_live = Counter()
|
|
_prov_dead = Counter()
|
|
|
|
# --- import eternal_v10 ---
|
|
spec = importlib.util.spec_from_file_location("ev10_reval", str(EV10))
|
|
m = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(m)
|
|
print("[init] eternal_v10 imported", flush=True)
|
|
|
|
# Patch _DB_LOCK to no-op: each _connect() creates own connection,
|
|
# WAL handles concurrent writes. Avoids contention with harvesters.
|
|
class _NoopLock:
|
|
def __enter__(self): return self
|
|
def __exit__(self, *a): pass
|
|
def acquire(self, *a): return True
|
|
def release(self): pass
|
|
m._DB_LOCK = _NoopLock()
|
|
print("[init] _DB_LOCK patched to no-op", flush=True)
|
|
# --- runtime GOOGLE patch ---
|
|
GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/models"
|
|
|
|
def _v_google(_prov, key):
|
|
try:
|
|
r = m.requests.get(GEMINI_URL, params={"key": key}, timeout=15)
|
|
if r.status_code == 200:
|
|
n = len(r.json().get("models", []))
|
|
return "WORKING", "GEMINI API", f"{n} models", str(n)
|
|
if r.status_code == 429:
|
|
return "WORKING", "GEMINI API (rate-limited)", "429", "429"
|
|
return "DEAD", "", "", ""
|
|
except Exception:
|
|
return "ERR", "", "", ""
|
|
|
|
m.VALIDATORS["google"] = _v_google
|
|
print("[init] GOOGLE runtime validator patched", flush=True)
|
|
|
|
db = sqlite3.connect(str(DB_PATH), timeout=120)
|
|
rows = db.execute(
|
|
"SELECT val, prov FROM keys "
|
|
"WHERE status IN ('WORKING','FREE','LIMITED') "
|
|
"ORDER BY id"
|
|
).fetchall()
|
|
db.close()
|
|
total = len(rows)
|
|
print(f"[init] revalidating {total} stale-live keys", flush=True)
|
|
if not rows:
|
|
print("[done] nothing to revalidate", flush=True)
|
|
sys.exit(0)
|
|
|
|
done = 0
|
|
|
|
def work(item):
|
|
global done
|
|
val, prov = item
|
|
try:
|
|
res = m.validate(val, prov)
|
|
st = res[0]
|
|
except Exception as e:
|
|
st = "ERR"
|
|
with _lock:
|
|
_statuses[st] += 1
|
|
if st in LIVE:
|
|
_prov_live[prov] += 1
|
|
else:
|
|
_prov_dead[prov] += 1
|
|
done += 1
|
|
if done % 50 == 0 or done == total:
|
|
print(f"[{done}/{total}] " + " ".join(f"{s}={c}" for s, c in _statuses.most_common()), flush=True)
|
|
return st
|
|
|
|
t0 = time.time()
|
|
with ThreadPoolExecutor(max_workers=THREADS) as ex:
|
|
futures = {ex.submit(work, r): r for r in rows}
|
|
for f in as_completed(futures):
|
|
pass # work() handles counters internally
|
|
|
|
print(f"[done] {total} keys in {(time.time()-t0)/60:.1f} min", flush=True)
|
|
print("=== live after ===", flush=True)
|
|
for p, c in _prov_live.most_common():
|
|
print(f" {p}: {c}", flush=True)
|
|
print("=== dead/err ===", flush=True)
|
|
for p, c in _prov_dead.most_common():
|
|
print(f" {p}: {c}", flush=True)
|