fix: 10 пунктов ревью — /run (кортеж store.add), валидация limit/offset в панели, close() соединений SQLite, троттлинг под локом, read-only бот без OMNI_BOT_OWNERS, eviction JOBS, push_new отдаёт свежие записи, ai.py не спит после последней попытки, дедуп реестра, подключён abuse-реестр (+191 источник); tests/test_regress.py 16 проверок + CI py3.10-3.12
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:
ENI
2026-10-26 20:15:50 +03:00
parent 136aa88dd6
commit c9c0a11ef7
16 changed files with 806 additions and 54 deletions

View File

@@ -1,7 +1,7 @@
"""Telegram-бот поверх парсера: лента, абузы, источники, прогон, автодобавление.
Запуск: OMNI_BOT_TOKEN=... python -m omni.bot
Доступ: OMNI_BOT_OWNERS=7448683285,123 (если пусто — только первый написавший)
Доступ: OMNI_BOT_OWNERS=<id>,<id> (пусто = read-only: /new /abuse /find /src /stats)
Бот не постит никуда сам: только отвечает в личный чат тем, кто в списке.
Команды:
@@ -157,7 +157,8 @@ def cmd_run(chat_id, arg):
items = []
got += len(items)
if items:
new += store.add(items)
n_new, _dup = store.add(items)
new += n_new
_send(chat_id, "готово: собрано %d, новых %d" % (got, new))
@@ -455,7 +456,7 @@ def push_new(per_chat=5, window=2000):
fresh = [i for i in items if (i.get("id") or 0) > (last or 0)]
if not fresh:
continue
chunk = fresh[-per_chat:]
chunk = fresh[:per_chat] # раньше уходили САМЫЕ СТАРЫЕ из окна
fmt = prefs_get(target) if isinstance(target, int) else "short"
tr = sm = 0
if isinstance(target, int):
@@ -489,6 +490,8 @@ def pusher(every=300):
print("pusher fail", e, flush=True)
READONLY_CMDS = {"/start", "/help", "/new", "/abuse", "/find", "/src", "/stats"}
CMDS = {"/start": cmd_start, "/help": cmd_start, "/new": cmd_new, "/abuse": cmd_abuse,
"/find": cmd_find, "/src": cmd_src, "/add": cmd_add, "/run": cmd_run,
"/auto": cmd_auto, "/stats": cmd_stats,
@@ -504,13 +507,14 @@ def handle(update):
uid = (msg.get("from") or {}).get("id")
if not chat_id or not text:
return
if OWNERS and uid not in OWNERS:
return
if not OWNERS:
OWNERS.add(uid) # первый написавший становится владельцем до рестарта
parts = text.split(None, 1)
cmd = parts[0].lower().split("@")[0]
arg = parts[1] if len(parts) > 1 else ""
if OWNERS and uid not in OWNERS:
return
if not OWNERS and cmd not in READONLY_CMDS:
# раньше первый написавший становился владельцем: любой, кто нашёл бота
return _send(chat_id, "read-only: задай OMNI_BOT_OWNERS и перезапусти бота")
fn = CMDS.get(cmd)
if not fn:
return _send(chat_id, "не знаю такую команду. /help")