feat: fase 3 — export PDF con reportlab + /export command
Build & Deploy ResearchOwl / build-and-push (push) Successful in 1m2s
Build & Deploy ResearchOwl / build-and-push (push) Successful in 1m2s
This commit is contained in:
@@ -146,6 +146,7 @@ async def cmd_start(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
||||
" Extended: podcast_extended|blog_extended|report_extended\n"
|
||||
"`/sources` — List all sources found\n"
|
||||
"`/outputs` — List generated outputs\n"
|
||||
"`/export` — Exportar último output como PDF\n"
|
||||
"`/costs` — Show API usage costs\n"
|
||||
"`/watch <topic> [h]` — Schedule periodic research\n"
|
||||
"`/unwatch <topic>` — Remove a watch\n"
|
||||
@@ -760,6 +761,83 @@ async def _on_startup(app: Application) -> None:
|
||||
await _start_scheduler(app)
|
||||
|
||||
|
||||
async def cmd_export(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
||||
if not is_authorized(update.effective_user.id):
|
||||
return
|
||||
|
||||
chat_id = update.effective_chat.id
|
||||
db_conn = await get_db()
|
||||
db = ResearchDB(db_conn)
|
||||
|
||||
try:
|
||||
session = await db.get_latest_session(chat_id)
|
||||
if not session:
|
||||
await update.message.reply_text("No hay sesiones de investigación.")
|
||||
return
|
||||
|
||||
session_id = session["id"]
|
||||
topic = session["topic"]
|
||||
|
||||
outputs = await db.get_outputs(session_id)
|
||||
if not outputs:
|
||||
await update.message.reply_text(
|
||||
"No hay outputs generados. Usa `/generate <tipo>` primero.",
|
||||
parse_mode=ParseMode.MARKDOWN
|
||||
)
|
||||
return
|
||||
|
||||
priority = [
|
||||
"report_extended", "blog_extended", "podcast_extended",
|
||||
"report", "blog", "podcast", "thread",
|
||||
]
|
||||
chosen = None
|
||||
for ptype in priority:
|
||||
for o in outputs:
|
||||
if o["output_type"] == ptype:
|
||||
chosen = o
|
||||
break
|
||||
if chosen:
|
||||
break
|
||||
if not chosen:
|
||||
chosen = outputs[0]
|
||||
|
||||
msg = await update.message.reply_text(
|
||||
f"📄 Generando PDF para `{topic}`…",
|
||||
parse_mode=ParseMode.MARKDOWN
|
||||
)
|
||||
|
||||
try:
|
||||
from src.generator.generator import generate_pdf
|
||||
pdf_bytes = generate_pdf(chosen["content"], title=topic)
|
||||
except ImportError:
|
||||
await msg.edit_text("❌ reportlab no está instalado. Ejecuta: `pip install reportlab`")
|
||||
return
|
||||
except Exception as e:
|
||||
await msg.edit_text(f"❌ Error generando PDF: {str(e)[:200]}")
|
||||
return
|
||||
|
||||
safe_topic = topic[:40].replace(" ", "_").replace("/", "-")
|
||||
filename = f"researchowl_{safe_topic}_{chosen['output_type']}.pdf"
|
||||
|
||||
import io
|
||||
await update.message.reply_document(
|
||||
document=io.BytesIO(pdf_bytes),
|
||||
filename=filename,
|
||||
caption=f"📄 *{chosen['output_type'].upper()}* — {topic}\nExportado por ResearchOwl 🦉",
|
||||
parse_mode=ParseMode.MARKDOWN
|
||||
)
|
||||
try:
|
||||
await msg.delete()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Export failed", error=str(e))
|
||||
await update.message.reply_text(f"❌ Export failed: {str(e)[:200]}")
|
||||
finally:
|
||||
await db_conn.close()
|
||||
|
||||
|
||||
async def cmd_purge(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
|
||||
if not is_authorized(update.effective_user.id):
|
||||
return
|
||||
@@ -820,6 +898,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("export", cmd_export))
|
||||
app.add_handler(CommandHandler("costs", cmd_costs))
|
||||
app.add_handler(CommandHandler("watch", cmd_watch))
|
||||
app.add_handler(CommandHandler("unwatch", cmd_unwatch))
|
||||
|
||||
Reference in New Issue
Block a user