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

@@ -4,6 +4,7 @@
source_flags (name, enabled, status, items, note, checked)
source_custom (name, category, kind, url, adapter, tags, limit, detail, added)
"""
import contextlib
import json
import sqlite3
import time
@@ -34,9 +35,24 @@ def conn():
return c
@contextlib.contextmanager
def db():
"""Соединение с гарантированным close(): `with conn()` коммитит, но не закрывает,
и хендлы копятся (замер: 200 вызовов == +76 handles)."""
c = conn()
try:
yield c
c.commit()
except Exception:
c.rollback()
raise
finally:
c.close()
def flags():
"""{name: {enabled, status, items, note, checked}}"""
with conn() as c:
with db() as c:
rows = c.execute("SELECT name, enabled, status, items, note, checked FROM source_flags").fetchall()
return {r[0]: {"enabled": bool(r[1]), "status": r[2], "items": r[3], "note": r[4], "checked": r[5]}
for r in rows}
@@ -44,7 +60,7 @@ def flags():
def set_enabled(name, enabled):
now = int(time.time())
with conn() as c:
with db() as c:
c.execute("INSERT INTO source_flags(name, enabled, checked) VALUES(?,?,?) "
"ON CONFLICT(name) DO UPDATE SET enabled=excluded.enabled",
(name, 1 if enabled else 0, now))
@@ -53,7 +69,7 @@ def set_enabled(name, enabled):
def record_check(name, status, items, note=""):
now = int(time.time())
with conn() as c:
with db() as c:
c.execute("INSERT INTO source_flags(name, enabled, status, items, note, checked) VALUES(?,1,?,?,?,?) "
"ON CONFLICT(name) DO UPDATE SET status=excluded.status, items=excluded.items, "
"note=excluded.note, checked=excluded.checked",
@@ -62,7 +78,7 @@ def record_check(name, status, items, note=""):
def custom():
with conn() as c:
with db() as c:
rows = c.execute("SELECT name, category, kind, url, adapter, tags, lim, detail, added "
"FROM source_custom ORDER BY added DESC").fetchall()
out = []
@@ -81,7 +97,7 @@ def add_custom(name, category, url, kind=None, adapter=None, tags=(), limit=20,
if not url.startswith("http"):
raise ValueError("url должен начинаться с http")
kind, adapter = kind or guess_kind(url), adapter or guess_adapter(url, kind)
with conn() as c:
with db() as c:
c.execute("INSERT OR REPLACE INTO source_custom(name, category, kind, url, adapter, tags, lim, detail, added) "
"VALUES(?,?,?,?,?,?,?,?,?)",
(name, category or "custom", kind, url, adapter,
@@ -91,7 +107,7 @@ def add_custom(name, category, url, kind=None, adapter=None, tags=(), limit=20,
def remove_custom(name):
with conn() as c:
with db() as c:
c.execute("DELETE FROM source_custom WHERE name=?", (name,))
c.execute("DELETE FROM source_flags WHERE name=?", (name,))
return {"removed": name}