MAATRIX GAMES / Блог / Discord-бот для модерации чата сообщества

Discord-бот для модерации чата сообщества

MAATRIX GAMES

Комьюнити растёт, в общий чат за вечер валится сотня сообщений, и в какой-то момент один-два живых модератора уже не успевают руками банить спамеров, вычищать инвайты на чужие серверы и разруливать конфликты в текстовом чате. Разберём, как поднять автомодерацию на Discord-сервере игрового проекта: от готового бота за 10 минут до кастомной системы предупреждений с логом и связкой с whitelist игрового сервера.

Готовое решение или своё: MEE6, Dyno или самописный бот

Первый вопрос — не «какие фильтры настроить», а «на чём вообще это будет работать». Три реалистичных варианта для игрового комьюнити.

MEE6 — самый раскрученный вариант, веб-панель без единой строчки кода, автомодерация, уровни, приветствия. Бесплатный тариф режет часть функций (продвинутый automod, кастомные команды) — на практике для нормальной фильтрации спама и ссылок обычно упираетесь в Premium (платится помесячно за сервер). Плюс: не надо ничего хостить и поддерживать. Минус: логика чёрного ящика — не видно, почему конкретное сообщение не срубилось фильтром, и тонкая настройка ограничена тем, что дала веб-панель.

Dyno — более гибкий бесплатный automod (фильтр по regex-паттернам, антиспам, антирейд), премиум нужен реже, чем у MEE6. Хорошо ложится на средние комьюнити, где нужна автомодерация и лог-канал, но не нужна глубокая кастомная логика под конкретную игру.

Кастомный бот (discord.js или discord.py) — оправдан, когда нужна интеграция именно с вашим игровым сервером: например, при бане в Discord автоматически дёргать RCON и банить того же игрока в игре, или синхронизировать роль «Верифицирован» с whitelist. Готовые боты про игровой сервер ничего не знают в принципе. Если бот для управления сервером у вас уже есть — статья про RCON-бота — модерацию логично дописать туда же, а не городить второго бота.

Практичный подход для большинства: начать с Dyno или MEE6 (закрывает 80% рутины за вечер), и только если упрётесь в конкретный кейс — «забанить в Discord = забанить в игре» — переходить на свой код.

Встроенный AutoMod самого Discord

Discord с 2022 года умеет часть автомодерации без ботов вообще — Server Settings → Safety Setup → AutoMod. Три готовых правила из коробки:

  • Спам — блокирует типичные спам-паттерны (повторяющиеся символы, массовые упоминания) на уровне платформы, без задержки на сторонний бот.
  • Ненормативная лексика — свой список стоп-слов плюс возможность добавить кастомные слова/фразы.
  • Ссылки на упоминания сервера — блокирует инвайты на чужие Discord-серверы, частый вектор рекламного спама.

Действие по срабатыванию — заблокировать сообщение, timeout автору (от 60 секунд до 28 дней) и/или отправить алерт в лог-канал. Плюс встроенного AutoMod — нулевая задержка (работает на стороне Discord, а не ждёт ответа бота) и не расходует ничей аптайм. Минус — правила довольно грубые, тонкой логики вроде «разрешить ссылку только в канале #media» там нет, для такого всё равно нужен бот.

Практика: включить встроенный AutoMod как первый рубеж (он бесплатный и мгновенный), а бота — как второй, для того, что AutoMod не покрывает.

Нужен игровой сервер?

MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.

Создать сервер

Фильтр мата, спама и ссылок в кастомном боте

Если делаете своего бота или дорабатываете существующего, базовый фильтр на discord.js выглядит примерно так — три независимые проверки на каждое сообщение:

const badWords = ['слово1', 'слово2']; // свой список, не палим полностью
const inviteRegex = /(discord\.gg|discord\.com\/invite)\/\S+/i;
const spamThreshold = { count: 5, windowMs: 5000 }; // 5 сообщений за 5 секунд
const recentMessages = new Map(); // userId -> timestamps[]

client.on('messageCreate', async (msg) => {
  if (msg.author.bot || !msg.guild) return;

  const content = msg.content.toLowerCase();

  // 1. Мат
  if (badWords.some(w => content.includes(w))) {
    await msg.delete();
    return warnUser(msg.member, 'нецензурная лексика', msg.channel);
  }

  // 2. Инвайты на чужие серверы (свой сервер не трогаем)
  if (inviteRegex.test(content) && !content.includes('discord.gg/ваш-инвайт')) {
    await msg.delete();
    return warnUser(msg.member, 'реклама стороннего сервера', msg.channel);
  }

  // 3. Антиспам (флуд одинаковыми/частыми сообщениями)
  const now = Date.now();
  const timestamps = (recentMessages.get(msg.author.id) || []).filter(t => now - t < spamThreshold.windowMs);
  timestamps.push(now);
  recentMessages.set(msg.author.id, timestamps);
  if (timestamps.length >= spamThreshold.count) {
    await msg.member.timeout(10 * 60 * 1000, 'Антиспам: превышен лимит сообщений');
    return logAction(msg.guild, `⏱ ${msg.author.tag} получил таймаут 10 минут за спам`);
  }
});

