Files
hechakuaa_bot/CLAUDE.md
markosbenitez 7743919695 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>
2026-05-25 10:58:32 -03:00

9.3 KiB

Pedrito Hechakuaa — Instrucciones para Claude Code

Qué es esto

Pedrito Hechakuaa: un bot de Telegram que monitorea notificaciones pendientes en el sistema judicial de Paraguay (apps.csj.gov.py) y avisa cuando aparece algo nuevo. Uso propio de un solo abogado (single-tenant).

Scope estricto de la v0:

  • Login al PJ + polling de notificaciones pendientes
  • Diff vs snapshot anterior → solo notifica lo nuevo
  • Telegram como canal de salida
  • Correr en local o VPS con docker-compose

Fuera del scope de v0 (no agregar sin consulta explícita):

  • Motor de reglas de plazos
  • LLM / conversación libre
  • Multi-tenant
  • Web de onboarding
  • Actuaciones de expedientes (es v1)
  • Acuse de notificaciones (nunca en MVP)

⚠️ Invariantes de seguridad — NUNCA se violan, aunque sea v0

✅ Credenciales cifradas con Fernet en .env — desencriptar en memoria, usar, descartar
✅ .env en .gitignore desde el primer commit
✅ Todo log pasa por scrub() en utils/sanitize.py antes de escribirse
✅ Audit trail mínimo: quién hizo qué, cuándo, resultado — SIN payload sensible
✅ El bot solo responde al TELEGRAM_CHAT_ID configurado (no es público)

❌ PUT /Notificaciones/{id}/recibir — NUNCA. Acusa notificaciones, efecto procesal irreversible
❌ Password, bearer token, o cualquier credencial en logs (ni en DEBUG)
❌ print() para debug — SIEMPRE usar logger
❌ .env con valores reales en el repo

scrub() (from utils.sanitize import scrub): función en utils/sanitize.py que recibe cualquier string y reemplaza patrones sensibles (tokens, credenciales) antes de loggear. Toda llamada a logger.*() con datos externos pasa por acá.


Arquitectura

main.py               ← entrypoint, arranca bot + poller en paralelo
config.py             ← Settings via pydantic-settings (ÚNICA fuente de config)
csj_client.py         ← HTTP client del PJ (login + notificaciones, read-only)
poller.py             ← loop de polling + diff + dispara alertas
telegram_bot.py       ← envía mensajes y maneja /comandos básicos
storage.py            ← snapshot en JSON local (sin DB por ahora)
utils/
  sanitize.py         ← credential_scrubber() — NUNCA loggear sin pasar por acá

Stack (sin cambios hasta nuevo aviso)

Capa Tecnología
Lenguaje Python 3.12 con type hints estrictos
HTTP client httpx async
Bot python-telegram-bot v21 async
Scheduler APScheduler 3
Cifrado cryptography (Fernet)
Config pydantic-settings
Logging structlog (JSON en prod, ConsoleRenderer en dev)

Sin Playwright. Sin Postgres. Sin Redis. Sin LLM. Sin Docker por ahora (opcional).

No introducir librerías fuera de esta lista sin justificación explícita.


Sistema judicial target

URL base: https://apps.csj.gov.py Docs: docs/reconocimiento/SISTEMA_NUEVO_RECON.md y MANUAL_SANITIZADO.md

Flujo de autenticación (confirmado por reconocimiento)

1. GET https://apps.csj.gov.py/login
   → Cookie: cookiesession1={hex32}

2. POST https://apps.csj.gov.py/autenticador/login
   Headers: Cookie: cookiesession1={hex32}
   Body JSON: {"usuario": "{cedula}", "clave": "{password}", "temporal": false}
   → bearerToken (TTL 1h), refreshToken, timestampExpiracionUtc

3. Todos los requests siguientes llevan:
   Cookie: cookiesession1={hex32}
   Authorization: Bearer {bearerToken}
   usuario-rol: 16   ← 16 = Abogados

Endpoints de notificaciones (confirmados)

GET /api/gestion-partes/Notificaciones/CantidadNotificacionesPorRecibir
→ int (cantidad de notificaciones pendientes)

GET /api/gestion-partes/Notificaciones/PorRecibir?offset=0&cantidad=50
→ lista de NotificacionPendiente

Campos relevantes de NotificacionPendiente

{
  "codNotificacionOrigen": 1234567,
  "nroExpedienteNumero": 1234,
  "nroExpedienteAnio": 2024,
  "caratula": "Rodríguez c/ Municipalidad Capital",
  "descripcionActuacion": "Auto Interlocutorio",
  "descripcionTipoActuacion": "Providencia",
  "fecha": "2026-05-20",
  "juzgado": "1er Juzgado Civil Capital",
  "permisoFirmado": "JWT...",
  "codCasoJudicial": 103708,
  "origen": 0
}

Estados de conversación

El bot es simple pero tiene estados implícitos que deben manejarse explícitamente:

