Cómo crear un bot de Discord con comandos personalizados para tu servidor de MU Online
Construye un bot de Discord en Node.js para tu servidor de MU Online, con comandos de ranking, estado del servidor, consulta de personaje y vinculación de cuenta, consultando la base MS SQL del MuServer en tiempo real.
Un bot de Discord bien construido es hoy uno de los pilares de retención de cualquier servidor de MU Online: es el primer contacto del jugador con la comunidad, el canal donde aparecen rankings, eventos y estado del servidor en tiempo real, y el puente entre el juego y el soporte. A diferencia de lo
Un bot de Discord bien construido es hoy uno de los pilares de retención de cualquier servidor de MU Online: es el primer contacto del jugador con la comunidad, el canal donde aparecen rankings, eventos y estado del servidor en tiempo real, y el puente entre el juego y el soporte. A diferencia de los bots genéricos "listos para usar", un bot personalizado consulta directamente la base de datos de tu MuServer, permitiendo comandos como /ranking, /personaje y /status con datos reales, actualizados cada segundo. Este tutorial arma un bot en Node.js con discord.js, desde cero hasta el despliegue, incluyendo la conexión segura al MS SQL Server y los comandos más pedidos por la comunidad hispanohablante de MU.
Por qué un bot propio en vez de bots genéricos
Los bots de propósito general (moderación, música, memes) no saben nada sobre tu servidor de MU. Un bot propio lee directamente las tablas del MuServer —Character, AccountCharacter, MEMB_STAT— y transforma esos datos en comandos útiles dentro de Discord, sin que el jugador tenga que abrir el sitio web o iniciar sesión en el juego para saber su posición en el ranking. Esto también abre espacio para automatizaciones exclusivas: anuncio automático de un boss que apareció, alerta de caída del servidor y notificación de eventos, todo dentro del canal donde la comunidad ya está.
Requisitos previos
- Node.js 18 o superior instalado en la máquina que va a correr el bot.
- Una aplicación registrada en el Discord Developer Portal, con el token del bot generado.
- Acceso de red al MS SQL Server de tu MuServer (host, puerto, usuario y contraseña con permiso de lectura).
- Permiso para añadir bots al servidor de Discord de la comunidad (rol de administrador o dueño del servidor).
- Un editor de código (VS Code es el más común) y git instalado, opcional pero recomendado.
Estructura del proyecto
Organiza el bot en carpetas separadas por responsabilidad, facilitando el mantenimiento a medida que los comandos crecen:
mu-discord-bot/
├── index.js
├── deploy-commands.js
├── config.json
├── commands/
│ ├── ranking.js
│ ├── status.js
│ ├── personagem.js
│ └── vincular.js
├── db/
│ └── connection.js
└── package.json
Paso 1 — Crear la aplicación y el token del bot
En el Discord Developer Portal, crea una nueva aplicación, ve a "Bot" y haz clic en "Add Bot". Copia el token generado: nunca debe subirse a git ni compartirse. En "OAuth2 > URL Generator", marca los alcances bot y applications.commands, selecciona los permisos Send Messages, Use Slash Commands y Embed Links, y usa la URL generada para invitar al bot a tu servidor de Discord.
Paso 2 — Instalar dependencias y configurar la base
mkdir mu-discord-bot && cd mu-discord-bot
npm init -y
npm install discord.js mssql dotenv
Crea un archivo .env (nunca versionado) con las credenciales:
DISCORD_TOKEN=tu_token_aqui
CLIENT_ID=id_de_la_aplicacion
GUILD_ID=id_del_servidor_discord
DB_HOST=127.0.0.1
DB_USER=mu_bot_readonly
DB_PASS=contrasena_fuerte_aqui
DB_NAME=MuOnline
Paso 3 — Conectar a la base MS SQL con una cuenta restringida
Crea la cuenta de base de datos mu_bot_readonly con permiso solo de SELECT en las tablas Character, AccountCharacter y MEMB_STAT. Esa es la diferencia entre que la filtración de un token se convierta en un incidente de seguridad grave o solo sea una exposición de datos públicos de ranking.
// db/connection.js
const sql = require('mssql');
const config = {
user: process.env.DB_USER,
password: process.env.DB_PASS,
server: process.env.DB_HOST,
database: process.env.DB_NAME,
options: { encrypt: false, trustServerCertificate: true }
};
async function getPool() {
return sql.connect(config);
}
module.exports = { getPool, sql };
Paso 4 — Comando de ranking
// commands/ranking.js
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
const { getPool, sql } = require('../db/connection');
module.exports = {
data: new SlashCommandBuilder()
.setName('ranking')
.setDescription('Mostra o top 10 de resets do servidor'),
async execute(interaction) {
await interaction.deferReply();
const pool = await getPool();
const result = await pool.request().query(`
SELECT TOP 10 Name, cLevel, Resets
FROM Character
ORDER BY Resets DESC, cLevel DESC
`);
const embed = new EmbedBuilder()
.setTitle('🏆 Top 10 Resets')
.setColor(0xffcc00)
.setDescription(
result.recordset
.map((r, i) => `**${i + 1}.** ${r.Name} — Lvl ${r.cLevel} — ${r.Resets} resets`)
.join('\n')
);
await interaction.editReply({ embeds: [embed] });
}
};
Paso 5 — Comando de consulta de personaje
El comando /personaje <nombre> debe buscar solo datos públicos (nivel, clase, resets, guild), nunca información sensible de la cuenta como correo o contraseña. Valida el parámetro de entrada con sql.VarChar para evitar inyección SQL: nunca concatenes strings directamente en la consulta.
// commands/personagem.js (fragmento de la consulta segura)
const request = pool.request();
request.input('nome', sql.VarChar, nomePersonagem);
const result = await request.query(
'SELECT Name, cLevel, Class, Resets FROM Character WHERE Name = @nome'
);
Paso 6 — Comando de estado del servidor
Un comando /status que verifica si el GameServer y el ConnectServer están activos es uno de los más usados por la comunidad, ya que evita que el jugador intente iniciar sesión en un servidor caído sin saberlo. La comprobación más simple es abrir un socket TCP en el puerto del ConnectServer (generalmente 44405) con un tiempo de espera corto y reportar éxito o falla.
Paso 7 — Vinculación de cuenta Discord ↔ juego
| Etapa | Dónde ocurre | Descripción |
|---|---|---|
| Generar código | Panel web o comando in-game | El sistema genera un código de 6 dígitos válido por 10 minutos |
| Informar código | Discord (DM al bot) | El jugador envía /vincular 123456 |
| Validar y guardar | Bot + base de datos propia del bot | El bot verifica el código y guarda el vínculo discord_id ↔ account_id |
| Usar el vínculo | Comandos futuros | /mipersonaje ya sabe qué cuenta buscar sin que el jugador escriba el nombre |
Nunca implementes la vinculación pidiendo la contraseña de la cuenta por Discord: eso normaliza un vector clásico de phishing dentro de la propia comunidad.
Paso 8 — Registrar los comandos slash y desplegar el bot
// deploy-commands.js
const { REST, Routes } = require('discord.js');
const fs = require('fs');
require('dotenv').config();
const commands = fs.readdirSync('./commands')
.map(file => require(`./commands/${file}`).data.toJSON());
const rest = new REST().setToken(process.env.DISCORD_TOKEN);
(async () => {
await rest.put(
Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
{ body: commands }
);
console.log('Comandos registrados com sucesso.');
})();
Ejecuta node deploy-commands.js una vez para registrar los comandos, luego inicia el bot con node index.js. Para producción, usa un gestor de procesos como PM2 (pm2 start index.js --name mu-bot) para reiniciar automáticamente en caso de caída.
Paso 9 — Hospedaje y monitoreo continuo
Una VPS pequeña (1 vCPU, 1 GB RAM) ya corre un bot de tamaño medio sin problemas, siempre que la base de datos no esté en la misma instancia sobrecargada del GameServer. Configura logs en archivo (no solo en consola) y una alerta simple —aunque sea un webhook propio en Discord— para saber cuándo el bot cae, evitando descubrir la falla solo cuando un jugador se queje.
Errores comunes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| El bot no responde a comandos | deploy-commands.js no se ejecutó tras el cambio | Vuelve a ejecutar el deploy y espera la propagación (hasta 1h en comandos globales) |
| Error de conexión con la base de datos | Firewall bloqueando el puerto 1433 | Habilita el puerto solo para la IP de la máquina del bot |
| El comando de ranking es lento | Consulta sin índice en la columna Resets | Crea un índice en la columna usada en el ORDER BY |
| Token expuesto en GitHub | .env subido por error | Revoca el token de inmediato y genera uno nuevo |
| El bot se cae solo tras horas activo | Proceso sin gestor de reinicio | Usa PM2 o systemd con reinicio automático |
Lista de verificación de lanzamiento
- Aplicación y token creados en el Discord Developer Portal.
- Cuenta de base de datos restringida a SELECT creada y probada.
.envfuera del control de versiones (.gitignoreconfigurado).- Comandos de ranking, status y personaje probados en producción.
- Flujo de vinculación de cuenta validado sin exposición de contraseña.
- Proceso del bot corriendo bajo PM2 o equivalente con reinicio automático.
- Logs y alerta de caída configurados.
Con el bot en línea respondiendo comandos en tiempo real, el paso natural siguiente es integrarlo a los eventos automáticos de tu servidor —por ejemplo, anunciando la aparición de bosses o el inicio de invasiones directamente en Discord— cerrando el ciclo entre el servidor de MU Online y la comunidad que juega en él.
Preguntas frecuentes
¿Necesito saber programar para tener un bot así?
Es recomendable tener nociones básicas de JavaScript, pero este tutorial ofrece el código base completo. Copiando los archivos y ajustando la cadena de conexión de la base de datos, un administrador con conocimiento intermedio puede poner el bot en marcha sin escribir lógica desde cero.
¿El bot necesita correr en el mismo servidor del juego?
No. Incluso se recomienda ejecutarlo en una máquina separada, siempre que tenga acceso de red al MS SQL Server del MuServer (puerto 1433 habilitado) o a una réplica de solo lectura de la base de datos, lo cual es más seguro.
¿Es seguro darle al bot acceso directo a la base de datos del juego?
Solo con una cuenta de base de datos restringida a SELECT en las tablas necesarias (ranking, personajes, cuentas). Nunca uses la cuenta de administrador del SQL Server en el bot: si el token del bot se filtra, un acceso de lectura limitado reduce drásticamente el daño posible.
¿Cómo hago el comando de vincular cuenta (Discord ↔ juego) de forma segura?
Genera un código temporal de 6 dígitos dentro del panel web o mediante un comando in-game, y pide al jugador que escriba ese código en el bot por mensaje directo. El bot registra la vinculación en una tabla propia; nunca pidas la contraseña de la cuenta del juego por Discord.
¿Se puede alojar este bot gratis?
Para pruebas, sí, en una VPS pequeña o en un plan gratuito de hospedaje Node.js. Para producción, con cientos de usuarios simultáneos, se recomienda una VPS de pago con al menos 1 vCPU y 1 GB de RAM dedicados al bot, separada del GameServer.