Add LLM key-hunter toolkit, vault, and skill
- tools/scripts/llm-key-hunter: GitHub leak hunting pipeline (hunt_*, pivot miner, two-layer verify/content caches, per-provider verification) - usable_keys: verified key vault across 12 providers (deepseek, minimax, volcanoark, longcat, codingplan, zhipu free-tier, mimo, siliconflow, etc.) - .grok/skills/llm-key-hunter: operator skill for the hunt/verify/vault flow - NewAPI channel import scripts and CDP capture helpers - Result verdict buckets (excluding multi-GB blob caches and dedup dumps)
This commit is contained in:
1 parent
a3f698806b
commit
5d215e1649
684 files changed
+133838
No files matched your search
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Persistent verification cache for key hunters.
|
||||
|
||||
Avoids re-querying keys whose verdict is already known. TTL by verdict:
|
||||
DEAD -> never re-check (401 does not come back)
|
||||
NO_BALANCE / NO_ACCESS -> 6h (account may be recharged / plan opened)
|
||||
USABLE -> 30m (balance drains; refresh periodically)
|
||||
UNKNOWN/network error -> never cached (retry next run)
|
||||
|
||||
Cache is one JSON file per provider under results/_cache/<provider>.json,
|
||||
shape: { key: {"v": verdict, "d": detail, "t": epoch_seconds} }
|
||||
|
||||
Usage in a hunter:
|
||||
|
||||
from verify_cache import CachedVerifier
|
||||
ver = CachedVerifier("deepseek", verify_key) # verify_key(key)->(verdict,detail)
|
||||
verdict, detail = ver(key) # cached or live
|
||||
ver.save()
|
||||
|
||||
Or wrap the whole batch loop:
|
||||
with CachedVerifier("deepseek", verify_key) as ver:
|
||||
results = list(pool.map(ver, keys))
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).parent
|
||||
CACHE_DIR = HERE / "results" / "_cache"
|
||||
|
||||
# TTL in seconds per verdict. None = never expire.
|
||||
TTL = {
|
||||
"DEAD": None,
|
||||
"NO_BALANCE": 6 * 3600,
|
||||
"NO_ACCESS": 6 * 3600,
|
||||
"USABLE": 30 * 60,
|
||||
}
|
||||
# verdicts that are never written to cache (re-run every time)
|
||||
NEVER_CACHE = {"UNKNOWN"}
|
||||
|
||||
|
||||
class CachedVerifier:
|
||||
def __init__(self, provider, verify_fn, cache_dir=None, force=False, ttl=None):
|
||||
self.provider = provider
|
||||
self.verify_fn = verify_fn
|
||||
self.force = force or os.environ.get("NO_CACHE") == "1"
|
||||
self.ttl = {**TTL, **(ttl or {})}
|
||||
self.path = Path(cache_dir) if cache_dir else (CACHE_DIR / f"{provider}.json")
|
||||
self.path.parent.mkdir(parents=True, exist_ok=True)
|
||||
self._lock = threading.Lock()
|
||||
self._cache = {}
|
||||
self.hits = 0
|
||||
self.live = 0
|
||||
self._load()
|
||||
|
||||
def _load(self):
|
||||
if self.path.exists():
|
||||
try:
|
||||
self._cache = json.loads(self.path.read_text())
|
||||
except Exception:
|
||||
self._cache = {}
|
||||
|
||||
def save(self):
|
||||
tmp = self.path.with_suffix(".tmp")
|
||||
with self._lock:
|
||||
tmp.write_text(json.dumps(self._cache, ensure_ascii=False))
|
||||
os.replace(tmp, self.path)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
self.save()
|
||||
|
||||
def _fresh(self, entry):
|
||||
ttl = self.ttl.get(entry.get("v"))
|
||||
if ttl is None:
|
||||
return True # never expires (DEAD)
|
||||
return (time.time() - entry.get("t", 0)) < ttl
|
||||
|
||||
def __call__(self, key):
|
||||
if not self.force:
|
||||
with self._lock:
|
||||
ent = self._cache.get(key)
|
||||
if ent and self._fresh(ent):
|
||||
self.hits += 1
|
||||
return ent["v"], ent["d"]
|
||||
verdict, detail = self.verify_fn(key)
|
||||
self.live += 1
|
||||
if verdict not in NEVER_CACHE:
|
||||
with self._lock:
|
||||
self._cache[key] = {"v": verdict, "d": detail, "t": time.time()}
|
||||
return verdict, detail
|
||||
|
||||
def stats(self):
|
||||
return {"hits": self.hits, "live": self.live, "cached": len(self._cache)}
|
||||
|
||||
|
||||
def prune_dead(provider=None):
|
||||
"""Utility: print cache size / drop nothing."""
|
||||
for p in sorted(CACHE_DIR.glob("*.json")):
|
||||
if provider and p.stem != provider:
|
||||
continue
|
||||
try:
|
||||
d = json.loads(p.read_text())
|
||||
except Exception:
|
||||
continue
|
||||
counts = {}
|
||||
for v in d.values():
|
||||
counts[v.get("v", "?")] = counts.get(v.get("v", "?"), 0) + 1
|
||||
print(f"{p.stem:16s} {len(d):6d} entries {counts}")
|
||||
Reference in new issue
Block a user