UNREGISTERED          → usuario no autorizado (responder a cualquier message)
REGISTERED_IDLE       → autorizado, sin operación en curso (estado normal)
CHECKING              → revisión de notificaciones en curso (no disparar otra)
ERROR_AUTH            → fallo de auth del PJ (bot pausado hasta intervención)
ERROR_PASSWORD_TEMP   → password temporal detectada (bot pausado)

Manejo de errores — mensajes accionables

Nunca: "Error inesperado" o mensajes técnicos al usuario. Siempre: qué pasó + qué puede hacer el usuario.

Situación Mensaje al abogado
Login fallido por credenciales "No pude conectarme al PJ. Las credenciales podrían haber cambiado. Contactá al administrador."
Password temporal "Tu contraseña del PJ es temporal. Cambiala en apps.csj.gov.py y luego reiniciá Pedrito Hechakuaa."
Error de red transitorio "No pude conectarme al sistema judicial. Voy a reintentar en la próxima ronda ({hora})."
5 errores consecutivos "El sistema del PJ estuvo inaccesible por un tiempo. Si el problema persiste, revisá apps.csj.gov.py directamente."

Emojis con criterio: éxito, error, ⚠️ advertencia, procesando. No decorativos.


Estructura del snapshot (storage.py)

JSON local en data/snapshot.json:

{
  "last_check": "2026-05-20T10:00:00Z",
  "notificaciones_vistas": [1234567, 1234568, 1234569]
}

Solo guardamos los IDs (codNotificacionOrigen) ya notificados. Diff: cualquier ID en la respuesta del PJ que no esté en esta lista = novedad.


Comandos de Telegram

  • /start — muestra estado del bot
  • /notif — revisa notificaciones ahora (no espera al siguiente polling)
  • /estado — último check, cantidad de notificaciones activas
  • /ayuda — lista comandos
  • /exp <número> — detalle de expediente por número (ej: /exp 198 o /exp 198/2026)
  • /exp <texto> — busca expedientes por carátula (ej: /exp rodríguez)

Formato de mensajes al abogado

Notificación proactiva nueva

📋 Nueva notificación judicial

{caratula}
Exp. {numero}/{anio} — {juzgado}

Tipo: {descripcionActuacion}
Fecha: {fecha formateada DD/MM/YYYY}

⚠️ Para acusar esta notificación entrá directamente
en https://apps.csj.gov.py

Sin novedades (solo cuando el abogado lo pide con /notif)

✅ Sin novedades desde las {hora} de hoy.
Tenés {N} notificaciones pendientes en total.

Iniciativa agéntica — clasificación de cambios

Nivel Tipo de cambio Acción
1 Refactor interno, naming, código muerto Ejecutar libremente
2 Cambio visible en mensajes del bot o UX conversacional Ejecutar y reportar
3 Seguridad, credenciales, dependencias nuevas, scope del bot Consultar antes

Argumentos NO válidos para evitar consulta de Nivel 3:

  • "Es una mejora de legibilidad"
  • "Es trivial de revertir"
  • "Resuelve un bug menor"

Definition of Done — cada feature o fix

  • Happy path funciona en ejecución real (python main.py)
  • Credenciales no aparecen en logs (verificar con LOG_LEVEL=DEBUG)
  • Maneja desconexión del PJ con retry + mensaje claro al usuario
  • Maneja timeout de sesión: relogin automático transparente
  • Maneja datos vacíos (cero notificaciones, response inesperado)
  • credential_scrubber() usado en cualquier log con datos externos
  • Type hints completos en funciones nuevas o modificadas
  • Sin print() en código nuevo

Alerta especial: fragilidad del cliente HTTP

El csj_client.py es la parte más probable de romperse. Ante cualquier reporte de "el cliente funciona", verificar:

  • ¿Se probó con credenciales reales o con mocks?
  • ¿Los campos del JSON del PJ son los actuales o los del último test?
  • ¿El timeout fue suficiente para la red real?

Ante cambio de schema del PJ: actualizar csj_client.py y documentar en OBSERVATIONS.md qué cambió y cuándo.


Archivos del proyecto

Archivo Propósito
CLAUDE.md Este archivo — reglas para Claude Code
OBSERVATIONS.md Hallazgos durante el desarrollo (crear si no existe)
TECH_DEBT.md Deuda técnica anotada (crear si no existe)
data/snapshot.json Estado persistido del poller

Cómo arrancar

# Setup (una sola vez)
pip install uv && uv sync
cp .env.example .env
# Completar .env: FERNET_KEY, TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID

# Cifrar credenciales del PJ
python scripts/cifrar_credenciales.py

# Arrancar
python main.py

# Tests
pytest
pytest -k "poller" -v
pytest --co -q   # solo listar tests sin correr

Cómo migrar de v0 a v1

La v0 está diseñada para migración incremental:

  • csj_client.pyapp/scraper/ con el mismo contrato de métodos
  • storage.py → Postgres con el mismo interface
  • config.py → se expande con más settings
  • telegram_bot.pyapp/bot/channels/telegram/
  • poller.pyapp/poller/
  • utils/sanitize.py → se mantiene igual

No es código desechable.