feat(news): F1 — monitor RSS best-effort + comando /news manual
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
Build & Deploy ResearchOwl / build-and-push (push) Successful in 7s
src/news/monitor.py: 7 feeds UAP/OVNI, matching normalizado (NFKD sin tildes) con word-boundary para tokens cortos (uap/ufo/nhi/aaro/ovni) y substring para frases. poll_feeds aísla cada feed (uno caído nunca tumba el run) y parsea en hilo aparte para no bloquear el event loop; cold-start por fuente (siembra sin notificar). format_digest agrupa por fuente y trocea <4000 chars. /news refresca y muestra novedades 24h. Capa desacoplada: src/news NO importa de src/bot. No toca el scheduler (F2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
fd814a3ccf
commit
c0b0c1ba0a
@@ -22,6 +22,7 @@ from src.db.database import get_db, close_db, ResearchDB, ResearchStatus, Output
|
||||
from src.scraper.exhaustive import ExhaustiveScraper
|
||||
from src.processor.processor import OllamaClient, ContentProcessor
|
||||
from src.generator.generator import OutputGenerator
|
||||
from src.news.monitor import poll_feeds, item_from_row, format_digest
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
@@ -503,6 +504,33 @@ async def cmd_outputs(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
||||
await db_conn.close()
|
||||
|
||||
|
||||
async def cmd_news(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
||||
"""Monitor RSS manual (sin scheduler — F2 lo automatiza). Refresca news_seen
|
||||
(siembra en cold-start) y muestra las novedades matcheadas de las últimas 24h."""
|
||||
if not is_authorized(update.effective_user.id):
|
||||
return
|
||||
|
||||
db_conn = await get_db()
|
||||
db = ResearchDB(db_conn)
|
||||
try:
|
||||
await update.message.reply_text("📡 Buscando novedades UAP/OVNI…")
|
||||
try:
|
||||
await poll_feeds(db, settings) # refresca tabla (siembra en cold-start)
|
||||
except Exception:
|
||||
logger.exception("news poll failed") # best-effort: nunca rompe el comando
|
||||
|
||||
recent = await db.get_recent_news(24)
|
||||
if not recent:
|
||||
await update.message.reply_text("Sin novedades UAP/OVNI en las últimas 24h.")
|
||||
return
|
||||
|
||||
items = [item_from_row(r) for r in recent]
|
||||
for chunk in format_digest(items):
|
||||
await update.message.reply_text(chunk, disable_web_page_preview=False)
|
||||
finally:
|
||||
await db_conn.close()
|
||||
|
||||
|
||||
async def cmd_costs(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
||||
if not is_authorized(update.effective_user.id):
|
||||
return
|
||||
@@ -1292,6 +1320,7 @@ def create_bot() -> Application:
|
||||
app.add_handler(CommandHandler("generate", cmd_generate))
|
||||
app.add_handler(CommandHandler("sources", cmd_sources))
|
||||
app.add_handler(CommandHandler("outputs", cmd_outputs))
|
||||
app.add_handler(CommandHandler("news", cmd_news))
|
||||
app.add_handler(CommandHandler("export", cmd_export))
|
||||
app.add_handler(CommandHandler("costs", cmd_costs))
|
||||
app.add_handler(CommandHandler("watch", cmd_watch))
|
||||
|
||||
Reference in New Issue
Block a user