Handoff técnico · IMVU

Radio en la sala: un bot de música para IMVU, de cero al aire

El camino completo que recorrimos — la arquitectura, cada fase del montaje y, sobre todo, el porqué de cada pieza. Hecho para que repitas los pasos y tengas tu propio DJ en tu sala.

★ léelo antes de montar

Aquí hay dos caminos — y conviene elegir antes de meter las manos. El capítulo 03 (Las fases del montaje) monta la versión con navegador: un Chromium invisible que lee la página de la sala. Es la forma más didáctica de entender cada pieza, y fue como empezamos.

Pero hoy ya no usamos navegador. La versión que de verdad funciona es un cliente WebSocket ~10x más liviano, y reaprovecha casi todo de aquí — solo cambia la forma de hablar con el chat. Si sigues las fases en orden, vas a montar el Chromium primero y solo después descubrir que dejó de hacer falta.

¿Quieres ir directo al método actual? Salta a El giro — el camino dorado: te lleva de cero al aire por sí solo, reutilizando las fases que no cambiaron.

// 01

La idea en una frase

Antes de los pasos, entiende el diseño. Sabiendo qué resuelve cada pieza, adaptas en vez de copiar a ciegas.

Una cuenta de IMVU aparte se convierte en el "bot". Funciona 24/7 en una máquina virtual gratis que hace dos cosas a la vez: transmite una radio de música (un stream de audio) y mantiene un navegador invisible con la sesión iniciada en tu sala, leyendo el chat y obedeciendo comandos. IMVU apunta a la dirección de la radio; la gente en la sala escucha la música y conversa con el bot.

Por qué separado así

Cada pieza resuelve un problema real que solo descubrimos a golpes. Las secciones de abajo explican cada decisión — lee los "porqués", valen más que el código. Nota: empezamos con un navegador invisible (Chromium); después evolucionó a un cliente sin navegador, mucho más liviano — el capítulo El giro cuenta cómo y por qué (y por qué el "bot = Chromium" del diagrama es la versión original).

// 02

Antes de empezar

Qué tener a mano. Nada de esto cuesta dinero.

Instala las herramientas del PC de una vez. Elige tu sistema, haz clic en copiar y pégalo en tu terminal (la página no ejecuta nada por ti — ves el comando antes de ejecutarlo).

# Windows 10/11 - winget ya viene incluido (Terminal o PowerShell)
winget install OpenJS.NodeJS.LTS
winget install yt-dlp.yt-dlp
winget install Gyan.FFmpeg
winget install Git.Git
# macOS - con Homebrew (instala brew antes, en brew.sh)
brew install node yt-dlp ffmpeg git
# Debian/Ubuntu - el Node de apt puede ser viejo; para v18+ mira nodejs.org
sudo apt update
sudo apt install -y nodejs npm ffmpeg git python3-pip
python3 -m pip install -U yt-dlp
Cuidado

Guarda los secretos (la sesión del bot, la clave de Gemini, el token de DuckDNS) fuera del control de versiones. Un .gitignore con auth.json, *.key, *.token y cache/ evita filtrar todo en el primer git push.

// 03

Las fases del montaje

El orden importa — cada fase se apoya en la anterior. Monta y prueba en tu PC primero; solo después súbelo a la nube.

0Prototipo
En tu PC · Node + Playwright

Entrar a la sala y leer el chat

En el fondo, el bot es un navegador automatizado. Usa Playwright para abrir un Chromium, iniciar sesión con la cuenta del bot una vez y guardar la sesión (un login.js que graba un auth.json). Después solo reabres la sala reutilizando esa sesión.

Dentro de la sala, IMVU se comunica por un WebSocket (el "IMQ"). El bot escucha los mensajes que llegan por ahí, y así es como "lee" el chat y detecta comandos. Empieza simple: iniciar sesión, entrar a la sala, imprimir en la terminal cada mensaje que aparece.

Por qué Playwright

IMVU no tiene API pública de chat. La forma confiable de participar en la sala es ser un cliente de verdad — un navegador con la sesión iniciada. Headless (invisible) para funcionar en un servidor sin pantalla.

1Radio
En tu PC · server.js

El stream de audio (la rocola)

Un servidor HTTP simple que transmite un MP3 continuo en un puerto (ej.: :8000). Tiene una carpeta de canciones de "relleno" (filler, las que suenan solas), mezcla el orden y expone rutas internas para encolar y saltar pistas (/add, /skip, /status). El bot habla con esas rutas por localhost.

Por qué un proceso separado del bot

Más adelante vas a querer reiniciar el bot (acumula memoria) sin cortar la música. Mantener la radio como un servicio propio hace que el sonido siga aunque el bot se reinicie.

