From e1368f2a46cbe123b337726c27b4fc85abb7ea84 Mon Sep 17 00:00:00 2001 From: dev Date: Sun, 25 Oct 2026 19:11:52 +0300 Subject: [PATCH] =?UTF-8?q?omni:=20=D0=B2=D0=B5=D0=B1-=D0=BF=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=20(serve=20+=20/api),=20=D0=B4=D0=B0=D0=B9?= =?UTF-8?q?=D0=B4=D0=B6=D0=B5=D1=81=D1=82,=20arxiv=20API-fallback,=20HF=20?= =?UTF-8?q?trending=20unwrap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- omni/cli.py | 30 ++++++ omni/feeds.py | 35 ++++++- omni/serve.py | 111 +++++++++++++++++++++ omni/sources.py | 3 +- omni/store.py | 19 ++++ web/index.html | 250 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 446 insertions(+), 2 deletions(-) create mode 100644 omni/serve.py create mode 100644 web/index.html diff --git a/omni/cli.py b/omni/cli.py index f82be0b..6d83720 100644 --- a/omni/cli.py +++ b/omni/cli.py @@ -1,5 +1,6 @@ """CLI: providers / search / fanout / cards / top / find / stats / export.""" import argparse +import pathlib import concurrent.futures import json import sys @@ -175,6 +176,26 @@ def cmd_feed(args): return 0 +def cmd_digest(args): + """Сводка по базе: топ записей по категориям за прогон, в markdown.""" + rows = store.top(limit=args.limit * 8) + by_cat = {} + for prov, kind, src, title, sc, price, cur, url in rows: + by_cat.setdefault(src or prov, []).append((sc, title, url)) + out = [f"# дайджест omni · {__import__('datetime').date.today()}", ""] + for cat, entries in sorted(by_cat.items(), key=lambda x: -len(x[1])): + out.append(f"## {cat} · {len(entries)}") + for sc, title, url in entries[:args.limit]: + out.append(f"- [{title[:110]}]({url}) · {sc:.2f}") + out.append("") + text = chr(10).join(out) + path = args.out + pathlib.Path(path).parent.mkdir(parents=True, exist_ok=True) + pathlib.Path(path).write_text(text, encoding="utf-8") + print(f"дайджест: {len(by_cat)} категорий, {len(rows)} записей -> {path}") + return 0 + + def cmd_export(args): n, path = store.export(args.out, args.fmt, args.limit) print(f"выгружено {n} записей -> {path}") @@ -246,6 +267,15 @@ def main(argv=None): sp.add_argument("--verbose", action="store_true") sp.set_defaults(fn=cmd_feed) + sp = sub.add_parser("serve", help="веб-панель + JSON API") + sp.add_argument("--port", type=int, default=8077) + sp.set_defaults(fn=lambda a: __import__("omni.serve", fromlist=["serve"]).serve(a.port)) + + sp = sub.add_parser("digest", help="markdown-дайджест из базы") + sp.add_argument("--limit", type=int, default=8) + sp.add_argument("--out", default="data/digest.md") + sp.set_defaults(fn=cmd_digest) + sp = sub.add_parser("export", help="выгрузка jsonl/csv") sp.add_argument("--out", default="data/top.jsonl") sp.add_argument("--fmt", default="jsonl", choices=["jsonl", "csv"]) diff --git a/omni/feeds.py b/omni/feeds.py index a4e5b18..48b8535 100644 --- a/omni/feeds.py +++ b/omni/feeds.py @@ -33,6 +33,33 @@ def _item(src, kind, url, title, text="", author="", published="", extra=None): # ---------------- rss ---------------- +def _arxiv_api(src, limit): + """arxiv RSS часто пустой -> официальный API (Atom), тот же формат.""" + m = re.search(r"rss/([A-Za-z\-.]+)$", src.url) + cat = m.group(1) if m else "cs.AI" + url = ("https://export.arxiv.org/api/query?search_query=cat:" + cat + + "&sortBy=submittedDate&sortOrder=descending&max_results=" + str(max(limit, 5))) + r = net.get(url, headers=UA) + if r.status != 200 or " + + + + +omni · радар источников + + + +
+ + +
+
+
+

что прилетело без запроса

+
ленты, API и форумы в одной базе
+
+
+ + + +
+
+ +
+ +
+ — + все категории + api /api/items +
+ +
+ +
+
+
+ + + + \ No newline at end of file