Herramienta que escucha partidas de rol en tiempo real a través de Discord, transcribe automáticamente quién dice qué, y genera un resumen narrativo vivo de la sesión usando inteligencia artificial.
RPG Scribe distingue entre diálogo in-game (lo que dicen los personajes) y meta-rol (conversaciones de los jugadores sobre reglas, estrategia, etc.), soporta múltiples campañas con diferentes sistemas de juego, y mantiene un resumen acumulado por campaña.
- Transcripción en tiempo real — Captura audio de Discord con separación automática por usuario (sin necesidad de diarización externa)
- Resumen narrativo con IA — Claude genera resúmenes incrementales cada ~2 minutos, distinguiendo in-game vs meta-rol
- Historial de resúmenes de campaña — Resumen de sesión (detallado) + resúmenes de campaña acumulativos que se preservan en historial; generación automática al cerrar sesión o bajo demanda
- Dashboard web — Interfaz FastAPI con WebSocket para ver transcripciones y resúmenes en tiempo real; modo Browse para consultar sesiones y campañas históricas
- Integración Discord — Comandos slash (
/scribe start/stop/status) y publicación de resúmenes como embeds - Multi-campaña — Configuración TOML por campaña con jugadores, personajes, PNJs, localizaciones y sistema de juego
- Grafo de relaciones — Visualización de relaciones entre personajes dentro de cada campaña
- Resiliencia — Retry con backoff exponencial, circuit breaker y reconexión automática
Listener (Discord/File) → Transcriber (OpenAI/Whisper) → Summarizer (Claude)
│ │ │
└──────────── Event Bus (async pub/sub) ──────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
Database Web UI Discord Bot
(SQLite) (FastAPI) (Publisher)
| Componente | Descripción |
|---|---|
| Listener | Captura audio de Discord (por usuario via SSRC) con VAD para chunking inteligente |
| Transcriber | Envía chunks de audio a OpenAI API (gpt-4o-transcribe). Fallback local con faster-whisper |
| Summarizer | Usa Claude Sonnet para generar resúmenes narrativos incrementales |
| Event Bus | Bus de eventos async que desacopla todos los componentes |
| Web UI | Dashboard FastAPI con WebSocket para visualización en tiempo real |
| Discord Bot | Comandos slash y publicación de resúmenes en canales de texto |
| Database | SQLite async para persistencia de campañas, sesiones y transcripciones |
- Python 3.11 o superior
- Cuenta de Discord con un bot configurado (ver Configuración del Bot)
- API key de OpenAI (para transcripción)
- API key de Anthropic (para resúmenes con Claude)
- (Opcional) GPU con CUDA para transcripción local con faster-whisper
git clone https://github.com/Zartch/Rpg-Scriber.git
cd Rpg-Scriberpython -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\Activate.ps1 # Windows# Instalación estándar (transcripción vía API)
pip install -e .
# Con dependencias de desarrollo (tests, linter)
pip install -e ".[dev]"
# Con transcripción local (faster-whisper, requiere GPU)
pip install -e ".[local]"RPG Scribe necesita 3 API keys para funcionar. Crear un archivo .env o exportar las variables:
export DISCORD_BOT_TOKEN="tu_token_de_discord"
export OPENAI_API_KEY="tu_api_key_de_openai"
export ANTHROPIC_API_KEY="tu_api_key_de_anthropic"
# Opcionales
export RPG_SCRIBE_HOST="127.0.0.1" # Host del dashboard web
export RPG_SCRIBE_PORT="8000" # Puerto del dashboard web
export RPG_SCRIBE_DB="rpg_scribe.db" # Ruta de la base de datos
export RPG_SCRIBE_WEB_TRANSCRIPTIONS_MAX_ITEMS="5000" # Max transcripciones en memoria para la vista live
export RPG_SCRIBE_WEB_FEED_MAX_ITEMS="1000" # Max filas visibles en Live Transcription (DOM)
export DISCORD_SUMMARY_CHANNEL_ID="" # Canal para publicar resúmenesCómo obtener cada key:
-
DISCORD_BOT_TOKEN— Ir a Discord Developer Portal, crear una aplicación, ir a la sección Bot y copiar el token. Ver la sección Configuración del Bot de Discord para los permisos necesarios. -
OPENAI_API_KEY— Crear una cuenta en platform.openai.com, ir a API Keys en el menú lateral y generar una nueva key. Se necesita un método de pago configurado (la transcripción usagpt-4o-transcribe). -
ANTHROPIC_API_KEY— Crear una cuenta en console.anthropic.com, ir a API Keys y generar una nueva key. Se necesita un método de pago configurado (los resúmenes usan Claude Sonnet).
Copiar y editar el archivo de ejemplo:
cp config/campaigns/example.toml config/campaigns/mi-campana.tomlEl archivo TOML define la campaña, jugadores, personajes y PNJs:
[campaign]
id = "mi-campana-2025"
name = "El Aquelarre de las Sombras"
game_system = "Akelarre"
language = "es"
description = "Castilla, 1342. Un grupo de viajeros investiga sucesos oscuros."
[campaign.dm]
discord_id = "123456789"
discord_name = "Carlos"
[[campaign.players]]
discord_id = "234567890"
discord_name = "Ana"
character_name = "María de Tordesillas"
character_description = "Curandera castellana, 28 años."
[[campaign.npcs]]
name = "Don Alfonso"
description = "Alcalde de Tordesillas. Nervioso y con secretos."Windows (recomendado): usar el launcher local del repo. Ejecuta siempre el .venv
de esta carpeta y el código de este proyecto, sin depender del PATH global ni de
activar el entorno manualmente.
.\rpg.cmd --campaign config/campaigns/mi-campana.tomlPara arrancar solo la Web UI:
.\rpg.cmd --web-onlySi prefieres el flujo clásico, también puedes activar .venv y luego usar
rpg-scribe, pero el launcher local evita confusiones cuando tienes varias copias
del proyecto.
rpg-scribe --campaign config/campaigns/mi-campana.tomlO directamente con Python:
python -m rpg_scribe --campaign config/campaigns/mi-campana.tomlrpg-scribe [opciones]
--campaign, -c PATH Ruta al archivo TOML de campaña
--host HOST Host del Web UI (default: 127.0.0.1)
--port PORT Puerto del Web UI (default: 8000)
--log-level LEVEL Nivel de log: DEBUG, INFO, WARNING, ERROR (default: INFO)
--json-logs Activar salida de logs en formato JSON
--web-only Arranca solo Web UI + API (sin listener/transcriber/bot)
Una vez que el bot está conectado, usar estos comandos slash en Discord:
| Comando | Descripción |
|---|---|
/scribe start |
Inicia la grabación en el canal de voz actual |
/scribe stop |
Detiene la grabación y finaliza la sesión |
/scribe status |
Muestra el estado actual de la grabación |
Al iniciar RPG Scribe, el dashboard web estará disponible en http://127.0.0.1:8000 (por defecto). Funcionalidades:
- Estado de los componentes del sistema
- Transcripciones en vivo con feed configurable
- Resumen de sesión actualizado incrementalmente
- Resumen de campaña con botón Generate (genera bajo demanda; también rellena resúmenes de sesión faltantes) y enlace View all al historial completo (
/campaign-summaries.html) - Modo Browse: navegar el historial de cualquier campaña y sesión sin necesidad de tener una sesión activa
- Gestión inline de jugadores, NPCs, localizaciones y relaciones entre personajes
RPG_SCRIBE_WEB_TRANSCRIPTIONS_MAX_ITEMSlimita cuantas transcripciones recientes mantiene el backend en memoria para la vista live (FIFO: se descartan las mas antiguas).RPG_SCRIBE_WEB_FEED_MAX_ITEMSlimita cuantas filas renderiza el navegador en "Live Transcription".
Referencia rapida de memoria (aproximada, depende del tama�o real de texto):
- 1.000 transcripciones cortas (~300-500 bytes cada una en memoria Python): ~0.3-0.5 MB
- 5.000 transcripciones: ~1.5-2.5 MB
- 20.000 transcripciones: ~6-10 MB
Para sesiones largas, un valor razonable suele ser 5000 en backend y 1000 en frontend.
- Ir a Discord Developer Portal
- Crear una nueva aplicación
- En la sección Bot, crear un bot y copiar el token
- Habilitar los siguientes Privileged Gateway Intents:
- Message Content Intent
- Server Members Intent (opcional)
- En OAuth2 > URL Generator, seleccionar los scopes:
botapplications.commands
- Seleccionar los permisos del bot:
- Connect (conectarse a canales de voz)
- Speak (necesario para la conexión de voz)
- Send Messages (para publicar resúmenes)
- Embed Links (para los embeds de resumen)
- Usar la URL generada para invitar el bot a tu servidor
El script auxiliar scripts/setup_discord_bot.py puede ayudar con la configuración inicial.
pytest
pytest -v # Verbose
pytest -k test_nombre # Test específicoruff check src/ tests/ # Verificar estilo
ruff format src/ tests/ # Formatear códigosrc/rpg_scribe/
├── main.py # CLI y orquestador (Application)
├── config.py # Carga de configuración
├── logging_config.py # Logging estructurado
├── core/ # Event bus, eventos, modelos, DB, resiliencia
├── listeners/ # Captura de audio (Discord, archivos)
├── transcribers/ # Speech-to-text (OpenAI API, faster-whisper)
├── summarizers/ # Resumen narrativo (Claude API)
├── discord_bot/ # Bot, comandos slash, publisher
└── web/ # FastAPI, WebSocket, frontend estático
| Capa | Tecnología |
|---|---|
| Lenguaje | Python 3.11+ |
| Bot Discord | discord.py + discord-ext-voice-recv |
| Detección de voz | webrtcvad |
| Transcripción (API) | OpenAI API (gpt-4o-transcribe) |
| Transcripción (local) | faster-whisper |
| Resumen | Anthropic API (Claude Sonnet) |
| Web | FastAPI + WebSocket + uvicorn |
| Base de datos | SQLite (aiosqlite) |
| Logging | structlog |
| Testing | pytest + pytest-asyncio |
| Linter | ruff |
Para una sesión de 4-6 horas con 4-5 jugadores:
| Servicio | Coste estimado |
|---|---|
| Transcripción (OpenAI) | ~$5-15 |
| Resumen (Claude Sonnet) | ~$2-5 |
| Total | ~$10-20/sesión |
- El audio se procesa y descarta inmediatamente. Solo se almacenan las transcripciones en texto.
- La base de datos SQLite es local.
- Las API keys no se almacenan en el código (usar variables de entorno).
Nunca commitear las keys al repositorio. Usar un archivo .env y asegurarse de que esté en .gitignore:
# Verificar que .env está ignorado
grep -q ".env" .gitignore || echo ".env" >> .gitignoreRestringir permisos del archivo .env:
Linux/macOS:
chmod 600 .envWindows (PowerShell):
icacls .env /inheritance:r /grant:r "$($env:USERNAME):(R)"La base de datos SQLite contiene transcripciones de las sesiones (conversaciones de los jugadores). Restringir acceso:
Linux/macOS:
chmod 600 rpg_scribe.dbWindows (PowerShell):
icacls rpg_scribe.db /inheritance:r /grant:r "$($env:USERNAME):(M)"Por defecto el dashboard escucha en 127.0.0.1 (solo local). No exponer a 0.0.0.0 sin protección, ya que no tiene autenticación. Si necesitas acceso remoto durante desarrollo, limitar por IP con firewall:
Linux (ufw):
# Permitir solo una IP específica al puerto 8000
sudo ufw allow from 192.168.1.100 to any port 8000Windows (PowerShell como admin):
# Permitir solo una IP específica al puerto 8000
New-NetFirewallRule -DisplayName "RPG Scribe" -Direction Inbound `
-LocalPort 8000 -Protocol TCP -RemoteAddress 192.168.1.100 -Action AllowInvitar el bot solo con los permisos listados en Configuración del Bot de Discord. No marcar "Administrator". El bot solo necesita: Connect, Speak, Send Messages y Embed Links.
Si se expone el dashboard fuera de localhost, colocarlo detrás de un reverse proxy con TLS:
Linux (nginx):
server {
listen 443 ssl;
server_name rpg-scribe.tu-dominio.com;
ssl_certificate /etc/letsencrypt/live/rpg-scribe.tu-dominio.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rpg-scribe.tu-dominio.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}Windows: usar IIS como reverse proxy con un certificado, o Caddy (caddy reverse-proxy --from rpg-scribe.tu-dominio.com --to localhost:8000) que gestiona TLS automáticamente.
Rotar las keys periódicamente desde los respectivos paneles de cada proveedor. Al rotar, actualizar .env y reiniciar RPG Scribe. Si una key se compromete, revocarla inmediatamente desde:
- Discord: Developer Portal > Bot > Reset Token
- OpenAI: platform.openai.com > API Keys > revocar
- Anthropic: console.anthropic.com > API Keys > revocar
Configurar límites de uso mensual para evitar costes inesperados:
- OpenAI: Settings > Limits > establecer un hard limit mensual
- Anthropic: Settings > Plans and billing > configurar spending limit
Programar backups periódicos de la base de datos SQLite:
Linux (cron, backup diario):
# Añadir a crontab -e
0 3 * * * cp /ruta/a/rpg_scribe.db /ruta/a/backups/rpg_scribe_$(date +\%Y\%m\%d).dbWindows (Task Scheduler, PowerShell):
# Crear script backup_db.ps1
Copy-Item "C:\ruta\a\rpg_scribe.db" "C:\ruta\a\backups\rpg_scribe_$(Get-Date -Format yyyyMMdd).db"
# Programarlo con: schtasks /create /tn "RPG Scribe Backup" /tr "powershell C:\ruta\backup_db.ps1" /sc daily /st 03:00Evitar ejecutar como root/administrador. Crear un usuario sin privilegios:
Linux (systemd):
# /etc/systemd/system/rpg-scribe.service
[Unit]
Description=RPG Scribe
After=network.target
[Service]
Type=simple
User=rpg-scribe
WorkingDirectory=/opt/rpg-scribe
EnvironmentFile=/opt/rpg-scribe/.env
ExecStart=/opt/rpg-scribe/.venv/bin/rpg-scribe --campaign config/campaigns/mi-campana.toml
Restart=on-failure
[Install]
WantedBy=multi-user.targetsudo useradd -r -s /usr/sbin/nologin rpg-scribe
sudo systemctl enable --now rpg-scribeWindows (NSSM):
# Instalar NSSM (https://nssm.cc) y registrar como servicio
nssm install RPGScribe "C:\ruta\.venv\Scripts\rpg-scribe.exe"
nssm set RPGScribe AppParameters "--campaign config\campaigns\mi-campana.toml"
nssm set RPGScribe AppDirectory "C:\ruta\Rpg-Scriber"
nssm set RPGScribe ObjectName ".\rpg-scribe-user"
nssm start RPGScribeEste proyecto es software privado. Todos los derechos reservados.