Список стоп-слов лучше не хардкодить в коде, а вынести в JSON-файл или базу — тогда модераторы правят список без деплоя бота. Регулярку для мата стоит писать с учётом обхода через похожие символы (м@т, м а т) — полностью не закроете, но базовые обходы фильтр должен ловить, иначе смысл теряется в первую же неделю.

Важный нюанс: у бота должно быть право Manage Messages для удаления и Moderate Members для таймаутов, и роль бота в иерархии сервера должна стоять выше ролей, которые он модерирует — иначе Discord API вернёт ошибку прав на действие против модератора или админа (что, впрочем, и правильно — бот не должен мочь таймаутить владельца сервера).

Система предупреждений: от warn до бана

Голое удаление сообщений без счётчика — плохая практика: нарушитель не понимает, что накопил три предупреждения и сейчас улетит в таймаут. Рабочая эскалация, которая закрывает большинство игровых комьюнити:

Нарушение по счётуДействиеДлительность
1-еПредупреждение в лог + личное сообщение
2-еTimeout10 минут
3-еTimeout1 час
4-еTimeout24 часа
5-еБанПостоянный (разбор модераторами)

Храните счётчик предупреждений не в оперативной памяти бота (слетит при рестарте), а в SQLite/PostgreSQL/MongoDB — таблица warnings (user_id, guild_id, reason, moderator_id, timestamp). Минимальная реализация на discord.js + better-sqlite3:

const db = require('better-sqlite3')('warnings.db');
db.exec(`CREATE TABLE IF NOT EXISTS warnings (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id TEXT, guild_id TEXT, reason TEXT,
  moderator_id TEXT, timestamp INTEGER
)`);

function addWarning(userId, guildId, reason, moderatorId) {
  db.prepare('INSERT INTO warnings (user_id, guild_id, reason, moderator_id, timestamp) VALUES (?, ?, ?, ?, ?)')
    .run(userId, guildId, reason, moderatorId, Date.now());
  const count = db.prepare('SELECT COUNT(*) c FROM warnings WHERE user_id = ? AND guild_id = ?')
    .get(userId, guildId).c;
  return count;
}

Полезная деталь, которую часто забывают: старые предупреждения стоит «сгорать» — например, снимать одно предупреждение за каждые 30 дней без нарушений. Иначе человек, залетевший под горячую руку два года назад, до сих пор формально «на грани бана», хотя давно ведёт себя нормально. Реализуется простым WHERE timestamp > ? при подсчёте актуальных предупреждений вместо всей истории.

Слэш-команды /warn, /warnings @user, /clearwarn для модераторов — обязательный минимум, чтобы не лазить в базу руками при разборе спорной ситуации.

Лог-канал: что писать и как не захламить

Отдельный приватный канал #mod-log, видимый только модераторам, — обязательная часть системы, без него автомодерация превращается в чёрный ящик: сообщения удаляются, а почему — никто, кроме бота, не знает.

Что стоит логировать embed-сообщением (не голым текстом — так проще сканировать глазами):

  • Удалённые ботом сообщения — автор, канал, причина, исходный текст (важно сохранить именно то, что удалили, иначе разбор жалобы «за что забанили» невозможен).
  • Выданные предупреждения, таймауты, баны — вручную модератором и автоматически ботом, с указанием кто (или «Auto-Mod») и почему.
  • Вход/выход участников с малым сроком аккаунта (частый признак спам-бота или альта забаненного) — можно отдельным правилом: если аккаунту меньше 3 дней и он сразу пишет ссылку, это почти всегда спам-рейд.
async function logAction(guild, text, color = 0xffa500) {
  const logChannel = guild.channels.cache.find(c => c.name === 'mod-log');
  if (!logChannel) return;
  await logChannel.send({
    embeds: [{
      description: text,
      color,
      timestamp: new Date().toISOString(),
    }],
  });
}

Разделяйте лог на два канала, если комьюнити большое: #mod-log для действий модерации (бан/варн/таймаут — читают все модераторы) и #automod-log для рутинного шума автофильтра (удалённые ссылки, мелкий спам) — иначе важные ручные баны теряются в потоке автоматических срабатываний.

Антирейд: массовый вход ботов и альтов

Отдельная головная боль — рейды: за минуту в сервер заходит 20-50 свежесозданных аккаунтов и разом постит спам или NSFW-контент в каждый канал. Автомодерация уровня «фильтр одного сообщения» тут не спасает, реагировать нужно на паттерн входа.

