chore(auto): 2026-10-26T16:37:00Z — items=5594 typed=5594 sources=665 priced=224 runs=120 problems=OK
This commit is contained in:
46
README.md
46
README.md
@@ -24,21 +24,24 @@ python -m omni.bot
|
|||||||
|
|
||||||
## Состояние на сейчас
|
## Состояние на сейчас
|
||||||
|
|
||||||
- **источников: 589** в 11 категориях, **записей в базе: 3411**
|
- **источников: 665** в 11 категориях, **записей в базе: 5594** (224 с ценой, 120 прогонов)
|
||||||
- вкладки панели: `Лента` · `Источники` · `Прогоны` · `Абузы`
|
- **типы записей: 14** — каждая запись размечена по смыслу: новость 3693 · софт 516 · уязвимость 402 · релиз 361 · модель 331 · вопрос 265 · цена 205 · гайд 194 · датасет 126 · слив 90 · триал 41 · промо 34 · вакансия 21 · перепродажа 10
|
||||||
|
- **X/Twitter: 30 аккаунтов** читаются без API-ключей через `api.fxtwitter.com` (313 твитов в базе)
|
||||||
|
- вкладки панели: `Лента` · `Источники` · `Прогоны` · `Абузы`; полоса чипов «тип», фильтр «только с ценой», сортировка `score/fresh/price`, экспорт CSV
|
||||||
- health-аудит: живой/мёртвый/непроверенный по каждому источнику, причина в примечании
|
- health-аудит: живой/мёртвый/непроверенный по каждому источнику, причина в примечании
|
||||||
- Telegram-бот на той же базе: `/new`, `/abuse`, `/find`, `/src`, `/run`, `/add`, `/auto`, `/stats`
|
- Telegram-бот на той же базе: `/new`, `/abuse`, `/find`, `/src`, `/run`, `/add`, `/auto`, `/sum`, `/tr`, `/stats`
|
||||||
|
- автоаудит: `tools/selfcheck.py` обновляет `REPORT.md`, `tools/autocommit.sh` коммитит и пушит в Gitea + GitHub
|
||||||
|
|
||||||
| категория | источников |
|
| категория | источников |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `community` | 123 |
|
| `company` | 135 |
|
||||||
| `company` | 119 |
|
| `community` | 131 |
|
||||||
| `security` | 72 |
|
| `security` | 99 |
|
||||||
| `papers` | 68 |
|
| `papers` | 72 |
|
||||||
| `forum` | 50 |
|
| `forum` | 57 |
|
||||||
| `models` | 35 |
|
| `models` | 40 |
|
||||||
|
| `abuse` | 40 |
|
||||||
| `company_cn` | 34 |
|
| `company_cn` | 34 |
|
||||||
| `abuse` | 31 |
|
|
||||||
| `release` | 30 |
|
| `release` | 30 |
|
||||||
| `benchmark` | 16 |
|
| `benchmark` | 16 |
|
||||||
| `company_kr` | 11 |
|
| `company_kr` | 11 |
|
||||||
@@ -157,6 +160,29 @@ API: `GET /api/stats`, `GET /api/items?category=&q=&limit=&offset=`, `GET /api/s
|
|||||||
|
|
||||||
Состояние источников — таблицы `source_flags` и `source_custom` в той же SQLite (`data/omni.sqlite3`).
|
Состояние источников — таблицы `source_flags` и `source_custom` в той же SQLite (`data/omni.sqlite3`).
|
||||||
|
|
||||||
|
|
||||||
|
## Типы записей (не путать с категорией источника)
|
||||||
|
|
||||||
|
Категория отвечает «откуда», тип — «что именно». Одна запись получает до 3 типов, они лежат в `items.types` и отдаются в API (`/api/types`, `?type=leak`), видны бейджами в панели, попадают в CSV.
|
||||||
|
|
||||||
|
`слив` · `триал` · `софт` · `промо` · `перепродажа` · `уязвимость` · `релиз` · `гайд` · `датасет` · `модель` · `вакансия` · `цена` · `вопрос` · `новость`
|
||||||
|
|
||||||
|
Правила лежат в `omni/taxonomy.py`: взвешенные RU/EN-паттерны, порог по весу, чтобы «free» в рекламе не превращалось в «триал».
|
||||||
|
|
||||||
|
## X / Twitter без ключей
|
||||||
|
|
||||||
|
- `omni/providers/x.py` → `profile(handle)` тянет ленту через `api.fxtwitter.com/2/profile/<handle>/statuses`: полный текст, метрики (лайки, репосты, просмотры), дата, язык.
|
||||||
|
- адаптер `x_user` в `omni/feeds.py`, источники — `omni/sources_x.py` (30 живых хендлов: abuse/security/models/community).
|
||||||
|
- путь `syndication.twitter.com` (429 для всех IP, включая VPS и прокси) и nitter-зеркала (410/403/0) выпилены — не тратить время.
|
||||||
|
- свой хендл добавляется как источник: `{"name": "x_ник", "category": "abuse", "kind": "tweet", "url": "https://x.com/ник", "adapter": "x_user", "extra": {"handle": "ник"}}`.
|
||||||
|
|
||||||
|
## Автоаудит и непрерывные коммиты
|
||||||
|
|
||||||
|
- `python tools/selfcheck.py` — компиляция пакета, счётчики базы, распределение типов, топ источников, поиск дублей и пустых источников; пишет блок в `REPORT.md` и историю в `data/selfcheck.jsonl`.
|
||||||
|
- `bash tools/autocommit.sh [--feed]` — самоаудит (+ подкачка, если `--feed`), коммит только при реальных изменениях, пуш в оба ремоута.
|
||||||
|
- cron: каждые 6 часов аудит-коммит, каждые 12 часов аудит + подкачка источников.
|
||||||
|
- `omni dedupe [--apply]` — схлопывает дубли по url (или source+title при пустом url), вставка тоже дедуплицирует по url.
|
||||||
|
|
||||||
## Как устроено
|
## Как устроено
|
||||||
|
|
||||||
- `omni/net.py` — лестница транспорта: httpx → curl_cffi (подмена TLS-фингерпринта) → прокси из env. Троттлинг по хосту, лимит байт, пометка блокировок.
|
- `omni/net.py` — лестница транспорта: httpx → curl_cffi (подмена TLS-фингерпринта) → прокси из env. Троттлинг по хосту, лимит байт, пометка блокировок.
|
||||||
|
|||||||
14
REPORT.md
14
REPORT.md
@@ -1,19 +1,19 @@
|
|||||||
# omni-parse · автоотчёт
|
# omni-parse · автоотчёт
|
||||||
|
|
||||||
<!-- STATS:START -->
|
<!-- STATS:START -->
|
||||||
_обновлено 2026-10-26T16:21:06+00:00_
|
_обновлено 2026-10-26T16:37:00+00:00_
|
||||||
|
|
||||||
| метрика | значение |
|
| метрика | значение |
|
||||||
|---|---|
|
|---|---|
|
||||||
| источников | 635 |
|
| источников | 665 |
|
||||||
| категорий | 11 |
|
| категорий | 11 |
|
||||||
| записей | 5281 |
|
| записей | 5594 |
|
||||||
| с ценой | 209 |
|
| с ценой | 224 |
|
||||||
| типизировано | 5281 |
|
| типизировано | 5594 |
|
||||||
| прогонов | 120 |
|
| прогонов | 120 |
|
||||||
| пустых источников | 200 |
|
| пустых источников | 208 |
|
||||||
|
|
||||||
типы: новость 3516 · софт 495 · релиз 347 · уязвимость 342 · модель 322 · вопрос 245 · цена 192 · гайд 190
|
типы: новость 3693 · софт 516 · уязвимость 402 · релиз 361 · модель 331 · вопрос 265 · цена 205 · гайд 194
|
||||||
|
|
||||||
| источник | записей |
|
| источник | записей |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|||||||
54
tools/_readme_patch.py
Normal file
54
tools/_readme_patch.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
cats = open("data/_cats.md", encoding="utf-8").read().strip()
|
||||||
|
types = open("data/_types.md", encoding="utf-8").read().strip()
|
||||||
|
p = "README.md"
|
||||||
|
s = open(p, encoding="utf-8").read()
|
||||||
|
|
||||||
|
state_new = f"""## Состояние на сейчас
|
||||||
|
|
||||||
|
- **источников: 665** в 11 категориях, **записей в базе: 5594** (224 с ценой, 120 прогонов)
|
||||||
|
- **типы записей: 14** — каждая запись размечена по смыслу: {types}
|
||||||
|
- **X/Twitter: 30 аккаунтов** читаются без API-ключей через `api.fxtwitter.com` (313 твитов в базе)
|
||||||
|
- вкладки панели: `Лента` · `Источники` · `Прогоны` · `Абузы`; полоса чипов «тип», фильтр «только с ценой», сортировка `score/fresh/price`, экспорт CSV
|
||||||
|
- health-аудит: живой/мёртвый/непроверенный по каждому источнику, причина в примечании
|
||||||
|
- Telegram-бот на той же базе: `/new`, `/abuse`, `/find`, `/src`, `/run`, `/add`, `/auto`, `/sum`, `/tr`, `/stats`
|
||||||
|
- автоаудит: `tools/selfcheck.py` обновляет `REPORT.md`, `tools/autocommit.sh` коммитит и пушит в Gitea + GitHub
|
||||||
|
|
||||||
|
| категория | источников |
|
||||||
|
|---|---|
|
||||||
|
{cats}"""
|
||||||
|
|
||||||
|
start = s.index("## Состояние на сейчас")
|
||||||
|
end = s.index("## Панель")
|
||||||
|
s = s[:start] + state_new + "\n\n" + s[end:]
|
||||||
|
|
||||||
|
extra = """
|
||||||
|
## Типы записей (не путать с категорией источника)
|
||||||
|
|
||||||
|
Категория отвечает «откуда», тип — «что именно». Одна запись получает до 3 типов, они лежат в `items.types` и отдаются в API (`/api/types`, `?type=leak`), видны бейджами в панели, попадают в CSV.
|
||||||
|
|
||||||
|
`слив` · `триал` · `софт` · `промо` · `перепродажа` · `уязвимость` · `релиз` · `гайд` · `датасет` · `модель` · `вакансия` · `цена` · `вопрос` · `новость`
|
||||||
|
|
||||||
|
Правила лежат в `omni/taxonomy.py`: взвешенные RU/EN-паттерны, порог по весу, чтобы «free» в рекламе не превращалось в «триал».
|
||||||
|
|
||||||
|
## X / Twitter без ключей
|
||||||
|
|
||||||
|
- `omni/providers/x.py` → `profile(handle)` тянет ленту через `api.fxtwitter.com/2/profile/<handle>/statuses`: полный текст, метрики (лайки, репосты, просмотры), дата, язык.
|
||||||
|
- адаптер `x_user` в `omni/feeds.py`, источники — `omni/sources_x.py` (30 живых хендлов: abuse/security/models/community).
|
||||||
|
- путь `syndication.twitter.com` (429 для всех IP, включая VPS и прокси) и nitter-зеркала (410/403/0) выпилены — не тратить время.
|
||||||
|
- свой хендл добавляется как источник: `{"name": "x_ник", "category": "abuse", "kind": "tweet", "url": "https://x.com/ник", "adapter": "x_user", "extra": {"handle": "ник"}}`.
|
||||||
|
|
||||||
|
## Автоаудит и непрерывные коммиты
|
||||||
|
|
||||||
|
- `python tools/selfcheck.py` — компиляция пакета, счётчики базы, распределение типов, топ источников, поиск дублей и пустых источников; пишет блок в `REPORT.md` и историю в `data/selfcheck.jsonl`.
|
||||||
|
- `bash tools/autocommit.sh [--feed]` — самоаудит (+ подкачка, если `--feed`), коммит только при реальных изменениях, пуш в оба ремоута.
|
||||||
|
- cron: каждые 6 часов аудит-коммит, каждые 12 часов аудит + подкачка источников.
|
||||||
|
- `omni dedupe [--apply]` — схлопывает дубли по url (или source+title при пустом url), вставка тоже дедуплицирует по url.
|
||||||
|
|
||||||
|
"""
|
||||||
|
anchor = "## Как устроено"
|
||||||
|
s = s.replace(anchor, extra + anchor, 1)
|
||||||
|
|
||||||
|
open(p, "w", encoding="utf-8", newline="\n").write(s)
|
||||||
|
print("README updated", len(s))
|
||||||
25
tools/_readme_stats.py
Normal file
25
tools/_readme_stats.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import collections
|
||||||
|
import sqlite3
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, ".")
|
||||||
|
from omni import sources, taxonomy # noqa: E402
|
||||||
|
|
||||||
|
src = sources.all_sources()
|
||||||
|
by_cat = collections.Counter(s.category for s in src)
|
||||||
|
con = sqlite3.connect("data/" + "omni.sqlite3")
|
||||||
|
items = con.execute("select count(*) from items").fetchone()[0]
|
||||||
|
priced = con.execute("select count(*) from items where price is not null and price>0").fetchone()[0]
|
||||||
|
runs = con.execute("select count(*) from runs").fetchone()[0]
|
||||||
|
xn = con.execute("select count(*) from items where provider='x'").fetchone()[0]
|
||||||
|
flat = collections.Counter()
|
||||||
|
for combo, n in con.execute("select types,count(*) from items where types!='' group by 1"):
|
||||||
|
for t in str(combo).split(","):
|
||||||
|
if t.strip():
|
||||||
|
flat[t.strip()] += n
|
||||||
|
print(f"sources={len(src)} items={items} priced={priced} runs={runs} x={xn}")
|
||||||
|
print(" · ".join(f"{taxonomy.LABELS.get(k, k)} {v}" for k, v in flat.most_common()))
|
||||||
|
open("data/_cats.md", "w", encoding="utf-8").write(
|
||||||
|
"\n".join(f"| `{c}` | {n} |" for c, n in by_cat.most_common()))
|
||||||
|
open("data/_types.md", "w", encoding="utf-8").write(
|
||||||
|
" · ".join(f"{taxonomy.LABELS.get(k, k)} {v}" for k, v in flat.most_common()))
|
||||||
Reference in New Issue
Block a user