Como criar um bot de Discord com o status do servidor de MU
Monte um bot de Discord que exibe status online/offline, jogadores conectados e rankings do seu servidor de MU Online, com atualização automática e comandos slash.
Um bot de Discord que mostra o status do seu servidor de MU Online em tempo real é uma das ferramentas de comunidade com melhor retorno sobre esforço que você pode montar. Enquanto o jogador está no Discord conversando, ele vê num piscar de olhos se o servidor está no ar, quantas pessoas estão conec
Um bot de Discord que mostra o status do seu servidor de MU Online em tempo real é uma das ferramentas de comunidade com melhor retorno sobre esforço que você pode montar. Enquanto o jogador está no Discord conversando, ele vê num piscar de olhos se o servidor está no ar, quantas pessoas estão conectadas e quem lidera o ranking de resets — sem precisar abrir o cliente ou o site. Isso reduz drasticamente as perguntas repetidas de "tá on?" nos canais e projeta uma imagem de servidor cuidado e profissional. Neste tutorial você vai montar, do zero, um bot em Node.js com a biblioteca discord.js que consulta o estado do servidor e publica um painel que se atualiza sozinho, além de responder a comandos slash como /status e /online. Vamos tratar tanto do caminho de ler o banco de dados diretamente quanto do caminho mais seguro de consumir uma API do próprio site. Lembre-se desde já: nomes de tabelas, colunas e portas variam por versão e emulador (Season 6, Season 15, IGCN, MuEMU, etc.), então os exemplos aqui são esqueleto a adaptar, não verdade universal.
Pré-requisitos
Antes de escrever qualquer linha de código, garanta que o ambiente base está pronto. Um bot é apenas um cliente a mais consumindo dados que já existem; se a fonte de dados não estiver acessível e estável, nenhum código salva. Se você ainda está montando a infraestrutura, vale revisar antes o guia de como criar servidor de MU Online, porque o bot pressupõe um servidor já funcional.
- Node.js 18 LTS ou superior instalado na máquina onde o bot vai rodar (o discord.js v14 exige no mínimo Node 16.11).
- Acesso ao banco de dados do servidor (SQL Server na maioria das versões) ou uma API de status no site.
- Uma conta no Discord Developer Portal com permissão para criar aplicações.
- Um servidor Discord onde você tenha permissão de administrador para adicionar o bot.
- Conhecimento básico de linha de comando e edição de arquivos de configuração.
- Firewall ajustado: se for ler o banco de outra máquina, a porta do SQL Server (ex.: 1433) precisa estar liberada apenas para o IP do bot.
Arquitetura: banco direto vs. API do site
Existem dois modelos para o bot obter os dados, e a escolha define toda a segurança do projeto.
| Abordagem | Vantagens | Desvantagens | Quando usar |
|---|---|---|---|
| Ler o banco direto | Simples, sem camada extra, dados brutos | Expõe a porta do SQL na rede; risco se o token/credencial vazar | Bot roda no mesmo VPS do banco, rede fechada |
| Consumir API do site | Você controla exatamente o que sai; não abre o banco | Exige criar e manter um endpoint no site | Bot roda em máquina externa; ambiente de produção |
A recomendação para produção é a API do site: você cria um endpoint como /api/status.php que devolve um JSON enxuto, e o bot só enxerga aquilo. Assim, mesmo que o token do bot vaze, ninguém alcança o banco. Vamos mostrar os dois, mas trate a API como o caminho preferencial.
Passo 1 — Criar a aplicação e o bot no Discord
- Acesse o Discord Developer Portal e clique em New Application. Dê um nome como
StatusMU. - No menu lateral, entre em Bot e clique em Add Bot.
- Em Privileged Gateway Intents, para este bot de status você normalmente não precisa de intents privilegiadas, o que simplifica a aprovação. Deixe-as desligadas se só for publicar embeds.
- Clique em Reset Token, copie o token e guarde-o com cuidado — ele será usado no
.env. - Vá em OAuth2 → URL Generator, marque os escopos
boteapplications.commands, e nas permissões marque Send Messages, Embed Links e Read Message History. - Copie a URL gerada, abra no navegador e adicione o bot ao seu servidor Discord.
Passo 2 — Estrutura do projeto
No terminal da máquina do bot, crie a pasta e instale as dependências:
mkdir statusmu && cd statusmu
npm init -y
npm install discord.js dotenv mssql axios
Crie um arquivo .env na raiz do projeto. Este arquivo nunca deve ir para o Git:
DISCORD_TOKEN=cole_seu_token_aqui
CLIENT_ID=id_da_aplicacao
GUILD_ID=id_do_seu_servidor_discord
STATUS_CHANNEL_ID=id_do_canal_de_status
# Opção A: banco direto
DB_SERVER=127.0.0.1
DB_PORT=1433
DB_DATABASE=MuOnline
DB_USER=mubot_reader
DB_PASSWORD=SenhaForte!123
# Opção B: API do site
STATUS_API_URL=https://seusite.com/api/status.php
Passo 3 (opção A) — Endpoint de status no site
Se você optar pela API — o caminho seguro —, crie no site um arquivo PHP que devolve o status como JSON. Ele encapsula toda a lógica de consulta e nunca expõe credenciais ao bot:
<?php
// api/status.php
header('Content-Type: application/json');
$host = '127.0.0.1';
$db = 'MuOnline';
$user = 'site_reader';
$pass = 'SenhaDoSite!123';
try {
$conn = new PDO("sqlsrv:Server=$host;Database=$db", $user, $pass);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Jogadores conectados — nome da tabela varia por versão
$stmt = $conn->query("SELECT COUNT(*) AS online
FROM MEMB_STAT WHERE ConnectStat = 1");
$online = (int) $stmt->fetch(PDO::FETCH_ASSOC)['online'];
echo json_encode([
'status' => 'online',
'online' => $online,
'updated' => date('c'),
]);
} catch (Exception $e) {
echo json_encode(['status' => 'offline', 'online' => 0]);
}
Teste abrindo a URL no navegador — você deve ver um JSON limpo. A grande vantagem: o bot só conhece essa URL, e você decide exatamente quais campos aparecem.
Passo 4 — Testar de fato se o servidor está no ar
O erro clássico é considerar o servidor "online" só porque o banco respondeu. O banco continua de pé mesmo com o GameServer parado. Para refletir o estado real, faça também um teste de porta TCP no GameServer. Em Node.js:
// checkPort.js
const net = require('net');
function checkPort(host, port, timeout = 3000) {
return new Promise((resolve) => {
const socket = new net.Socket();
socket.setTimeout(timeout);
socket.once('connect', () => { socket.destroy(); resolve(true); });
socket.once('timeout', () => { socket.destroy(); resolve(false); });
socket.once('error', () => { resolve(false); });
socket.connect(port, host);
});
}
module.exports = { checkPort };
Use a porta do seu GameServer (frequentemente 55901 ou 44405, mas varia por versão). Só marque como online quando a porta responder E a consulta de dados funcionar.
Passo 5 — O bot principal com painel automático
Crie o index.js. Ele consulta o status a cada 60 segundos e edita uma única mensagem fixa no canal, mantendo o painel sempre atual sem poluir o chat:
require('dotenv').config();
const { Client, GatewayIntentBits, EmbedBuilder } = require('discord.js');
const axios = require('axios');
const { checkPort } = require('./checkPort');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
let statusMessageId = null;
async function fetchStatus() {
try {
const gameOnline = await checkPort('127.0.0.1', 55901);
const { data } = await axios.get(process.env.STATUS_API_URL, { timeout: 5000 });
return {
online: gameOnline && data.status === 'online',
players: data.online ?? 0,
};
} catch (err) {
return { online: false, players: 0 };
}
}
function buildEmbed(status) {
return new EmbedBuilder()
.setTitle('Status do Servidor')
.setColor(status.online ? 0x2ecc71 : 0xe74c3c)
.addFields(
{ name: 'Estado', value: status.online ? '🟢 Online' : '🔴 Offline', inline: true },
{ name: 'Jogadores', value: `${status.players}`, inline: true },
)
.setFooter({ text: 'Atualizado' })
.setTimestamp();
}
async function updatePanel() {
const channel = await client.channels.fetch(process.env.STATUS_CHANNEL_ID);
const status = await fetchStatus();
const embed = buildEmbed(status);
if (statusMessageId) {
try {
const msg = await channel.messages.fetch(statusMessageId);
await msg.edit({ embeds: [embed] });
return;
} catch { statusMessageId = null; }
}
const sent = await channel.send({ embeds: [embed] });
statusMessageId = sent.id;
}
client.once('ready', () => {
console.log(`Bot online como ${client.user.tag}`);
updatePanel();
setInterval(updatePanel, 60_000);
});
client.login(process.env.DISCORD_TOKEN);
Passo 6 — Comandos slash
Além do painel automático, dê ao jogador o comando /status sob demanda. Registre o comando com um script de deploy e trate a interação no bot:
// deploy-commands.js
require('dotenv').config();
const { REST, Routes, SlashCommandBuilder } = require('discord.js');
const commands = [
new SlashCommandBuilder().setName('status')
.setDescription('Mostra o status atual do servidor').toJSON(),
];
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
rest.put(
Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
{ body: commands }
).then(() => console.log('Comandos registrados')).catch(console.error);
No index.js, adicione o listener de interação, reaproveitando fetchStatus() e buildEmbed() para responder com o mesmo embed. Comandos de guild aparecem em segundos; comandos globais podem levar até uma hora para propagar.
Passo 7 — Manter o bot vivo com PM2
O processo do bot precisa reiniciar sozinho se cair. Use o PM2:
npm install -g pm2
pm2 start index.js --name statusmu
pm2 save
pm2 startup
Com pm2 logs statusmu você acompanha a saída em tempo real, e pm2 restart statusmu recarrega após mudanças.
Erros comuns e soluções
| Sintoma | Causa provável | Solução |
|---|---|---|
| Bot mostra "online" com servidor caído | Só checa o banco, não o GameServer | Adicione teste de porta TCP no GameServer |
ECONNREFUSED 1433 | TCP/IP desabilitado ou porta bloqueada | Habilite TCP/IP no SQL Config Manager e libere no firewall |
| Comandos slash não aparecem | Deploy não executado ou escopo errado | Rode deploy-commands.js e confira CLIENT_ID/GUILD_ID |
| Painel cria mensagem nova a cada ciclo | ID da mensagem não persiste | Salve o statusMessageId em arquivo/DB para sobreviver a restart |
Invalid Token | Token errado ou regenerado | Copie o token atual em Bot → Reset Token e atualize o .env |
| Rate limit ao editar | Intervalo curto demais | Aumente o intervalo para 60s ou mais |
Boas práticas de segurança
Nunca commite o .env. Crie um usuário de banco somente-leitura, com GRANT SELECT apenas nas tabelas necessárias, e jamais use a conta sa. Se for expor uma API de status no site, devolva só os campos estritamente públicos — nunca senhas, seriais ou e-mails. Prefira que o bot consuma a API em vez de abrir a porta do banco para a internet. E documente o intervalo de atualização para que futuras manutenções não o reduzam a ponto de gerar rate limit.
Checklist de lançamento
- Node.js 18+ instalado na máquina do bot
- Aplicação e bot criados no Developer Portal, token guardado no
.env - Bot adicionado ao servidor Discord com permissões de enviar mensagens e embeds
- Fonte de dados definida (API do site ou banco somente-leitura)
- Teste de porta TCP do GameServer implementado
- Canal de status criado e ID configurado
- Painel automático atualizando a cada 60s
- Comando
/statusregistrado e respondendo - PM2 configurado com
pm2 saveepm2 startup .envfora do controle de versão e com permissões restritas
Perguntas frequentes
O bot precisa rodar no mesmo VPS do servidor de MU?
Não. Ele só precisa de acesso ao banco de dados ou a um endpoint HTTP do site. Pode rodar em outra máquina, desde que a porta do SQL Server (1433, por exemplo) esteja liberada ou o site exponha uma API de status.
Qual a diferença entre ler o banco direto e usar uma API do site?
Ler o banco é mais direto mas expõe o SQL na rede. Uma API HTTP no próprio site é mais segura porque você controla exatamente quais dados saem e não abre a porta do banco para fora.
Por que meu bot mostra 'online' mesmo com o servidor caído?
Provavelmente você está checando só a conexão com o banco, e o banco continua de pé mesmo com o GameServer parado. Faça também um teste de porta TCP no GameServer para refletir o estado real.
Com que frequência o bot deve atualizar o status?
Um intervalo de 60 segundos é seguro. Atualizações mais frequentes que 15 segundos podem esbarrar em rate limit do Discord ao editar a mesma mensagem várias vezes por minuto.
É seguro deixar o token do bot no código?
Nunca. O token dá controle total sobre o bot. Guarde-o em um arquivo .env fora do controle de versão e restrinja as permissões do arquivo no servidor.