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

86
tools/wave_cn.py Normal file
View File

@@ -0,0 +1,86 @@
"""Волна по китайским/азиатским источникам: форумы, реверс, security-новости.
Проверяет кандидатов живыми запросами (лента / список тем), пишет живые в
omni/sources_cn.py. Запуск: python tools/wave_cn.py [--apply]
"""
import sys
import time
from pathlib import Path
import requests
ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "omni" / "sources_cn.py"
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/131.0 Safari/537.36")
# name, url, adapter, category, tags, limit
CAND = [
("cn_52pojie", "https://www.52pojie.cn/forum.php?mod=guide&view=new", "html_listing", "forum", ("cn", "reverse", "abuse"), 20),
("cn_nodeseek", "https://www.nodeseek.com/", "html_listing", "forum", ("cn", "hosting"), 20),
("cn_freebuf", "https://www.freebuf.com/", "html_listing", "security", ("cn", "security"), 20),
("cn_seebug", "https://paper.seebug.org/", "html_listing", "security", ("cn", "papers", "security"), 15),
("cn_anquanke", "https://www.anquanke.com/", "html_listing", "security", ("cn", "security"), 20),
("cn_4hou", "https://www.4hou.com/", "html_listing", "security", ("cn", "security"), 15),
("cn_kanxue", "https://bbs.kanxue.com/", "html_listing", "forum", ("cn", "reverse"), 20),
("cn_ithome", "https://www.ithome.com/rss/", "rss", "news", ("cn", "tech"), 20),
("cn_cnbeta", "https://www.cnbeta.com.tw/backend.php", "rss", "news", ("cn", "tech"), 20),
("cn_sspai", "https://sspai.com/feed", "rss", "news", ("cn", "tech"), 15),
("cn_chiphell", "https://www.chiphell.com/forum.php?mod=guide&view=new", "html_listing", "forum", ("cn", "hardware"), 15),
("cn_52pojie_rss", "https://www.52pojie.cn/forum.php?mod=rss", "rss", "forum", ("cn", "reverse"), 15),
("cn_v2ex_rss", "https://www.v2ex.com/index.xml", "rss", "forum", ("cn", "community"), 20),
("cn_linuxdo_rss", "https://linux.do/latest.rss", "rss", "forum", ("cn", "community", "ai"), 20),
("cn_hostloc_rss", "https://hostloc.com/forum.php?mod=rss", "rss", "forum", ("cn", "hosting"), 15),
("cn_baidu_sec", "https://xz.aliyun.com/", "html_listing", "security", ("cn", "security"), 15),
("cn_secfree", "https://www.secfree.com/", "html_listing", "security", ("cn", "security"), 10),
("cn_tttang", "https://tttang.com/", "html_listing", "security", ("cn", "security"), 10),
]
def probe(url, adapter="html_listing"):
try:
r = requests.get(url, headers={"User-Agent": UA}, timeout=18, allow_redirects=True)
except Exception as e:
return 0, type(e).__name__, 0
if adapter == "rss":
n_links = r.text.count("<item") + r.text.count("<entry")
else:
n_links = r.text.count("<a ")
return r.status_code, "", n_links
def main():
apply = "--apply" in sys.argv
live, dead = [], []
for name, url, adapter, cat, tags, lim in CAND:
code, err, links = probe(url, adapter)
ok = code == 200 and links >= 30
print("%-18s %-3s links %-5d %s" % (name, code or err, links, "LIVE" if ok else "—"))
if ok:
live.append((name, url, adapter, cat, tags, lim))
else:
dead.append(name)
time.sleep(0.8)
print("\nживых %d, мёртвых %d" % (len(live), len(dead)))
if not apply:
print("dry-run: --apply чтобы записать omni/sources_cn.py")
return 0
body = ['"""Китайские и азиатские источники (проверены живыми запросами tools/wave_cn.py).',
'', 'Форумы реверса/хостинга, security-новости, tech-ленты. Все — публичные списки,',
'без ключей и кук. Подключаются в реестр omni как категории forum/security/news.',
'"""', '', 'CN = [']
for name, url, adapter, cat, tags, lim in live:
t = ", ".join('"%s"' % x for x in tags)
body.append(' {"name": "%s", "category": "%s", "kind": "site", "url": "%s",'
' "adapter": "%s", "tags": (%s), "limit": %d},' % (name, cat, url, adapter, t, lim))
body.append(']')
OUT.write_text("\n".join(body) + "\n", encoding="utf-8", newline="\n")
print("записано", OUT, "| источников", len(live))
return 0
if __name__ == "__main__":
sys.exit(main())