Brazil's biggest MU Online portal — since 2003
Tutorial Intermediate Gameplay

How to integrate your MU Online guild with Discord and bots

Set up a MU Online guild Discord with its own bot: member and role syncing, event and boss alerts, reset ranking, and real-time castle siege attack notifications.

BR Bruno · Updated on May 8, 2025 · ⏱ 14 min read
Quick answer

Discord has become the communication hub of virtually every active MU Online guild, but most treat it as a generic chat, disconnected from what's actually happening in the game. A well-integrated Discord knows who's in the guild in real time, automatically warns about bosses and events, shows a rese

Discord has become the communication hub of virtually every active MU Online guild, but most treat it as a generic chat, disconnected from what's actually happening in the game. A well-integrated Discord knows who's in the guild in real time, automatically warns about bosses and events, shows a reset ranking of members, and notifies Castle Siege attacks without anyone having to type anything manually. This tutorial shows how to structure this Discord, choose between ready-made bots and a custom one, sync members and roles with the MuServer database, and set up the most valuable alerts for a competitive guild.

Why integrate (and not just use a generic Discord)

A Discord server without integration depends on someone remembering to announce the boss, manually updating who joined or left the guild, and checking the ranking separately on the site. That works for small guilds, but it doesn't scale. A basic integration with the game database solves the three biggest friction points: outdated presence, no automatic alerts, and manual ranking.

ChannelPurposeAutomatable?
#announcementsOfficial guild/server announcementsPartial (webhook from server events)
#boss-alertsAutomatic boss spawn warningYes
#castle-siegeAttack notification and war statusYes
#rankingReset/level ranking updated periodicallyYes
#recruitmentNew member applicationsManual, with a form
#generalFree conversationNot applicable

Step 1 — Choose between a ready-made bot and a custom one

Generic bots (MEE6, Carl-bot, Dyno) cover moderation, reaction roles, and welcome messages well, but have no access to your MuServer data. For guild presence, ranking, and boss alerts, you need your own bot (discord.js or discord.py) connected to an API that queries the game database with read permission.

Step 2 — Create the bot application on Discord

  1. Go to the Discord Developer Portal and create a new application.
  2. On the Bot tab, generate the token and store it as an environment variable, never in the code.
  3. Invite the bot to the server with the minimum permissions needed: manage roles, send messages, read message history.
# bot's .env
DISCORD_TOKEN=your-token-here
API_MU_BASE_URL=https://yourserver.com/api

Step 3 — Expose your own API over the MuServer database

Never connect the bot directly to the production game database. Create dedicated, read-only endpoints:

<?php
// api/guild_membros.php
header('Content-Type: application/json');
$guildName = $_GET['guild'] ?? '';
$membros = consultarMembrosGuild($guildName); // SELECT Name, cLevel, Resets FROM Character WHERE G_Name = ?
echo json_encode($membros);

Step 4 — Automatically sync members and roles

Run a periodic routine on the bot that compares the guild's in-game member list with who holds the corresponding role on Discord, applying and removing roles as needed:

async function sincronizarCargosGuild() {
  const membrosJogo = await fetch(`${API_BASE}/guild_membros.php?guild=Legends`).then(r => r.json());
  const guild = client.guilds.cache.get(GUILD_DISCORD_ID);
  const cargoGuild = guild.roles.cache.find(r => r.name === 'Legends');

  for (const membro of guild.members.cache.values()) {
    const estaNaGuildJogo = membrosJogo.some(m => m.discordId === membro.id);
    if (estaNaGuildJogo && !membro.roles.cache.has(cargoGuild.id)) {
      await membro.roles.add(cargoGuild);
    } else if (!estaNaGuildJogo && membro.roles.cache.has(cargoGuild.id)) {
      await membro.roles.remove(cargoGuild);
    }
  }
}

setInterval(sincronizarCargosGuild, 10 * 60 * 1000); // every 10 minutes

The link between the Discord account and the in-game character must already exist — typically via a link command (/vincular <charactername>) that records the relationship in its own table.

Step 5 — Automatic boss alerts

For bosses with a fixed schedule, a simple scheduler does the job:

const bossSchedule = [
  { nome: 'Kundun', horario: '20:00' },
  { nome: 'Selupan', horario: '21:30' },
];

cron.schedule('*/1 * * * *', () => {
  const agora = formatarHoraAtual();
  const bossAgora = bossSchedule.find(b => proximoDe(b.horario, agora, 5)); // 5 min before
  if (bossAgora) {
    canalBossAlertas.send(`⚔️ **${bossAgora.nome}** spawns in 5 minutes! Get ready.`);
  }
});

