Cómo automatizar el anuncio de eventos multiplataforma en tu servidor de MU Online
Configura un pipeline que lee los eventos del GameServer (Blood Castle, Devil Square, Chaos Castle, Invasiones) y publica el aviso automáticamente en Discord, Telegram y el sitio web, sin depender de un admin conectado.
Anunciar eventos como Blood Castle, Devil Square, Chaos Castle e Invasiones manualmente es una de las tareas más repetitivas — y más fáciles de olvidar — en la administración de un servidor de MU Online. Un admin que tiene que estar pendiente del reloj para publicar "¡Devil Square en 5 minutos!" en
Anunciar eventos como Blood Castle, Devil Square, Chaos Castle e Invasiones manualmente es una de las tareas más repetitivas — y más fáciles de olvidar — en la administración de un servidor de MU Online. Un admin que tiene que estar pendiente del reloj para publicar "¡Devil Square en 5 minutos!" en Discord, Telegram y el sitio pierde tiempo y, inevitablemente, falla en los horarios pico. Este tutorial muestra cómo armar un pipeline simple que lee el estado de los eventos directamente de la base de datos o de los logs del GameServer y publica el aviso automáticamente en los tres canales, con la infraestructura mínima. Vas a aprender la estructura del script, los webhooks de cada plataforma, el cálculo del "T-minus" antes del evento y cómo evitar mensajes duplicados.
Por qué automatizar el anuncio de eventos
Los eventos con cronograma fijo (Devil Square cada hora, Blood Castle en horarios específicos) y los eventos dinámicos (Invasión de Kundun, Red Dragon) tienen un patrón en común: el jugador solo participa si sabe que está pasando. Las comunidades que anuncian manualmente tienen una tasa de participación inconsistente — cae mucho de noche, de madrugada, o cuando el admin está ausente. Un pipeline automatizado garantiza consistencia 24/7, reduce la carga operativa del equipo y profesionaliza la percepción del servidor ante los jugadores.
Arquitectura de la solución
El diseño más simple y robusto tiene tres componentes: un colector (script que lee el estado de los eventos), un normalizador (transforma el dato crudo en un mensaje estándar) y publicadores (uno por plataforma: Discord, Telegram, sitio web). Ejecutar el colector cada 30-60 segundos vía cron es suficiente para la mayoría de los servidores — no hace falta que sea en tiempo real.
| Componente | Función | Tecnología sugerida |
|---|---|---|
| Colector | Lee la tabla de eventos de MySQL/MSSQL o parsea el log del GameServer | PHP, Python o Node.js |
| Normalizador | Convierte en {evento, mapa, horario, estado} | Función pura, sin I/O |
| Publicador Discord | Envía embed vía Webhook | curl o librería HTTP |
| Publicador Telegram | Envía mensaje vía Bot API | curl o librería HTTP |
| Publicador Web | Escribe en la tabla eventos_ativos leída por el sitio | Consulta SQL directa |
Identificando la fuente de datos del evento
Cada emulador guarda el estado del evento de una manera distinta. En IGCN, muchos eventos registran el horario en la tabla EVENT_LOG o en columnas de GameServerInfo; en MuEMU, es común que exista una tabla adyacente a MEMB_STAT o un archivo de log rutinario (Event.log) que registra el inicio/fin. Antes de escribir cualquier script, hacé una consulta exploratoria:
SELECT TOP 20 * FROM EVENT_LOG ORDER BY dtCreate DESC;
Si tu emulador no expone esto en una tabla, la alternativa es leer el archivo de log del GameServer con un tail -f equivalente, filtrando por palabras clave como BloodCastle, DevilSquare, ChaosCastle, Invasion.
Calculando el aviso anticipado (T-minus)
Anunciar solo cuando el evento ya empezó tiene un valor limitado — el jugador necesita tiempo para trasladarse y entrar. Lo ideal es calcular el horario programado y disparar avisos en T-5min y T-1min, además de la apertura del evento. Si tus eventos son cíclicos (por ejemplo, Devil Square cada 60 minutos, en punto :00), el cálculo es directo:
from datetime import datetime, timedelta
def proximo_ciclo(intervalo_minutos=60):
agora = datetime.now()
minuto_atual = agora.minute
proximo = agora.replace(second=0, microsecond=0) + timedelta(minutes=(intervalo_minutos - minuto_atual % intervalo_minutos))
return proximo
Para eventos dinámicos (Invasiones disparadas por un script del servidor), el T-minus no existe de antemano — en ese caso, el anuncio siempre es "evento iniciado ahora", disparado en el instante en que el colector detecta el cambio de estado.
Publicando en Discord vía Webhook
El Discord Webhook es la forma más simple de publicar sin mantener un bot corriendo. Creá el webhook en Configuración del canal → Integraciones → Webhooks y usá la URL generada:
curl -X POST "$DISCORD_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{
"embeds": [{
"title": "⚔️ ¡Devil Square empieza en 5 minutos!",
"description": "Nivel recomendado: 380+\nLugar: Devias 2\nEntrada: NPC Devil Messenger",
"color": 15158332
}]
}'
Usá embeds en vez de texto plano — quedan visualmente destacados y permiten color, título y descripción por separado, lo que ayuda al jugador a identificar rápidamente qué evento está por empezar.
Publicando en Telegram vía Bot API
Creá un bot con @BotFather, obtené el token y el chat_id del canal/grupo, y publicá con una llamada HTTP simple:
curl -X POST "https://api.telegram.org/bot$TELEGRAM_TOKEN/sendMessage" \
-d chat_id="$TELEGRAM_CHAT_ID" \
-d parse_mode="Markdown" \
-d text="*Chaos Castle* empieza en 5 minutos! Nivel recomendado: 400+."
A diferencia de Discord, Telegram no exige crear un "webhook de canal" — el bot solo necesita ser administrador del canal/grupo de destino para poder publicar.
Sincronizando con el sitio (banner de "evento en vivo")
Además de las redes sociales, conviene registrar el estado actual en una tabla simple leída por el front-end del sitio, para mostrar un banner "¡Devil Square en vivo ahora!" en la home:
INSERT INTO eventos_ativos (evento, mapa, inicio, fim)
VALUES ('Devil Square', 'Devias 2', NOW(), DATE_ADD(NOW(), INTERVAL 15 MINUTE));
El front-end consulta esa tabla vía API cada pocos segundos (o usa websocket, si la stack ya lo soporta) y muestra/oculta el banner a medida que el fim expira.
Evitando anuncios duplicados
El mayor riesgo de un script corriendo en un intervalo corto es publicar el mismo aviso varias veces. Resolvelo con una tabla o archivo de control que guarda el último "hash de evento" ya anunciado (por ejemplo, evento + horario_programado), y el colector solo publica si el hash es nuevo:
CREATE TABLE eventos_anunciados (
hash_evento VARCHAR(64) PRIMARY KEY,
anunciado_em DATETIME DEFAULT NOW()
);
Antes de publicar, intentá insertar el hash; si da error de clave duplicada, el script simplemente lo ignora y continúa — es una forma barata y confiable de idempotencia.
Programando la ejecución (cron)
En Linux, un cron cada minuto es suficiente:
* * * * * /usr/bin/php /var/www/scripts/anunciar_eventos.php >> /var/log/mu/anuncios.log 2>&1
En Windows, usá el Programador de Tareas con un disparador "repetir cada 1 minuto" que llame al script PHP/Python vía línea de comandos. Asegurate de que el log se rote (mirá el tutorial de limpieza de logs) para no acumular gigabytes de ejecuciones repetidas.
Manejando fallas de red y reintentos
Las llamadas a APIs externas fallan eventualmente — Discord y Telegram tienen límites de tasa e inestabilidades puntuales. Implementá un reintento simple con backoff exponencial (1s, 2s, 4s) y un límite de intentos (por ejemplo, 3), registrando la falla definitiva en un log sin trabar el resto del flujo:
| Intento | Espera antes | Acción si falla |
|---|---|---|
| 1 | 0s | Reintenta |
| 2 | 1s | Reintenta |
| 3 | 2s | Reintenta |
| 4 (final) | 4s | Registra en log de falla y continúa |
Personalizando el mensaje por tipo de evento
Cada evento tiene un público y una urgencia diferentes — conviene tener una plantilla por tipo, con nivel recomendado, lugar y recompensa esperada, para que el jugador decida rápidamente si vale la pena participar:
| Evento | Nivel recomendado | Recompensa típica |
|---|---|---|
| Blood Castle | Varía según el nivel de la BC | Box of Luck, Jewels |
| Devil Square | 380+ | Jewels, ítems raros |
| Chaos Castle | 400+ | Combinación de Chaos, PK reset |
| Invasión de Kundun | Todos | Drop de ítems de boss |
| Red Dragon Invasion | Todos | Boxes exclusivas de evento |
Errores comunes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| Anuncio duplicado en Discord | Falta de control de idempotencia | Implementá una tabla de hash de evento anunciado |
| No se publica ningún anuncio | La consulta apunta a la tabla/columna equivocada del emulador | Confirmá el schema real con una consulta exploratoria |
| El webhook devuelve 401/404 | URL del webhook vencida o canal eliminado | Generá un nuevo webhook y actualizá la variable de entorno |
| El mensaje llega tarde | Cron corriendo en un intervalo muy largo | Reducí el intervalo a 30-60s |
| El bot de Telegram no publica | El bot no es admin del canal | Agregá el bot como administrador del canal/grupo |
| El script traba el servidor | Llamada de red sin timeout | Definí un timeout de pocos segundos en toda llamada HTTP |
Lista de verificación de automatización de eventos
- Fuente de datos del evento identificada y probada (tabla SQL o log).
- Script colector corriendo vía cron/Programador en un intervalo de 30-60s.
- Webhook de Discord configurado y probado con un mensaje real.
- Bot de Telegram creado, agregado como admin y probado.
- Tabla de idempotencia (
eventos_anunciados) creada y funcionando. - Banner del sitio sincronizado con la tabla
eventos_ativos. - Reintentos con backoff implementados para fallas de red.
- Logs de ejecución con rotación configurada.
Con el anuncio de eventos corriendo solo, tu equipo libera tiempo para enfocarse en contenido y soporte en lugar de estar pendiente del reloj. Si todavía estás armando la base del servidor antes de automatizar este flujo, empezá por el tutorial de creación de servidor de MU Online.
Preguntas frecuentes
¿Necesito un bot propio o alcanza con webhooks?
Para la mayoría de los casos, los webhooks (Discord Webhook y Telegram Bot API) resuelven el problema sin necesidad de alojar un bot completo. Un bot dedicado solo vale la pena si querés comandos interactivos, como '/proximoevento', además del anuncio automático.
¿El anuncio puede avisar con anticipación (por ejemplo, 5 minutos antes)?
Sí, siempre que tu script lea el horario programado del evento (mediante una tabla de la base de datos o la configuración del GameServer) y no solo el evento en curso. El tutorial muestra cómo calcular el T-5min a partir del ciclo configurado.
¿Esto funciona con cualquier emulador (IGCN, MuEMU, X-Team)?
La lógica es la misma, pero el nombre de las tablas/columnas de evento cambia entre emuladores. Vas a necesitar identificar dónde tu emulador guarda el estado del evento (tabla SQL, archivo de log o memoria compartida) y ajustar la consulta.
¿Se puede anunciar en más de un servidor (idiomas diferentes) con el mismo script?
Sí. Basta con parametrizar el script por servidor/idioma y ejecutar una instancia (o un bucle) para cada configuración, apuntando al webhook correspondiente de cada comunidad.
¿Qué pasa si Discord o Telegram están caídos en el momento del evento?
Lo ideal es implementar reintentos con backoff y registro de fallas. Si la llamada falla, registrala en un archivo de log y volvé a intentar en pocos segundos — nunca dejes que el script se trabe por una falla de red.