feat(tg): реальные абузы из Telegram без сессии — адаптер tg_web переписан (служебное/пустыши отсекаются, заголовок из тела, порог 12/8 для leak-каналов), 19 живых каналов из 87 проверенных, сигнал абуза в score (+до 1.8), tools/tg_discover.py, tools/fixtg.py, tools/purge_tg_junk.py, tools/rescore.py; реестр 869, база 7295 (ab_ 3135, tg_ 374); тесты 19
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

This commit is contained in:
Vlad Gothbreach
2026-10-26 21:06:50 +03:00
parent f9719b9149
commit eaedd37bcd
11 changed files with 414 additions and 22 deletions

78
tools/tg_discover.py Normal file
View File

@@ -0,0 +1,78 @@
"""Разведка публичных Telegram-каналов под абузы: t.me/s без сессии и ключей.
python tools/tg_discover.py name1,name2,... # проверить и показать
python tools/tg_discover.py --file data/tg_cand.txt # список из файла
python tools/tg_discover.py ... --min 3 # печатать только годные
Для каждого канала: HTTP-статус, сколько постов даёт парсер (после отсева
служебного и пустышей) и сколько из них новые для базы.
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from omni import feeds, sources, store # noqa: E402
def candidates(argv):
"""Позиционные аргументы = имена/списки через запятую; --file/--min пропускаем."""
names = []
skip_next = False
for a in argv:
if skip_next:
skip_next = False
continue
if a in ("--file", "--min"):
skip_next = True
continue
if a.startswith("--"):
continue
names += [x.strip() for x in a.split(",") if x.strip()]
if "--file" in argv:
p = Path(argv[argv.index("--file") + 1])
names += [x.strip() for x in p.read_text(encoding="utf-8").splitlines() if x.strip()]
seen, out = set(), []
for n in names:
k = n.lower().lstrip("@")
if k and k not in seen:
seen.add(k)
out.append(n.lstrip("@"))
return out
def main():
args = sys.argv[1:]
min_items = 1
if "--min" in args:
min_items = int(args[args.index("--min") + 1])
todo = candidates(args)
print(f"кандидатов {len(todo)}")
good = []
for name in todo:
src = sources.Source("probe_" + name.lower(), "abuse", "html",
f"https://t.me/s/{name}", "tg_web", ("abuse",), 25, "", {})
try:
items = feeds.fetch(src)
except Exception as e:
print(f" {name:24} ошибка: {type(e).__name__}")
continue
c = store.conn()
urls = {r[0] for r in c.execute("SELECT url FROM items WHERE source=?", (src.name,))}
c.close()
fresh = sum(1 for it in items if it.url not in urls)
mark = ""
if fresh >= min_items and items:
good.append((name, len(items), fresh))
mark = " <-- ГОДЕН"
print(f" {name:24} постов {len(items):>3} новых {fresh:>3}{mark}")
print(f"\nгодных каналов: {len(good)}")
for name, n, f in sorted(good, key=lambda x: -x[1]):
print(f" {name:24} постов {n:>3} новых {f}")
if good:
Path("data/tg_good.txt").write_text("\n".join(g[0] for g in good), encoding="utf-8")
print("список: data/tg_good.txt")
if __name__ == "__main__":
main()