For bosses with a random spawn within a window, query the GameServer's active monster table (if your emulator exposes that information) or treat it as a window warning ("Kundun may spawn between 8pm and 8:30pm").

Step 6 — Castle Siege notification

Castle Siege is the game's most competitive event, and deserves a dedicated channel with notifications for registration opening, war start, and final result:

async function notificarCastleSiege(status) {
  const embed = new EmbedBuilder()
    .setTitle('🏰 Castle Siege')
    .setDescription(status.mensagem)
    .addFields({ name: 'Guild currently holding the castle', value: status.guildDona })
    .setColor(status.emGuerra ? 0xff0000 : 0x00ff00);
  canalCastleSiege.send({ embeds: [embed] });
}

Trigger this function from a script that monitors Castle Siege status in the game database or via a webhook fired by the GameServer itself, if the emulator supports it.

Step 7 — Reset ranking updated on Discord

Publish an automatic ranking of guild members, updated periodically, instead of relying on the site:

async function publicarRanking() {
  const membros = await fetch(`${API_BASE}/guild_membros.php?guild=Legends`).then(r => r.json());
  const ordenado = membros.sort((a, b) => b.resets - a.resets).slice(0, 10);
  const texto = ordenado.map((m, i) => `${i + 1}. **${m.nome}** — Reset ${m.resets}, Lv ${m.nivel}`).join('\n');
  canalRanking.send(`📊 **Guild Ranking — updated now**\n${texto}`);
}

Step 8 — Useful commands for members

Offer slash commands for self-service, reducing repetitive chat questions:

CommandFunction
/personagem <name>Shows the character's level, reset, class
/vincular <name>Links the Discord account to the in-game character
/proximobossShows the next scheduled boss
/eventohojeLists the day's scheduled events
/statusShows whether the server is online and player count

Common errors and fixes

SymptomLikely causeFix
Roles don't syncDiscord-character link missing or API is downCheck the link table and API availability
Bot removes the role from a member still in the guildGuild name case-sensitivity mismatch in the queryNormalize name comparison (case-insensitive)
Boss alert arrives late or doesn't arriveBot's cron isn't running or the process crashedAdd monitoring/auto-restart for the bot process
Ranking is outdatedUpdate interval configured too longReduce the interval or trigger an update by event
Bot has write access to the database by mistakeDatabase user configured without permission restrictionsCreate a dedicated read-only database user for the bot
Bot token leaked/compromisedToken committed to the repo or sharedRevoke and generate a new token, move it to an environment variable

Guild-Discord integration checklist

  • Channel structure defined (announcements, boss, castle siege, ranking, recruitment).
  • Custom bot created with the minimum permissions needed on the server.
  • Read-only API exposed over the MuServer database for the bot to consume.
  • Discord-to-character link command implemented.
  • Automatic role syncing running periodically.
  • Boss and Castle Siege alerts configured and tested.
  • Automatic ranking published in a dedicated channel.
  • Self-service commands (slash commands) available to members.

With the guild running automated on Discord, it's worth also reviewing the server's overall integration with the community — from the forum to the public status page. See the MU Online server setup guide to review the project's complete foundation.

Frequently asked questions

Do I need to know how to code to have a guild bot on Discord?

Not necessarily. There are configurable generic bots (MEE6, Carl-bot) that cover roles and basic moderation. But to sync real game data (level, reset, boss attendance) you'll need your own bot or a custom integration, even if simple.

Is it safe to give the bot access to the server's database?

Only with read-only access and through a restricted connection (ideally your own API, not a direct connection to the production database). Never give the bot write credentials to the game database — it should query data, not change it.

How does the bot know when a boss is about to spawn?

By querying the MuServer's monster spawn table or GameServer logs, or more simply, with a schedule of fixed times configured manually if spawns are predictable. Bosses with random spawns within a window require closer monitoring of the database/logs.

Is it worth having a separate bot for each guild, or one bot for the whole server?

Depends on community size. A single server-wide bot, with commands filtered by the player's guild, covers most cases well and is easier to maintain. Per-guild bots make sense in very large communities where guilds want full configuration autonomy.

Can the bot automatically kick someone who leaves the guild in-game?

Yes, if the bot periodically queries the guild membership table in the MuServer database and compares it against the Discord server's members, removing the role (or access) of anyone no longer in the guild in-game.

BR
Events, maps & items editor

Bruno specializes in MU Online events, maps, bosses and item economy. He documents every detail based on real gameplay.

Keep reading

Related articles