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

View File

@@ -207,6 +207,58 @@ SOURCES = _dedup(SOURCES_BASE + [Source(**d) for d in EXTRA] + [Source(**d) for
BY_NAME = {s.name: s for s in SOURCES}
CATEGORIES = sorted({s.category for s in SOURCES})
_RADAR = {"at": 0.0, "rows": []}
def radar_sources():
"""Источники из abuse-radar: data/radar_sources.json (см. omni radar --apply).
Домены идут html_listing (root страница с маркерами), каналы — tg_web.
Файл перечитывается при изменении mtime, реестр не разрастается дублями.
"""
import json
from pathlib import Path
p = Path(__file__).resolve().parents[1] / "data" / "radar_sources.json"
try:
mtime = p.stat().st_mtime
except OSError:
return []
if _RADAR["rows"] and _RADAR["at"] == mtime:
return _RADAR["rows"]
rows = []
try:
d = json.loads(p.read_text(encoding="utf-8"))
except Exception:
return []
for item in d.get("domains") or []:
dom, score, title = (list(item) + ["", 0, ""])[:3] if isinstance(item, list) else (item.get("domain"), item.get("score"), item.get("title"))
name = "rad_" + str(dom).replace(".", "_").replace("-", "_")
rows.append(Source(name=name, category="abuse", kind="site", url="https://" + str(dom),
adapter="html_listing", tags=("radar", "abuse"),
limit=12, detail=False, extra={"score": score, "title": (title or "")[:120], "opt_in": True}))
for ch in d.get("channels") or []:
name = "tg_" + str(ch).lstrip("@")
rows.append(Source(name=name, category="abuse", kind="tg", url="https://t.me/" + str(ch).lstrip("@"),
adapter="tg_web", tags=("tg", "abuse", "radar"), limit=25,
detail=False, extra={"opt_in": True}))
_RADAR.update(at=mtime, rows=rows)
return rows
def _cn_sources():
"""Китайские/азиатские источники из сгенерированного omni/sources_cn.py."""
try:
from .sources_cn import CN
except Exception:
return []
out = []
for d in CN:
try:
out.append(Source(**d))
except Exception:
continue
return out
def all_sources():
"""Реестр + пользовательские источники из панели, без дублей по url."""
@@ -219,7 +271,7 @@ def all_sources():
("name", "category", "kind", "url", "adapter", "tags", "limit", "detail", "extra")}
extra.append(Source(**d))
seen, out = set(), []
for s in SOURCES + extra:
for s in SOURCES + _cn_sources() + extra + radar_sources():
key = (s.url or "").rstrip("/").lower()
if key and key in seen:
continue
@@ -227,15 +279,27 @@ def all_sources():
out.append(s)
return out
def enabled_sources():
"""Только включённые: выключенные в панели источники не дёргаем."""
def enabled_sources(include_opt_in=False):
"""Только включённые: выключенные в панели источники не дёргаем.
Источники из abuse-radar помечены extra["opt_in"] — их 2000+, в общий обход
они не лезут; берутся только когда явно просят (--tags radar / --only / category abuse).
"""
from . import source_state
fl = source_state.flags()
return [s for s in all_sources() if fl.get(s.name, {}).get("enabled", True)]
out = []
for s in all_sources():
if not fl.get(s.name, {}).get("enabled", True):
continue
if not include_opt_in and (s.extra or {}).get("opt_in"):
continue
out.append(s)
return out
def pick(category=None, only=None, tags=None, respect_enabled=True):
out = enabled_sources() if respect_enabled else all_sources()
explicit = bool(only or tags or category)
out = enabled_sources(include_opt_in=explicit) if respect_enabled else all_sources()
if category:
cats = {c.strip() for c in category.split(",") if c.strip()}
out = [s for s in out if s.category in cats]