vault ключей + мост abuse-radar + китайские источники: всё в одном реестре
- omni/vault.py: пул ключей парсеров (env-first, data/vault.json, ротация, cooldown 401/402/403=1ч, 429=10м), только маски в выводе - tools/build_vault.py: сборка пула из MASTER_KEYS.csv и локальных дампов (значения не в git) - tools/vault_check.py: живая проверка с маскированием - searchers: firecrawl/tavily/exa/serper/jina/apify берут ключ из vault, если нет env - omni radar: домены+каналы из radar_server.db -> реестр, --import-posts заливает посты каналов - radar-источники помечены opt_in: общий обход не тормозят (869 против 3337 всего) - omni/sources_cn.py + tools/wave_cn.py: 9 живых китайских источников (52pojie, nodeseek, kanxue, anquanke, 4hou, xz.aliyun, ithome, cnbeta, v2ex) - net.get(proxy=...): принудительный прокси для CF-площадок - /api/keys + строка ключей в панели - tests/test_vault_radar.py: 7 тестов, всего 29 passed
This commit is contained in:
112
tools/vault_check.py
Normal file
112
tools/vault_check.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""Проверка ключей vault живыми запросами. Значения не печатаются — только маски.
|
||||
|
||||
python tools/vault_check.py firecrawl --limit 20
|
||||
python tools/vault_check.py 2captcha
|
||||
python tools/vault_check.py all
|
||||
|
||||
Статусы в data/vault.json: live / dead / rate / nofund.
|
||||
firecrawl: GET /v2/team/credit-usage (200 = живой, 402 = без кредитов, 401 = мёртвый).
|
||||
2captcha: GET res.php?action=getbalance (status:1 = живой, баланс в request).
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
VAULT = ROOT / "data" / "vault.json"
|
||||
|
||||
|
||||
def load():
|
||||
return json.loads(VAULT.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def save(d):
|
||||
VAULT.write_text(json.dumps(d, ensure_ascii=False, indent=0), encoding="utf-8")
|
||||
|
||||
|
||||
def mask(k):
|
||||
return k[:4] + "…" + k[-2:] if len(k) > 8 else "…"
|
||||
|
||||
|
||||
def check_firecrawl(key):
|
||||
for url in ("https://api.firecrawl.dev/v2/team/credit-usage",
|
||||
"https://api.firecrawl.dev/v1/credits"):
|
||||
try:
|
||||
r = requests.get(url, headers={"Authorization": "Bearer " + key}, timeout=20)
|
||||
except Exception as e:
|
||||
return "err", type(e).__name__
|
||||
if r.status_code == 200:
|
||||
try:
|
||||
j = r.json()
|
||||
except Exception:
|
||||
j = {}
|
||||
rem = j.get("remainingCredits") or j.get("remaining_credits") or \
|
||||
(j.get("data") or {}).get("remainingCredits")
|
||||
return "live", ("credits %s" % rem) if rem is not None else "ok"
|
||||
if r.status_code == 401:
|
||||
return "dead", "401"
|
||||
if r.status_code == 402:
|
||||
return "nofund", "402"
|
||||
if r.status_code == 429:
|
||||
return "rate", "429"
|
||||
if r.status_code == 403:
|
||||
return "dead", "403"
|
||||
return "dead", "no-endpoint"
|
||||
|
||||
|
||||
def check_2captcha(key):
|
||||
try:
|
||||
r = requests.get("https://2captcha.com/res.php",
|
||||
params={"key": key, "action": "getbalance", "json": 1}, timeout=20)
|
||||
j = r.json()
|
||||
except Exception as e:
|
||||
return "err", type(e).__name__
|
||||
if j.get("status") == 1:
|
||||
return "live", "баланс %s" % j.get("request")
|
||||
err = str(j.get("request") or "")
|
||||
if "ERROR_WRONG_USER_KEY" in err or "ERROR_KEY_DOES_NOT_EXIST" in err:
|
||||
return "dead", err[:30]
|
||||
return "rate", err[:30]
|
||||
|
||||
|
||||
CHECKS = {"firecrawl": check_firecrawl, "2captcha": check_2captcha}
|
||||
|
||||
|
||||
def run(service, limit):
|
||||
d = load()
|
||||
rows = d.get(service) or []
|
||||
if not rows:
|
||||
print("в vault нет сервиса", service)
|
||||
return
|
||||
fn = CHECKS[service]
|
||||
rows = rows[:limit] if limit else rows
|
||||
print("%s: проверяю %d ключей" % (service, len(rows)))
|
||||
stat = {}
|
||||
for r in rows:
|
||||
st, note = fn(r["key"])
|
||||
r["status"] = st
|
||||
r["note"] = note
|
||||
stat[st] = stat.get(st, 0) + 1
|
||||
mark = {"live": "+", "dead": "-", "nofund": "$", "rate": "~", "err": "!"}.get(st, "?")
|
||||
print(" %s %s %-7s %s" % (mark, mask(r["key"]), st, note))
|
||||
time.sleep(0.35)
|
||||
save(d)
|
||||
print("итог %s: %s" % (service, stat))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("service")
|
||||
ap.add_argument("--limit", type=int, default=0)
|
||||
a = ap.parse_args()
|
||||
if a.service == "all":
|
||||
for s in CHECKS:
|
||||
run(s, a.limit)
|
||||
elif a.service in CHECKS:
|
||||
run(a.service, a.limit)
|
||||
else:
|
||||
print("поддерживается:", ", ".join(CHECKS))
|
||||
Reference in New Issue
Block a user