YouTube baneó mi VPS
El 17 de abril por la mañana el cronjob noticias-ilustradas me mandó un mensaje distinto a lo habitual:
Localicé el vídeo de hoy de NOTICIASILUSTRADAS pero no pude extraer el transcript. Todas las vías fallaron — el skill de Hermes, yt-dlp con cookies, curl directo. La IP del VPS está baneada por YouTube para transcripciones.
Hasta ese mensaje yo creía que el digest matutino era robusto. Llevaba semanas funcionando. Pero un cambio silencioso en la política de YouTube (¿rate limit? ¿heurística de datacenter?) lo había dejado fuera.
Ese día acabé haciendo tres fixes en paralelo. Los tres se reducen a la misma lección: un sistema que "funciona" y un sistema que "es robusto" son cosas distintas.
Fix 1: del transcript de YouTube a Brave Search API
La ruta vieja
Cron → buscar vídeo del día de NOTICIASILUSTRADAS
→ yt-dlp descarga + extrae subtítulos
→ LLM resume
→ TTS → voice message Telegram
Dependía de YouTube en dos sitios: listar vídeos del canal, y extraer transcript. Los dos pasos sensibles a IP.
La pregunta que me hice
Antes de montar nada, me paré un segundo: ¿de verdad necesito YouTube? El objetivo real es darme un resumen de noticias del día en audio a las 06:57. YouTube era el cómo, no el qué.
La ruta nueva
Cron → Brave Search API con query de actualidad
→ LLM resume los titulares
→ TTS → voice message Telegram
Brave Search API no banea IPs de datacenter. No necesita cookies. No necesita transcript. Y a la hora del digest, los titulares frescos son más útiles que un vídeo de 15 minutos subido anoche.
Cambié audio_matutino.py, lo corrí manualmente, salió el voice message de 407KB, reinstalé el cron (57 6 * * *). 20 minutos.
La pregunta "¿necesito esto de verdad?" ahorra más código que cualquier refactor.
Fix 2: el RPi residencial como safety net
Aunque Brave resolvió el caso concreto, el problema de fondo sigue: cualquier servicio que banee IPs de Hostinger me deja sin ese cron. Y eso va a pasar otra vez.
Ya tenía el proxy residencial del RPi (montado el 12 de abril, 100.78.170.78:8888 vía Tailscale). Ese día lo dejé documentado como "opción B para cuando haga falta". Pues hacía falta ya.
Añadí el proxy como fallback opcional en los scripts sensibles: si detecto bloqueo, pivotar a salida residencial. No reescribí todo — solo marqué los puntos donde podría reengancharse fácil.
Lección: tener la infra preparada no basta. Tienes que marcar explícitamente dónde se usará cuando haga falta, o cuando llegue el fallo estarás reinventando la rueda.
El proxy RPi lo había montado hacía 5 días "por si acaso". Ese "por si acaso" llegó a las 72 horas. El "por si acaso" siempre llega antes de lo que crees.
Fix 3: política drafts-only en el sender de emails
El mismo día reforcé algo que venía arrastrando: el script send_via_gmail.py que usa Diana Ferrer (desarrollo de negocio de OhanaSmart) para mandar outreach.
El miedo
Tengo un cronjob diario que prepara un batch de prospects para enviar. Si ese cron, por un bug o un prompt mal afinado, decidiera enviar 50 emails a decisores del sector hotelero con un tono cutre — no hay vuelta atrás. Eso quema el negocio entero.
La solución
send_via_gmail.py ya no envía. Crea borrador en Gmail.
# Antes:
service.users().messages().send(userId='me', body=message)
# Ahora:
service.users().drafts().create(userId='me', body={'message': message})
Una llamada distinta a la Gmail API. Cero cambio de flujo para mí: cada mañana abro Gmail, reviso la columna "Borradores", y mando los que tienen sentido. Los que no, los edito o los borro.
Esa línea de código es más robusta que cualquier governance toolkit. No depende de un runtime de seguridad, no se puede bypassear, es arquitectónica. Si mi agente se vuelve loco mañana, lo peor que puede hacer es dejarme 50 borradores raros. Nada sale fuera sin mi click.
Regla: para decisiones irreversibles (email a un cliente, commit a main, payment, envío real), poner un humano en medio no es fricción — es la única defensa que no se puede hackear.
Fix 4 (bonus): los templates de Diana en divs, no tables
El mismo día descubrí que los templates HTML de los emails de Diana se rompían en Gmail al enviarlos como drafts. El <table> se transformaba en columnas aleatorias al abrir el borrador desde la UI de Gmail.
Reescribí todos los templates a <div> con max-width: 80% y estilos inline. Firma a 14px, sin centrado, imagen inline vía Content-ID.
No es glamoroso. Pero la diferencia entre un email que se ve profesional en Gmail y uno que parece un formulario de 2003 es ese detalle del <div> vs <table>.
El hilo común
Los cuatro cambios del día (YouTube → Brave, RPi como fallback, drafts-only, templates con divs) responden a la misma pregunta:
"Cuando esto falle — porque va a fallar — ¿qué pasa?"
- Cuando YouTube banea → el digest cambia a Brave Search, cero intervención.
- Cuando otro servicio banea → el RPi está ahí como salida residencial.
- Cuando mi agente se descontrola → lo peor es un borrador, no un envío.
- Cuando Gmail rompe un
<table>→ el<div>aguanta.
Cada uno cierra una modalidad de fallo. Ninguno es sofisticado. Ninguno necesita arquitectura nueva. Los cuatro son decisiones pequeñas con consecuencias grandes.
Lo que aprendí
-
"Funciona" no es "es robusto". Un sistema que funciona hasta que cambia algo externo (política de IP, rate limit, formato HTML) es frágil por diseño. Robusto es lo que aguanta el cambio.
-
Pregúntate si lo necesitas antes de arreglarlo. El fix de YouTube no fue arreglar YouTube. Fue darme cuenta de que YouTube era la implementación, no el requisito.
-
La infra preparada vale cero hasta que la usas. El RPi llevaba 5 días montado como "por si acaso". Hasta que no lo enganché a un script real, era un adorno.
-
Los controles arquitectónicos ganan a los controles de runtime. Drafts-only es una sola línea de código que hace lo que ningún governance toolkit puede hacer: imposibilitar el fallo catastrófico.
-
Los detalles cutres matan la credibilidad. Un email a un director de residencias con un template roto es peor que no haberlo mandado. Invertir una tarde en templates que no revientan en Gmail vale más que un día de prospección.
Qué viene
- Enganchar el RPi proxy como fallback automático cuando un request falla por IP (hoy es manual)
- Monitor del cron
noticias-ilustradasque avise si el output baja de X KB (proxy para "algo se rompió") - Replicar la política drafts-only a cualquier script que envíe algo externo — Telegram también debería ser reviewable antes de enviar en casos sensibles
Ningún cambio grande. Muchos cambios pequeños. Esa es la diferencia entre un sistema que funciona un mes y uno que aguanta un año.
— yo, Johnny — agente configurado: Harvie. La robustez no se añade al final. Se descubre cada vez que algo se rompe.