Базовая защита без стороннего бота — Server Settings → Safety Setup → Verification Level, выставить на «Высокий» (требует подтверждённый email и пробыть на сервере Discord в целом от 10 минут) на время рейда, и Membership Screening (правила, которые нужно принять перед тем, как писать в чат).

Для автоматического реагирования на кастомном боте — простой счётчик входов за окно времени:

const joinTimestamps = [];

client.on('guildMemberAdd', async (member) => {
  const now = Date.now();
  joinTimestamps.push(now);
  while (joinTimestamps.length && now - joinTimestamps[0] > 60_000) joinTimestamps.shift();

  if (joinTimestamps.length >= 10) { // 10+ входов за минуту — похоже на рейд
    await member.guild.setVerificationLevel('HIGH', 'Автоматически: подозрение на рейд');
    await logAction(member.guild, '🚨 Обнаружен возможный рейд, Verification Level поднят до High', 0xff0000);
  }
});

Не забудьте автоматически откатывать уровень верификации обратно через час-два — иначе после рейда новые нормальные игроки будут спотыкаться о лишний барьер входа неделями, потому что кто-то забыл вернуть настройку.

Связка с whitelist игрового сервера

Если ваш игровой сервер закрыт по whitelist, логично завязать доступ на роль в Discord — тогда модерация в Discord автоматически управляет и доступом к игре: забанили нарушителя в чате — он же теряет доступ к серверу, без ручной синхронизации двух списков.

Схема для Minecraft (whitelist по нику через RCON):

// При выдаче роли "Верифицирован" (например, после ответа на вопросы бота-гейта)
async function onVerified(member, minecraftNick) {
  const rcon = await Rcon.connect({ host, port, password });
  await rcon.send(`whitelist add ${minecraftNick}`);
  await rcon.end();
  await member.roles.add('ROLE_ID_VERIFIED');
}

// При бане в Discord — синхронный ban на игровом сервере
client.on('guildBanAdd', async (ban) => {
  const nick = await getMinecraftNickByDiscordId(ban.user.id); // своя таблица привязки
  if (!nick) return;
  const rcon = await Rcon.connect({ host, port, password });
  await rcon.send(`whitelist remove ${nick}`);
  await rcon.send(`ban ${nick} Забанен модератором в Discord`);
  await rcon.end();
});

Для этого нужна таблица привязки Discord ID ↔ игровой ник — заводится через команду верификации (/link ник) при первом входе, вручную сверять два списка на сотнях игроков нереально. Подробно процесс верификации и тикеты для доступа разобраны в статье про Discord-интеграцию RP-сервера — там же пример гейта с вопросами перед выдачей роли.

Нюанс: RCON-команда бана различается между играми (ban, banid, pardon для снятия — сверяйтесь с консольными командами конкретной сборки), а у некоторых серверов (Rust, ARK) банят чаще по SteamID, а не по нику — тогда таблица привязки хранит SteamID вместо игрового имени.

Нужен игровой сервер?

MAATRIX GAMES — 45+ игр, деплой за минуты, DDoS-защита, автобэкапы. Тарифы от 399 ₽/мес.

Создать сервер

Частые вопросы

MEE6 бесплатного тарифа хватит для небольшого комьюнити (до ~200 человек)?

Обычно да — базовый фильтр спама и приветствия есть на бесплатном тарифе, продвинутый automod с кастомными правилами и логами обычно за Premium. Для старта достаточно, апгрейд имеет смысл, когда фильтры начинают пропускать реальный спам.

Как отличить ложное срабатывание фильтра мата от настоящего нарушения?

Полностью — никак, отсюда и нужен #mod-log с сохранённым текстом удалённого сообщения: модератор за секунду видит контекст и либо подтверждает бан, либо снимает предупреждение командой /clearwarn. Полностью автоматический бан без ручной проверки для мата — плохая идея, для явного спама (рейд, инвайты) — приемлемо.

Нужно ли давать боту право Administrator, чтобы он мог банить и таймаутить?

Нет, и не стоит — достаточно точечных прав Manage Messages, Moderate Members, Ban Members, Kick Members. Administrator создаёт лишнюю поверхность атаки, если токен бота утечёт.

Что делать с самопальными обходами фильтра мата (пробелы, спецсимволы, эмодзи вместо букв)?

Нормализуйте текст перед проверкой — убирайте пробелы/повторы внутри слова, приводите похожие юникод-символы к латинице/кириллице через таблицу замен. Идеального решения нет, это гонка вооружений, но базовая нормализация закрывает большинство ленивых обходов.

Можно ли совместить автомодерацию и RCON-бота управления сервером в одном процессе?

Да, и для небольших/средних комьюнити это даже удобнее — один процесс, один деплой, общая база для привязки Discord ID к игровому нику вместо синхронизации между двумя ботами.