autoreg-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
140
skills/account-validation/scripts/aiand_check_keys_curl.py
Normal file
140
skills/account-validation/scripts/aiand_check_keys_curl.py
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
aiand_check_keys_curl.py
|
||||
Validate aiand/OpenAI-compatible API keys using system curl instead of Python urllib/httpx.
|
||||
|
||||
Why curl: aiand's Cloudflare layer may return Error 1010 (browser signature blocked)
|
||||
for Python HTTP clients even when the key is valid. curl can succeed where urllib fails.
|
||||
|
||||
Input formats supported:
|
||||
email|sk-...|org_id
|
||||
label,email,sk-...,org_id
|
||||
sk-...
|
||||
|
||||
Output: CSV report with status classification; raw keys are never printed.
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
DEFAULT_BASE_URL = "https://api.aiand.com/v1"
|
||||
DEFAULT_MODEL = "qwen/qwen3.6-27b"
|
||||
|
||||
|
||||
def load_rows(path: Path):
|
||||
rows = []
|
||||
for line_no, raw in enumerate(path.read_text(encoding="utf-8", errors="ignore").splitlines(), 1):
|
||||
line = raw.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
sep = "|" if "|" in line else "," if "," in line else None
|
||||
parts = [p.strip() for p in (line.split(sep) if sep else [line])]
|
||||
email = next((p for p in parts if "@" in p), f"line_{line_no}")
|
||||
key = next((p for p in parts if p.startswith("sk-")), "")
|
||||
org_id = next((p for p in parts if "-" in p and len(p) >= 25 and not p.startswith("sk-") and "@" not in p), "")
|
||||
rows.append({"line": line_no, "email": email, "key": key, "org_id": org_id})
|
||||
return rows
|
||||
|
||||
|
||||
def curl_chat(base_url: str, key: str, org_id: str, model: str, timeout: int):
|
||||
payload = json.dumps({
|
||||
"model": model,
|
||||
"messages": [{"role": "user", "content": "ping"}],
|
||||
"max_tokens": 1,
|
||||
"temperature": 0,
|
||||
})
|
||||
cmd = [
|
||||
"curl", "-sS", "--max-time", str(timeout),
|
||||
"-w", "\nHTTP_STATUS:%{http_code}",
|
||||
base_url.rstrip("/") + "/chat/completions",
|
||||
"-H", "Content-Type: application/json",
|
||||
"-H", "Accept: application/json",
|
||||
"-H", f"Authorization: Bearer {key}",
|
||||
]
|
||||
if org_id:
|
||||
cmd += ["-H", f"X-Org-ID: {org_id}"]
|
||||
cmd += ["--data-binary", payload]
|
||||
try:
|
||||
r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout + 10)
|
||||
combined = (r.stdout or "") + (("\nSTDERR:" + r.stderr) if r.stderr else "")
|
||||
except Exception as exc:
|
||||
return 0, repr(exc)
|
||||
marker = "\nHTTP_STATUS:"
|
||||
if marker not in combined:
|
||||
return 0, combined.strip()
|
||||
body, code = combined.rsplit(marker, 1)
|
||||
try:
|
||||
return int(code.strip().splitlines()[0]), body.strip()
|
||||
except Exception:
|
||||
return 0, body.strip()
|
||||
|
||||
|
||||
def classify(status: int, body: str):
|
||||
b = body.lower()
|
||||
if status == 200:
|
||||
return "ok"
|
||||
if "insufficient" in b and "credit" in b:
|
||||
return "valid_no_credits"
|
||||
if "error 1010" in b or ("browser" in b and "signature" in b):
|
||||
return "cloudflare_block"
|
||||
if status in (401, 403) or "no access token" in b or "unauthorized" in b or "invalid token" in b:
|
||||
return "auth_failed"
|
||||
if status == 429:
|
||||
return "rate_limited"
|
||||
if "model" in b and any(x in b for x in ("not found", "invalid", "not exist")):
|
||||
return "valid_model_problem"
|
||||
if status == 0:
|
||||
return "network_error"
|
||||
return "unknown_error"
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--keys", required=True, help="Path to key file")
|
||||
ap.add_argument("--out", default="aiand_key_report.csv", help="CSV report path")
|
||||
ap.add_argument("--base-url", default=DEFAULT_BASE_URL)
|
||||
ap.add_argument("--model", default=DEFAULT_MODEL)
|
||||
ap.add_argument("--timeout", type=int, default=60)
|
||||
args = ap.parse_args()
|
||||
|
||||
key_path = Path(args.keys).expanduser()
|
||||
if not key_path.exists():
|
||||
print(f"ERROR: not found: {key_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
report = []
|
||||
counts = {}
|
||||
rows = load_rows(key_path)
|
||||
for i, row in enumerate(rows, 1):
|
||||
key = row["key"]
|
||||
if not key or "..." in key or len(key) < 50:
|
||||
status, http_status, snippet = "local_truncated_key", "", "key missing/truncated locally; not tested"
|
||||
else:
|
||||
http_status, body = curl_chat(args.base_url, key, row["org_id"], args.model, args.timeout)
|
||||
status = classify(http_status, body)
|
||||
snippet = body.replace("\n", " ")[:300]
|
||||
counts[status] = counts.get(status, 0) + 1
|
||||
report.append({
|
||||
"email": row["email"], "line": row["line"], "key_len": len(key),
|
||||
"has_org_id": bool(row["org_id"]), "status": status,
|
||||
"http_status": http_status, "response_snippet": snippet,
|
||||
})
|
||||
print(f"[{i:03d}/{len(rows):03d}] {row['email']} len={len(key)} org={bool(row['org_id'])} status={status} http={http_status}")
|
||||
|
||||
out = Path(args.out)
|
||||
with out.open("w", encoding="utf-8", newline="") as f:
|
||||
writer = csv.DictWriter(f, fieldnames=["email", "line", "key_len", "has_org_id", "status", "http_status", "response_snippet"])
|
||||
writer.writeheader()
|
||||
writer.writerows(report)
|
||||
|
||||
print("\nSummary:")
|
||||
for key, value in sorted(counts.items()):
|
||||
print(f" {key}: {value}")
|
||||
print(f"\nReport written: {out.resolve()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
110
skills/account-validation/scripts/proxy_checker.py
Normal file
110
skills/account-validation/scripts/proxy_checker.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
Proxy rotation checker for V2EX/Discourse account validation.
|
||||
Usage: python proxy_checker.py <accounts_file>
|
||||
"""
|
||||
import requests
|
||||
import json
|
||||
import itertools
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
# === CONFIG ===
|
||||
PROXY_SOURCES = [
|
||||
'https://proxylist.geonode.com/api/proxy-list?limit=20&protocols=socks5',
|
||||
'https://api.proxyscrape.com/v4/free-proxy-list/get?request=display_proxies&protocol=socks5&timeout=10000&limit=20',
|
||||
'https://openproxy.space/list/socks5',
|
||||
]
|
||||
|
||||
def fetch_proxies():
|
||||
"""Get fresh SOCKS5 proxies from multiple sources."""
|
||||
proxies = []
|
||||
for url in PROXY_SOURCES:
|
||||
try:
|
||||
r = requests.get(url, timeout=10)
|
||||
if 'geonode' in url:
|
||||
data = r.json()
|
||||
for p in data.get('data', []):
|
||||
proxies.append(f"socks5://{p['ip']}:{p['port']}")
|
||||
elif 'openproxy' in url:
|
||||
for line in r.text.strip().split('\n')[:20]:
|
||||
if ':' in line and not line.startswith('#'):
|
||||
proxies.append(f"socks5://{line.strip()}")
|
||||
else:
|
||||
for line in r.text.strip().split('\n'):
|
||||
if line.strip():
|
||||
proxies.append(f"socks5://{line.strip()}")
|
||||
except Exception as e:
|
||||
print(f" [!] Proxy source failed: {url[:50]}... {e}")
|
||||
|
||||
return list(set(proxies))
|
||||
|
||||
def test_proxies(proxies, test_url, max_test=20):
|
||||
"""Filter proxies that can reach the target."""
|
||||
working = []
|
||||
for proxy_url in proxies[:max_test]:
|
||||
try:
|
||||
proxy_dict = {'http': proxy_url, 'https': proxy_url}
|
||||
r = requests.get(test_url, proxies=proxy_dict, timeout=10,
|
||||
headers={'User-Agent': 'Mozilla/5.0'})
|
||||
if r.status_code == 200 and 'Restricted' not in r.text:
|
||||
working.append(proxy_url)
|
||||
print(f" [+] {proxy_url}")
|
||||
except:
|
||||
pass
|
||||
return working
|
||||
|
||||
def parse_accounts(filepath):
|
||||
"""Parse user:pass file. Returns list of (host, username, password)."""
|
||||
entries = []
|
||||
with open(filepath, encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
parts = line.split(':')
|
||||
if len(parts) >= 3:
|
||||
pw = parts[-1]
|
||||
user = parts[-2]
|
||||
host = ':'.join(parts[:-2])
|
||||
entries.append((host, user, pw))
|
||||
|
||||
# Dedup
|
||||
seen = set()
|
||||
unique = []
|
||||
for h, u, p in entries:
|
||||
key = (u.lower().strip(), p)
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
unique.append((h, u, p))
|
||||
|
||||
return unique
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python proxy_checker.py <accounts_file>")
|
||||
sys.exit(1)
|
||||
|
||||
filepath = sys.argv[1]
|
||||
print(f"[*] Parsing {filepath}...")
|
||||
accounts = parse_accounts(filepath)
|
||||
print(f"[*] {len(accounts)} unique accounts")
|
||||
|
||||
print("[*] Fetching proxies...")
|
||||
proxies = fetch_proxies()
|
||||
print(f"[*] {len(proxies)} proxies fetched")
|
||||
|
||||
print("[*] Testing proxies against target...")
|
||||
working = test_proxies(proxies, 'https://www.v2ex.com/signin')
|
||||
print(f"[*] {len(working)} working proxies")
|
||||
|
||||
# Save
|
||||
out = os.path.splitext(filepath)[0] + '_parsed.json'
|
||||
with open(out, 'w') as f:
|
||||
json.dump([{"host": h, "user": u, "pass": p} for h, u, p in accounts], f, indent=2, ensure_ascii=False)
|
||||
print(f"[*] Saved parsed accounts to {out}")
|
||||
|
||||
proxy_out = os.path.join(os.path.dirname(filepath), 'working_socks5.txt')
|
||||
with open(proxy_out, 'w') as f:
|
||||
f.write('\n'.join(working))
|
||||
print(f"[*] Saved working proxies to {proxy_out}")
|
||||
Reference in New Issue
Block a user