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.
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.
Recommended channel structure
| Channel | Purpose | Automatable? |
|---|---|---|
| #announcements | Official guild/server announcements | Partial (webhook from server events) |
| #boss-alerts | Automatic boss spawn warning | Yes |
| #castle-siege | Attack notification and war status | Yes |
| #ranking | Reset/level ranking updated periodically | Yes |
| #recruitment | New member applications | Manual, with a form |
| #general | Free conversation | Not 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
- Go to the Discord Developer Portal and create a new application.
- On the Bot tab, generate the token and store it as an environment variable, never in the code.
- 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:
| Command | Function |
|---|---|
/personagem <name> | Shows the character's level, reset, class |
/vincular <name> | Links the Discord account to the in-game character |
/proximoboss | Shows the next scheduled boss |
/eventohoje | Lists the day's scheduled events |
/status | Shows whether the server is online and player count |
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Roles don't sync | Discord-character link missing or API is down | Check the link table and API availability |
| Bot removes the role from a member still in the guild | Guild name case-sensitivity mismatch in the query | Normalize name comparison (case-insensitive) |
| Boss alert arrives late or doesn't arrive | Bot's cron isn't running or the process crashed | Add monitoring/auto-restart for the bot process |
| Ranking is outdated | Update interval configured too long | Reduce the interval or trigger an update by event |
| Bot has write access to the database by mistake | Database user configured without permission restrictions | Create a dedicated read-only database user for the bot |
| Bot token leaked/compromised | Token committed to the repo or shared | Revoke 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.