2Comandos
Uniendo las dos mitades

Los comandos del chat se vuelven canciones

Ahora conecta lo que el bot lee del chat con la rocola. Un mensaje como !play nombre de la canción hace que el bot llame al stream por localhost y la encole. !skip, !queue etc. siguen la misma idea.

3Puente
En tu PC · resolver + túnel

Resolver canciones de YouTube (el bridge)

Para poner "cualquier canción", el bot necesita descargar de YouTube — y ahí está la mayor trampa: YouTube bloquea las IP de datacenter, así que la VM no puede descargar sola. La solución es un pequeño agente HTTP en el PC de tu casa (IP residencial, que YouTube acepta) ejecutando yt-dlp. La VM llega a ese agente por un túnel SSH inverso.

Cuidado

Eso significa que !play/!add solo funcionan con tu PC encendido y el bridge corriendo. Todo lo demás (radio, chat, IA) funciona sin él. Si no quieres esa dependencia, puedes poner solo la carpeta local de canciones y olvidarte de YouTube.

4Nube
Oracle Always Free · systemd

Subir todo a la máquina virtual

Crea una instancia Always Free (la E2.1.Micro, 1 GB de RAM, es la más fácil de conseguir; la A1 ARM da más margen si hay cupo). Ubuntu. Instala Node, el Chromium de Playwright y ffmpeg, manda los archivos por scp y registra los dos servicios en systemd para que arranquen con el sistema y se reinicien solos.

# esqueleto de un servicio systemd (/etc/systemd/system/bot.service)
[Service]
ExecStart=/usr/bin/node /home/ubuntu/bot/bot.js
Restart=always
WorkingDirectory=/home/ubuntu/bot
Cuidado · 1 GB es poco

Solo el Chromium se come ~500 MB. Activa swap (ej.: 2 GB en /swapfile) o la VM se cuelga. Y olvídate de 3D/WebGL en esa máquina — lo intentamos y se quedó sin memoria. Ejecuta el navegador sin 3D.

5HTTPS
DuckDNS + Caddy + firewall

Dejar la radio pública y segura

IMVU Next es una página HTTPS y rechaza direcciones de stream en HTTP simple — TLS es obligatorio. Pero Let's Encrypt no emite certificados para una IP "pelada", y por eso existe DuckDNS: te da un dominio gratis que apunta a la IP de la VM. Delante de todo va Caddy, que consigue el certificado HTTPS automáticamente y lo reenvía al stream.

# Caddyfile — reenvía el 443 al stream local
tu-nombre.duckdns.org {
    reverse_proxy localhost:8000 {
        flush_interval -1   # ESENCIAL: sin esto Caddy retiene el búfer y el sonido se traba
    }
}
Cuidado · dos capas de firewall

En Oracle, un puerto solo se abre si las dos lo permiten: el firewall del sistema (iptables) y la "Security List" de OCI. Abre 22, 80 y 443; deja el 8000 cerrado al público (solo Caddy habla con él por dentro). Y recuerda: la IP de Oracle es efímera — un cron que actualice DuckDNS cada 5 min mantiene el dominio correcto si la IP cambia.

6Conectar
En la configuración de la sala

Apuntar IMVU a la radio

