Files
hack/tools/scripts/llm-key-hunter/vault_cn_batch2.py
T

216 lines
7.0 KiB
Python

#!/usr/bin/env python3
"""Vault the 2026-08-03 CN coding-plan batch winners.
Reads results/cn_batch2_full/usable.txt + no_balance.txt and writes per-provider
vault directories under usable_keys/<provider>/ with keys.json, keys.env, and
per-key .txt files. Then rebuilds the root all_keys.json + README.md.
"""
import json, re, time
from pathlib import Path
HERE = Path(__file__).resolve().parent
VAULT = HERE / "usable_keys"
SRC = HERE / "results/cn_batch2_full"
# Provider metadata: base_url, default model, auth, official site
META = {
"iflytek_maas": {
"dir": "iflytek_maas",
"base_url": "https://maas-api.cn-huabei-1.xf-yun.com/v1",
"auth": "Bearer",
"site": "https://www.xfyun.cn/",
"plan": "iFlytek Spark MaaS (讯飞星火 MaaS)",
},
"dmxapi": {
"dir": "dmxapi",
"base_url": "https://www.dmxapi.cn/v1",
"auth": "Bearer",
"site": "https://www.dmxapi.cn/",
"plan": "DMXAPI — one_api relay aggregator",
},
"aihubmix": {
"dir": "aihubmix",
"base_url": "https://aihubmix.com/v1",
"auth": "Bearer",
"site": "https://aihubmix.com/",
"plan": "AIHubMix — international+CN model aggregator",
},
"closeai": {
"dir": "closeai",
"base_url": "https://api.closeai-asia.com/v1",
"auth": "Bearer",
"site": "https://www.closeai-asia.com/",
"plan": "CloseAI-Asia relay",
},
"api2d": {
"dir": "api2d",
"base_url": "https://oa.api2d.net/v1",
"auth": "Bearer",
"site": "https://api2d.com/",
"plan": "API2D — Chinese OpenAI/Anthropic relay",
},
}
def parse_bucket(path):
"""Parse prov<TAB>key<TAB>url<TAB>detail, handling the prov+key concat bug."""
rows = []
if not path.exists():
return rows
for ln in path.read_text(errors="replace").splitlines():
parts = ln.split("\t")
if len(parts) < 3:
continue
prov, key, url = parts[0], parts[1], parts[2]
detail = parts[3] if len(parts) > 3 else ""
if not key.startswith("sk-") and prov.startswith(tuple(META.keys())):
# split concatenated "provsk-..."
for p in META:
if prov.startswith(p):
maybe_key = prov[len(p):]
if maybe_key.startswith("sk-"):
prov, key = p, maybe_key
break
if prov in META and key.startswith("sk-"):
rows.append((prov, key, url, detail))
return rows
def slug(s, n=6):
return s[:n]
def write_provider(prov, entries, verdict):
"""entries: list of (key, source, detail). verdict: USABLE|NO_BALANCE."""
meta = META[prov]
d = VAULT / meta["dir"]
d.mkdir(parents=True, exist_ok=True)
# load existing keys.json if present
kf = d / "keys.json"
existing = []
if kf.exists():
try:
data = json.loads(kf.read_text())
existing = data if isinstance(data, list) else [data]
except Exception:
existing = []
existing_keys = {e.get("key") for e in existing}
# extract working model from detail: "model 200 ..." or "model 403 ..."
model_re = re.compile(r"^(\S+)\s+(200|40\d)")
added = 0
for key, source, detail in entries:
if key in existing_keys:
continue
m = model_re.match(detail or "")
model = m.group(1) if m else None
# try to pull content snippet
content = ""
if "200" in (detail or ""):
cm = re.search(r"200\s+(.*)", detail)
if cm:
content = cm.group(1)[:80]
entry = {
"key": key,
"source": source,
"detail": detail[:200],
"base_url": meta["base_url"],
"auth": meta["auth"],
"models": [model] if model else [],
"status": verdict,
"found": int(time.time()),
}
if verdict == "NO_BALANCE":
entry["note"] = "real credential, account balance exhausted"
existing.append(entry)
existing_keys.add(key)
added += 1
kf.write_text(json.dumps(existing, indent=2, ensure_ascii=False))
# rebuild keys.env
env = d / "keys.env"
lines = [f"# {meta['plan']}", f"# {meta['site']}",
f"# base_url={meta['base_url']}", ""]
for e in existing:
lines.append(f"{e['key']}")
env.write_text("\n".join(lines) + "\n")
# per-key .txt
for i, e in enumerate(existing, 1):
slug6 = slug(e["key"].replace("sk-", "").replace("-", ""), 6)
pf = d / f"key_{i:02d}_{slug6}.txt"
pf.write_text(
f"key: {e['key']}\n"
f"base_url: {e['base_url']}\n"
f"auth: {e['auth']}\n"
f"models: {','.join(e.get('models', []))}\n"
f"status: {e['status']}\n"
f"source: {e['source']}\n"
f"detail: {e.get('detail','')}\n"
)
return added, len(existing)
def rebuild_root():
ak = []
counts = {}
for d in sorted(p for p in VAULT.iterdir() if p.is_dir()):
f = d / "keys.json"
if not f.exists():
continue
try:
data = json.loads(f.read_text())
except Exception:
continue
data = data if isinstance(data, list) else [data]
for e in data:
if isinstance(e, dict) and e.get("key"):
ak.append(dict(e, provider=d.name))
counts[d.name] = len(data)
(VAULT / "all_keys.json").write_text(
json.dumps(ak, indent=2, ensure_ascii=False))
lines = ["# Usable LLM Keys Vault", "", "## Counts", ""]
for k in sorted(counts, key=lambda x: -counts[x]):
lines.append(f"- **{k}**: {counts[k]}")
lines.append("")
lines.append(f"**Total: {sum(counts.values())} keys across "
f"{len(counts)} providers**")
lines.append("")
(VAULT / "README.md").write_text("\n".join(lines))
return counts
def main():
usable = parse_bucket(SRC / "usable.txt")
nobal = parse_bucket(SRC / "no_balance.txt")
print(f"parsed {len(usable)} usable + {len(nobal)} no-balance keys")
by_prov_u = {}
by_prov_n = {}
for prov, key, url, detail in usable:
by_prov_u.setdefault(prov, []).append((key, url, detail))
for prov, key, url, detail in nobal:
by_prov_n.setdefault(prov, []).append((key, url, detail))
summary = {}
for prov in META:
u = by_prov_u.get(prov, [])
n = by_prov_n.get(prov, [])
if not u and not n:
continue
a1, t1 = write_provider(prov, u, "USABLE")
a2, t2 = write_provider(prov, n, "NO_BALANCE")
summary[prov] = (a1 + a2, t1)
print(f" {prov:16s}: +{a1+a2:3d} new (total {t1})")
counts = rebuild_root()
print("\nvault rebuild complete. counts:")
for k in sorted(counts, key=lambda x: -counts[x]):
print(f" {k:16s}: {counts[k]}")
print(f"\n TOTAL: {sum(counts.values())} keys across {len(counts)} providers")
if __name__ == "__main__":
main()