vault ключей + мост abuse-radar + китайские источники: всё в одном реестре
Some checks failed
ci / test (3.10) (push) Has been cancelled
ci / test (3.11) (push) Has been cancelled
ci / test (3.12) (push) Has been cancelled

- 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:
ENI
2026-10-26 23:31:37 +03:00
parent aa417d635b
commit 2320acf3c5
12 changed files with 790 additions and 8 deletions

112
tools/vault_check.py Normal file
View 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))