Discord-бот для управления игровым сервером
Заходить в отдельную RCON-консоль или панель хостинга каждый раз, когда нужно кикнуть гриферa, глянуть онлайн или объявить вайп — быстро надоедает, особенно если админите сервер не в одиночку. Игроки и так сидят в Discord — логично, чтобы там же жила панель управления. Разберём, как прикрутить к боту RCON, уведомления о статусе сервера, живую статистику онлайна и разграничение прав. Если бота ещё нет и вы не понимаете, с чего начать установку на VPS — сначала прочитайте статью про базовую установку Discord-бота, там про токен, discord.js/discord.py, автозапуск через pm2/systemd. Здесь исходим из того, что бот уже крутится 24/7, и говорим только про игровую начинку.
Содержание
Что такое RCON и зачем его пускать через Discord
RCON (Remote Console) — это протокол, которым почти все современные игровые серверы принимают админ-команды по сети: логин по паролю, потом произвольная команда в виде текстовой строки, и сервер отвечает текстом же. У Source-игр (CS2, Rust, Garry's Mod) это классический Source RCON на UDP/TCP, у Minecraft — свой RCON-протокол (включается в server.properties через enable-rcon=true, rcon.password, rcon.port), у модовых серверов вроде ARK или 7 Days to Die — часто телнет-подобный интерфейс с похожей логикой.
Идея простая: бот держит открытое соединение (или открывает новое на каждую команду) к RCON-порту игрового сервера и просто проксирует то, что вы написали в Discord-канале, в этот протокол. Никакой магии — по сути обёртка вокруг тех же команд, которые вы вбивали бы в консоль хостинг-панели.
Для Source-игр в Node.js обычно берут пакет rcon или rcon-client, для Python — mcrcon. Для Minecraft подходят те же библиотеки — протокол один, что для ванильного сервера, что для Paper/Spigot.
Минимальный пример на Node.js (discord.js + rcon-client) — команда !rcon доступна только с ролью «Админ сервера»:
const { Rcon } = require('rcon-client');
client.on('messageCreate', async (msg) => {
if (!msg.content.startsWith('!rcon ')) return;
if (!msg.member.roles.cache.some(r => r.name === 'Админ сервера')) {
return msg.reply('Недостаточно прав.');
}
const command = msg.content.slice(6).trim();
try {
const rcon = await Rcon.connect({
host: process.env.GAME_HOST,
port: Number(process.env.RCON_PORT),
password: process.env.RCON_PASSWORD,
});
const response = await rcon.send(command);
await rcon.end();
msg.reply('```' + (response || 'OK (пустой ответ)') + '```');
} catch (err) {
msg.reply(`Ошибка RCON: ${err.message}`);
}
});
Пароль и порт RCON храните в .env, никогда не пишите в коде бота напрямую — это отдельная точка входа в ваш сервер, и утечка токена бота (обсуждали в статье про установку) здесь так же критична, как утечка RCON-пароля.
Уведомления в канал: сервер упал, сервер поднялся
Второй по важности сценарий — не тянуть команды руками, а получать пуш в Discord, когда с сервером что-то произошло. Два рабочих подхода, и они не взаимоисключающие:
- Пассивный опрос (polling). Бот раз в 30-60 секунд стучится на игровой порт через
gamedig(Node) илиpython-a2s/opengsq(Python) — это библиотеки для Server Query протокола, которым живут почти все игровые серверы (не путать с RCON, тут не нужен пароль, только статус: онлайн игроков, карта, версия). Если сервер не ответил N проверок подряд — шлём алерт в канал#server-status. - Активный хук из скрипта запуска. Если сервер живёт на вашем же VPS под systemd или через скрипт-обвязку (как в статьях «Как поднять сервер...» для конкретных игр), проще всего дёрнуть Discord Webhook прямо из shell-скрипта при старте/падении процесса — без опроса, мгновенно и без лишней нагрузки на бота.
Пример systemd-юнита с ExecStartPre/ExecStopPost, который шлёт вебхук:
# /etc/systemd/system/rust-server.service
[Service]
ExecStartPre=/usr/bin/curl -s -X POST -H "Content-Type: application/json" \
-d '{"content":"🟢 Rust-сервер запускается"}' \
https://discord.com/api/webhooks/XXXXXXXX/YYYYYYYYYY
ExecStopPost=/usr/bin/curl -s -X POST -H "Content-Type: application/json" \
-d '{"content":"🔴 Rust-сервер остановлен или упал"}' \
https://discord.com/api/webhooks/XXXXXXXX/YYYYYYYYYY
Webhook — это отдельная сущность от самого бота (создаётся в настройках канала: Правка канала → Интеграции → Веб-хуки), не требует токена бота и прав на подключение к шлюзу Discord, поэтому для простых односторонних уведомлений он даже проще, чем городить логику в самом боте. Но если хотите красивые embed-сообщения со статусом (карта, онлайн, аптайм) — тот же вебхук принимает и JSON с полем embeds, не только content.
Нюанс: ExecStopPost срабатывает и при штатной остановке, и при краше. Если нужно различать «упал» и «остановили руками» — проверяйте код выхода через $EXIT_STATUS в скрипте-обёртке или полагайтесь на опрос Server Query, там разница видна по паттерну пропущенных проверок.
Поднять сервер Discord-бот за пару минут
Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.
Создать серверСтатус бота как живой онлайн-счётчик
Самый простой способ показать онлайн прямо в списке участников Discord-сервера, без единой команды от игроков — обновлять статус бота (Activity/Presence). Discord позволяет боту показывать «Играет в...» или, для ботов с включённым Server Members Intent, кастомный статус вида «Watching 14/60 online».
На discord.js это выглядит так — опрашиваем сервер раз в минуту и обновляем presence:
const Gamedig = require('gamedig');
async function updatePresence() {
try {
const state = await Gamedig.query({
type: 'rust', // тип берётся из документации gamedig под вашу игру
host: process.env.GAME_HOST,
port: Number(process.env.QUERY_PORT),
});
client.user.setActivity(`${state.players.length}/${state.maxplayers} онлайн`, {
type: ActivityType.Watching,
});
} catch {
client.user.setActivity('сервер офлайн', { type: ActivityType.Watching });
}
}
setInterval(updatePresence, 60_000);
updatePresence();
gamedig поддерживает большинство протоколов из коробки (Source-движок, Minecraft, ARK, 7 Days to Die и десятки других — список типов в README), так что для смены игры обычно достаточно поменять type и порт. Учтите: Discord ограничивает частоту смены presence — не дёргайте setActivity чаще раза в 15-20 секунд, иначе упрётесь в rate limit шлюза.
Для более наглядной картины многие вместо статуса переименовывают отдельный канал вида 📊-online-14-60 тем же таймером — виден даже тем, кто бота не открывал. Но у Discord лимит на переименование канала — не чаще нескольких раз за 10 минут, иначе поймаете 429.
Команды для игроков: онлайн, вайп, рестарт
Админский RCON — это одно, но часть команд бота полезно открыть вообще всем на сервере, без проверки ролей — это снижает поток вопросов «а когда вайп» в личку админам.
Базовый набор, который стоит сделать открытым:
/online— список игроков онлайн (тот же Server Query, что и для presence, просто списком, а не в статус)./wipeили/nextwipe— время до следующего вайпа/рестарта. Если у вас крон-вайп по расписанию (частый случай для Rust — force wipe в первый четверг месяца плюс еженедельный map wipe), проще зашить расписание в код бота черезnode-cron, а не дёргать это из RCON./status— краткая карточка: карта, онлайн, версия сборки, аптайм./connect— присылает IP:порт и, если нужно, пароль сервера — банально, но экономит время новичкам.
Пример слэш-команды /nextwipe на discord.js с зашитым расписанием (еженедельный вайп по четвергам в 15:00 МСК):
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('nextwipe')
.setDescription('Когда следующий вайп сервера'),
async execute(interaction) {
const now = new Date();
const nextThursday = new Date(now);
nextThursday.setUTCDate(now.getUTCDate() + ((4 - now.getUTCDay() + 7) % 7 || 7));
nextThursday.setUTCHours(12, 0, 0, 0); // 15:00 МСК = 12:00 UTC
await interaction.reply(`Следующий вайп: <t:${Math.floor(nextThursday.getTime() / 1000)}:R>`);
},
};
Формат <t:...:R> — это встроенный в Discord таймстемп, который сам отрисуется как «через 3 дня» и автоматически подстроится под часовой пояс каждого читающего — удобнее, чем текстом писать московское время и надеяться, что все поймут пересчёт.
Для игр, где вайп настраивается плагином (в Rust это часто Oxide/uMod-плагин или самописный крон-скрипт — ставится он так же, как остальные Oxide-плагины), лучше вместо жёстко зашитой даты в боте дёргать RCON-команду, которая читает время вайпа из самого плагина — надёжнее, чем дублировать расписание в двух местах.
Права доступа: роли, а не пароли
Худший сценарий — когда RCON-пароль знает половина сервера, потому что кто-то когда-то кинул его в общий чат «на всякий случай». Роли Discord решают это на уровне архитектуры бота, а не человеческой дисциплины.
Практическая схема ролей, которая работает для большинства серверов:
| Роль Discord | Что доступно | Пример команд |
|---|---|---|
| Игрок | Только read-only команды | /online, /nextwipe, /connect |
| Модератор | + кик/бан внутри игры, тепловые команды | !rcon kick, !rcon ban, !rcon mute |
| Админ сервера | Полный RCON, рестарт, смена карты | !rcon (любая команда), /restart |
| Owner/Root | + управление самим ботом и хостингом | смена конфигов, деплой, доступ к панели |
В коде это чаще всего проверка interaction.member.roles.cache.some(...) (как в примере выше) или, для более гибкой схемы, список разрешённых ID ролей в конфиге бота (.env или config.json), а не хардкод названий — названия ролей на сервере могут переименовать, а ID роли стабилен, пока роль не удалена.
Дополнительно стоит: принимать RCON-команды только в приватном канале #admin-console, видимом ролям Модератор+; логировать каждую команду в отдельный канал-аудит (кто, когда, что ввёл); не выдавать роль «Админ сервера» через реакции или автовыдачу — только вручную; ротировать RCON-пароль, если кто-то с доступом покинул команду.
Что делать, если бот и сервер на разных хостах
Частая ошибка новичков: бот крутится на одном VPS (например, дешёвом для бота), а игровой сервер — на другом (арендованном под игру, с нужным объёмом RAM). Тогда RCON-порт и Server Query порт игрового сервера должны быть доступны бота снаружи — то есть открыты в фаерволе именно на IP бота, а не «для всех» (0.0.0.0/0), если можно сузить.
Если у вас сервер на MAATRIX GAMES, RCON и Query-порты игры обычно уже проброшены и указаны в панели управления сервером — там же смотрите точный порт RCON (он не всегда совпадает с игровым портом, у Rust, например, отдельный +rcon.port). Если бот стоит отдельно, не забудьте:
- Узнать внешний IP бота (
curl ifconfig.meна VPS с ботом). - В фаерволе игрового сервера (
ufw,iptablesили правило в панели хостинга) разрешить входящие на RCON-порт именно с этого IP. - Проверить связь до RCON вручную перед тем, как чинить код бота — например,
mcrcon -H IP -P PORT -p PASSWORD "status". Если консоль не коннектится напрямую, бот тоже не сможет, и дело не в коде.
Держать бота и сервер на одном хосте проще с точки зрения сети (localhost вместо публичного IP в RCON-конфиге), но падение VPS убивает сразу и сервер, и панель управления им — для небольших проектов это приемлемый компромисс, для крупных community лучше разносить.
Поднять сервер Discord-бот за пару минут
Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.
Создать серверЧастые вопросы
RCON-порт вообще нужно открывать наружу, или можно ограничить только локальным доступом?
Если бот на том же хосте, что и игровой сервер — да, можно и нужно слушать RCON только на 127.0.0.1, это безопаснее любого файервол-правила. Открывайте наружу только если бот физически на другой машине.
Можно ли одним ботом управлять несколькими игровыми серверами сразу?
Да, обычная практика — держите словарь/конфиг вида «имя сервера → host/port/RCON-пароль» и добавляйте параметр сервера в команды (/rcon rust1 status), чтобы не путать, куда именно уходит команда.
Что если игра не поддерживает RCON вообще (например, некоторые модовые сборки)?
Тогда остаётся SSH-доступ к консоли сервера (screen/tmux-сессия, в которую бот пишет команды через screen -S rust -X stuff "команда\n") — грубее, чем RCON, но работает почти везде, где сервер запущен в screen или tmux.
Нужно ли боту право администратора на Discord-сервере?
Нет, и лучше не давать — боту достаточно точечных прав (Send Messages, Manage Channels если он переименовывает канал-счётчик, Use Slash Commands), права Administrator создают лишнюю поверхность атаки при утечке токена.
Как защититься, если токен бота или RCON-пароль всё же утекли?
Для токена бота — в Discord Developer Portal сразу Reset Token (старый токен инвалидируется мгновенно), для RCON — смените пароль в конфиге игры и перезапустите сервер; оба действия делайте сразу же, не дожидаясь подтверждения, что утечка была использована.