En el panel de edición de tu sala en IMVU, pega la URL del stream (https://tu-nombre.duckdns.org/stream) en el campo de música. Listo: quien entre a la sala ya escucha la radio. El bot, con la sesión de la cuenta dueña de la sala, aparece en el chat.

7Robustez
Lo que lo mantiene en pie solo

Sobrevivir 24/7 sin niñera

Un bot que se cae de madrugada no sirve. Estas defensas nacieron, una por una, de problemas reales:

  • Heartbeat anti-inactividad: IMVU expulsa a los avatares quietos. El bot manda una notita (♫) de vez en cuando si estuvo callado, para parecer activo.
  • Flags anti-throttle en Chromium: una pestaña invisible tiene sus temporizadores estrangulados por el navegador, lo que mata el keepalive del WebSocket y tumba al bot cada ~8 min. Activa --disable-background-timer-throttling (y sus hermanas).
  • Self-heal (autocuración): si el WebSocket se cae, se reconecta solo.
  • Watchdog: un temporizador independiente de la página que reinicia el proceso si se congela (en la máquina de 1 GB, a veces la página se traba).
  • "Breather" (respiro): un reinicio programado (ej.: 2 veces al día) para limpiar la memoria antes de que la fuga lenta termine en OOM. Como el stream está aparte, la música no para.
  • Detección de expulsión: consultando la lista de participantes de la sala, el bot nota si lo sacaron y vuelve a entrar.
Lección general

Casi toda esta lista salió de "¿por qué desapareció otra vez?". Anota tus propios síntomas — lo que parece un bug casi siempre es IMVU o Chromium reaccionando a algo específico.

8Alma
Opcional · Gemini + memoria

Personalidad e IA

Con la base en pie, lo demás es sabor. Conecta un modelo (usamos Gemini, tiene nivel gratis) para dar respuestas con una persona; deja que el bot decida solo cuándo el mensaje es para él (respondiendo "saltar" cuando no lo es, para no entrometerse). Puedes agregar saludos, memoria de quién ya visitó, conocimiento de la propia sala/sus muebles y una ventana de conversación continua.

Cuidado · el alias "-latest"

Si usas un modelo con un nombre tipo -latest, ten en cuenta que "avanza" solo y un día puede romperse en silencio (ya nos pasó). Fija una versión específica y prueba después de cualquier cambio.

// 04 · actualización

El giro: cuando el navegador dejó de hacer falta

★ método actual · sin navegador

Las fases de arriba son como empezamos — y enseñaron todo. Este capítulo es lo que usamos hoy, y se sostiene solo: más abajo hay un paso a paso desde cero que reutiliza las fases que no cambiaron y enseña, con calma, la única parte que de verdad cambió.

El bot de la Fase 0 era un Chromium invisible manejado por Playwright. Funcionaba, pero era un mensajero carísimo — un navegador entero solo para llevar unos recados. Entonces caímos en la cuenta: la "presencia" de IMVU en la sala es solo un protocolo — un WebSocket (el IMQ) más algunas llamadas REST. Se puede hablar ese protocolo directo, sin ningún navegador.

Eso hicimos: ingeniería inversa del protocolo — observando los mensajes que un navegador real intercambia con IMVU, partiendo de un build viejo encontrado en GitHub y puliéndolo para nuestro uso — y escribimos un cliente sin navegador ("browserless") que inicia sesión, entra a la sala, lee el chat, se sienta, habla y revisa los participantes hablando IMQ a pelo. Misma cuenta, misma sala, mismos comandos — cero Chromium.

Por qué lo cambia todo — adiós, dolor de RAM

Solo el Chromium se comía ~500 MB (y tenía fugas hasta quedarse sin memoria a las ~19 h de funcionamiento). El cliente en Node puro se queda en ~50 MB. Y de paso desaparecen TODOS los parches que solo existían por culpa del navegador: el swap obligatorio, el "olvídate de 3D/WebGL", las flags anti-throttle "estructurales", el watchdog de página congelada y el "breather" que reiniciaba solo para limpiar memoria. Sin navegador, nada de eso necesita existir.

La parte que te importa A TI: el tamaño de la máquina dejó de mandar

Con el bot tan liviano, esa angustia de "1 GB es poco" se evapora — y vale decirlo fuerte: quizás ni siquiera necesites una VM. Un PC de casa decente (un Windows 11 con 16 GB de RAM, digamos) ejecuta el bot, el stream y todo lo demás localmente, de sobra. La nube gratis servía en realidad para tener una máquina siempre encendida y una dirección pública; si tu casa ya resuelve eso, todo el esqueleto de arriba — Oracle, swap, systemd — pasa a ser opcional. Usa la nube si te resuelve un problema, no por obligación. Las fases de arriba son el camino más barato, no el único.

Y lo mejor: el cliente sin navegador reaprovechó casi todo. La radio/rocola (server.js), el puente de YouTube, la capa de IA y las defensas de la sala nunca dependieron de Chromium — solo la mitad "navegador" del bot se volvió "protocolo directo". Las demás fases siguen valiendo palabra por palabra. Y fue esa ligereza la que abrió espacio para crecer después (poner música en varias salas al mismo tiempo, por ejemplo, sin que la máquina sude).

El camino actual, desde cero

No necesitas montar el Chromium para llegar aquí. Si estás empezando ahora y solo quieres lo que funciona hoy, sigue este recorrido: los pasos que no cambiaron te remiten a las fases de arriba (es literalmente lo mismo), y el paso del medio — el corazón nuevo — viene explicado con calma.

  1. Elige la casa de la máquina. Una VM siempre encendida (Oracle Always Free) o un PC de casa que quede encendido — ahora da igual, el bot es liviano. Solo necesita Node.js 18+.
    Igual que la Fase 4 si vas por la nube — y, como el bot adelgazó, aquí ya puedes saltarte el swap.
  2. Levanta la radio y el puente de YouTube. La rocola (server.js) que sirve el MP3, más el resolver en tu PC que descarga las canciones.
    Igual que las Fases 1 y 3 — palabra por palabra. El stream nunca dependió del navegador, así que aquí no cambia nada.
  3. Hazlo público y seguro. Dominio gratis en DuckDNS y Caddy encargándose del HTTPS delante del stream.
    Igual que la Fase 5. (En un PC de casa, esto es todo lo que tienes que resolver: una forma de tener dirección fija y TLS — un túnel o un dominio dinámico ya sirven.)
  4. El corazón nuevo: el cliente IMQ, sin navegador. Aquí está la única pieza que cambia de verdad. En lugar del Chromium leyendo la página, un cliente en Node habla el protocolo de IMVU directo. Son cuatro movimientos:
    • Iniciar sesión (REST). Mandas las credenciales de la cuenta-bot al endpoint de login de IMVU y guardas la sesión que vuelve (las cookies/el "sauce"). Es exactamente el login que hace el sitio por debajo — solo que a mano. La contraseña queda en el entorno de la máquina, nunca en el código.
    • Abrir el IMQ (WebSocket). Con la sesión en mano, abres un WebSocket a la message queue de IMVU y te suscribes a la cola de tu sala. Ese es el caño que entrega los eventos: mensajes del chat, quién entró/salió, los asientos.
    • Escuchar y reaccionar. Cada evento que llega es un JSON simple. Miras el tipo ("mensaje de chat", "entró un participante"...) y disparas la misma lógica de comandos que el bot ya tenía — !play, !skip y compañía no cambian en nada.
    • Hablar y sentarse. Para responder en el chat o sentarse en un lugar, mandas un evento de vuelta por la misma cola — lo inverso de lo que escuchas.
    En la práctica esto se vuelve un solo archivo — imq-client.js — que envuelve esa capa; el resto de bot.js ni se entera de que el navegador desapareció.
    Dónde encontrar los mensajes

    El IMQ no está documentado. Descubres el formato de los eventos observando un login real (las herramientas de red del navegador, pestaña WebSocket) o partiendo de un build de referencia — eso hicimos, encontrando uno viejo en GitHub y puliéndolo. Reserva tiempo para esta parte; es el único tramo "detectivesco" del proyecto.

  5. Regístralo en systemd — ahora mucho más liviano. Mismo esquema que la Fase 4 (dos servicios que arrancan con el sistema y se reinician solos), pero borras todo lo que era muleta del navegador en las Fases 4 y 7: sin swap obligatorio, sin "olvídate de 3D/WebGL", sin flags anti-throttle, sin watchdog de página congelada, sin el reinicio "breather" para limpiar memoria. Todo desaparece — ya no necesita existir.
    Las demás defensas de la Fase 7 — heartbeat, self-heal, detección de kick — siguen valiendo.
  6. Apunta la sala a la radio. Pega la URL del stream en el campo de música de la sala y listo: el bot está al aire.
    Igual que la Fase 6. Y si quieres darle alma al bot, la Fase 8 (IA) también entra sin cambiar nada.
Cuidado · el precio de la ingeniería inversa

Hablar el protocolo directo es liviano y rápido, pero es un protocolo no oficial: si IMVU cambia los mensajes del IMQ, tu cliente se rompe y lo arreglas a mano (un navegador de verdad se adaptaría solo). Es un intercambio consciente — mucho más liviano, un poquito más frágil. Aquí valió muchísimo la pena.

// 05

Trampas, todas en un solo lugar

Si lees una sola sección, que sea esta. Cada línea costó horas de depuración.

// 06

Mapa de archivos

A grandes rasgos, quién hace qué. Los nombres son los que usamos — los tuyos pueden variar.

bot.jsEl bot: entra a la sala, lee el chat por el WebSocket, dispara comandos, saludos, IA y las defensas (heartbeat, self-heal, watchdog).
server.jsEl stream/rocola: transmite el MP3, maneja la cola y la carpeta de canciones, expone las rutas internas.
ai.jsLa capa de IA: arma el prompt (persona + contexto de la conversación) y llama al modelo.
resolver.js / bridge.jsEl puente de YouTube: yt-dlp resuelve/descarga en el PC; la VM llega por túnel.
config.jsAjustes en un solo lugar: URL de la sala, la palabra que "llama" a la IA, tiempos, puertos.
login.jsSe ejecuta una vez para generar la sesión guardada (auth.json) de la cuenta del bot.
messages/ · scene/Textos editables (saludos, frases) y el "conocimiento" de la sala (tema, muebles) que usa la IA.
Playlist de filler da Sofia carregando…