Construyendo un Gestor de Discord Rich Presence con Ink TUI

La semana pasada quería tener control total sobre el rich presence de mi perfil de Discord. El default solo muestra lo que Discord detecta como tu “juego actual” — yo quería estado, detalles, botones, timestamps y rotación automática de perfiles.

Así que construí discord-rpc-tui: un gestor de Discord Rich Presence usando Ink (React para terminal) que corre como servicio systemd.


Arquitectura

Ink TUI (React terminal)     ← o modo headless via systemd

  RPCManager                  ← @xhayper/discord-rpc con reconexión automática

  Socket IPC de Discord       ← /run/user/1000/discord-ipc-0

La app tiene dos modos:

  • Modo TUI — interfaz Ink interactiva con barra de estado, lista de perfiles y log de eventos. Para ver qué está pasando.
  • Modo headless — corre como daemon via systemd, logs a journalctl. Arranca automáticamente al iniciar sesión.
[5:19:58 PM] ○ Connecting...
[5:19:58 PM] Connected to Discord
[5:19:58 PM] Activity set: Coding

 Profile: Coding
 State: Building something cool
 Details: TypeScript | Ink TUI

 [●] q:quit Space:pause n:next r:reload

Características Clave

Rotación de Actividades

Múltiples perfiles que rotan automáticamente. Cada perfil tiene su propio estado, detalles, tipo de actividad, botones e imágenes:

{
  "profiles": [
    {
      "name": "Coding",
      "activity": {
        "state": "Building something cool",
        "details": "TypeScript | Ink TUI",
        "startTimestamp": true,
        "type": 0
      }
    },
    {
      "name": "Gaming",
      "activity": {
        "state": "Exploring Hyrule",
        "details": "Zelda: Tears of the Kingdom",
        "largeImageKey": "zelda",
        "buttons": [{ "label": "Ver Stream", "url": "https://twitch.tv/..." }]
      }
    }
  ],
  "rotationInterval": 600
}

Reconexión Automática con Backoff Exponencial

Si Discord se reinicia o el socket IPC se desconecta, RPCManager reintenta automáticamente: 1s → 2s → 4s → 8s → 16s → máx 30s

Detección de Discord

Un DiscordDetector monitorea el socket IPC en /run/user/1000/discord-ipc-0 cada 5 segundos. Cuando Discord se cierra, pausa el RPC. Cuando Discord se abre, reconecta automáticamente.

Título de Actividad

Cada perfil ahora soporta un campo name que aparece como título de la actividad en Discord — ej., “Playing easy-rag” o “Listening to My Playlist”. Control total sobre cómo se etiqueta tu presencia:

{
  "name": "Coding",
  "activity": {
    "name": "easy-rag",
    "state": "Building something cool",
    "details": "TypeScript • Ink TUI"
  }
}

El Bug Que Me Enseñó Algo

El bug más escurridizo fue descubrir que el método connect() de @xhayper/discord-rpc no emite el evento ready. Hay que llamar a login() — que internamente llama a connect() y luego emite ready cuando no se necesitan scopes OAuth:

❌ this.client.on('ready', handler);
   await this.client.connect();  // ready NUNCA se dispara

✅ this.client.on('ready', handler);
   await this.client.login();    // ready se dispara!

El método login() verifica si se requieren scopes OAuth. Para SET_ACTIVITY, no se necesita autenticación — solo un Client ID. Aunque el nombre login() suene a autenticación, es la forma correcta de establecer una sesión RPC completa incluso sin auth.


Stack Tecnológico

ComponenteElecciónPor qué
TUI FrameworkInk 7React para terminal, Flexbox, input de teclado first-class
RPC Library@xhayper/discord-rpcMantenimiento activo, TypeScript, transporte IPC + WebSocket
ConfigZod + JSONDependencia compartida con AI SDK, inferencia de tipos
RuntimeNode.js 22ESM nativo, integración systemd
TestsVitest — 37 tests en 4 módulos core3 nuevos archivos (activity-rotator, discord-detector, rpc-manager)
Auto-startsystemd —userServicio de usuario Linux estándar, restart on failure

Probarlo

git clone https://github.com/fxckcode/discord-rpc-tui.git
cd discord-rpc-tui
pnpm install
bash install.sh
# Editar ~/.config/discord-rpc-tui/config.json con tu Client ID de Discord

El script de instalación hace todo: compila el binario, crea la configuración, instala el servicio systemd, y opcionalmente habilita el auto-start.

El proyecto tiene licencia MIT y está abierto a contribuciones en GitHub.

Compartir
D
Diego Duran
@fxckcode

Ingeniero backend que construye herramientas CLI agentivas. NestJS, Go, Django, AWS.