Initial commit — Pedrito Hechakuaa v0
Bot de Telegram para monitoreo de notificaciones judiciales del PJ Paraguay. Multi-tenant con onboarding conversacional, audit log, y polling automático. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
146
main.py
Normal file
146
main.py
Normal file
@@ -0,0 +1,146 @@
|
||||
"""
|
||||
main.py — Entrypoint de Pedrito Hechakuaa.
|
||||
|
||||
Arranca el bot de Telegram y el poller en paralelo.
|
||||
Al arrancar: inicializa la DB y migra el tenant principal del .env si no existe.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import signal
|
||||
import sys
|
||||
|
||||
import structlog
|
||||
from telegram.ext import Application
|
||||
|
||||
import vault as vault_mod
|
||||
from config import get_settings
|
||||
from database import get_tenant, init_db, save_tenant
|
||||
from onboarding import OnboardingFlow
|
||||
from poller import Poller
|
||||
from telegram_bot import PedritoBot
|
||||
|
||||
# Configurar structlog
|
||||
structlog.configure(
|
||||
processors=[
|
||||
structlog.contextvars.merge_contextvars,
|
||||
structlog.processors.add_log_level,
|
||||
structlog.processors.TimeStamper(fmt="iso"),
|
||||
structlog.dev.ConsoleRenderer() if sys.stderr.isatty() else structlog.processors.JSONRenderer(),
|
||||
]
|
||||
)
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
async def _migrar_tenant_principal(settings) -> None:
|
||||
"""
|
||||
Migra el tenant principal del .env a la DB si todavía no existe.
|
||||
Permite que el dueño del bot siga funcionando sin re-registrarse.
|
||||
"""
|
||||
chat_id = settings.telegram_chat_id
|
||||
existente = await get_tenant(chat_id)
|
||||
if existente:
|
||||
logger.info("tenant_principal_ya_existe", chat_id=chat_id)
|
||||
return
|
||||
|
||||
logger.info("migrando_tenant_principal_del_env", chat_id=chat_id)
|
||||
usuario = settings.descifrar_usuario()
|
||||
password = settings.descifrar_password()
|
||||
try:
|
||||
credentials_enc = vault_mod.cifrar_credenciales(
|
||||
chat_id=chat_id,
|
||||
usuario=usuario,
|
||||
password=password,
|
||||
fernet_key=settings.fernet_key,
|
||||
)
|
||||
finally:
|
||||
del usuario, password
|
||||
|
||||
await save_tenant(
|
||||
chat_id,
|
||||
nombre_preferido="Admin",
|
||||
tono="cotidiano",
|
||||
timezone="America/Asuncion",
|
||||
credentials_enc=credentials_enc,
|
||||
estado="activo",
|
||||
consentimiento_aceptado=1,
|
||||
)
|
||||
logger.info("tenant_principal_migrado", chat_id=chat_id)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
settings = get_settings()
|
||||
|
||||
logger.info(
|
||||
"pedrito_hechakuaa_iniciando",
|
||||
poll_interval_minutes=settings.poll_interval_minutes,
|
||||
horario=f"{settings.poll_hora_inicio}:00-{settings.poll_hora_fin}:00",
|
||||
chat_id=settings.telegram_chat_id,
|
||||
)
|
||||
|
||||
# Base de datos
|
||||
await init_db(settings.db_path)
|
||||
await _migrar_tenant_principal(settings)
|
||||
|
||||
# Bot de Telegram
|
||||
onboarding = OnboardingFlow(settings)
|
||||
pedrito_bot = PedritoBot(
|
||||
token=settings.telegram_bot_token,
|
||||
settings=settings,
|
||||
onboarding=onboarding,
|
||||
)
|
||||
app: Application = pedrito_bot.build()
|
||||
|
||||
# Poller (sin snapshot global — maneja uno por tenant internamente)
|
||||
poller = Poller(
|
||||
settings=settings,
|
||||
bot=pedrito_bot,
|
||||
)
|
||||
|
||||
# El callback de /notif pasa el chat_id al poller
|
||||
pedrito_bot.set_notif_callback(
|
||||
lambda chat_id: poller.revisar_ahora(chat_id=chat_id, es_manual=True)
|
||||
)
|
||||
|
||||
# Manejo de señales para shutdown limpio
|
||||
loop = asyncio.get_running_loop()
|
||||
|
||||
def handle_shutdown(sig: int) -> None:
|
||||
logger.info("shutdown_recibido", signal=sig)
|
||||
poller.detener()
|
||||
loop.stop()
|
||||
|
||||
for sig in (signal.SIGINT, signal.SIGTERM):
|
||||
try:
|
||||
loop.add_signal_handler(sig, handle_shutdown, sig)
|
||||
except NotImplementedError:
|
||||
pass # Windows no soporta add_signal_handler
|
||||
|
||||
# Arrancar bot y poller en paralelo
|
||||
async with app:
|
||||
await app.initialize()
|
||||
await app.start()
|
||||
|
||||
logger.info("bot_telegram_iniciado")
|
||||
|
||||
telegram_task = asyncio.create_task(
|
||||
app.updater.start_polling(drop_pending_updates=True)
|
||||
)
|
||||
poller_task = asyncio.create_task(poller.run_forever())
|
||||
|
||||
logger.info("pedrito_hechakuaa_corriendo")
|
||||
|
||||
try:
|
||||
await asyncio.gather(telegram_task, poller_task)
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
finally:
|
||||
await app.updater.stop()
|
||||
await app.stop()
|
||||
|
||||
logger.info("pedrito_hechakuaa_detenido")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user