Bot Discord do whitelisty: formularz zgłoszenia i auto-whitelist na SMP

Bot Discord do whitelisty: formularz zgłoszenia i auto-whitelist na SMP

Jeśli SMP wpuszcza wszystkich, czat zamieni się w chaos w tydzień: grieferzy, dzieciaki bez mikrofonu i parę botów handlowych. Formularz zgłoszenia w Discordzie odsiewa przypadkowych ludzi i zbiera info o każdym graczu. Poniżej: których gotowych botów użyć, jak zbudować własnego na discord.js i jak spiąć wszystko z RCON lub DiscordSRV.

Po co formularz zgłoszenia zamiast zwykłego /whitelist

Otwarta whitelista przez /whitelist add nick działa tylko dopóki serwer jest mały. Gdy projekt trafi na topki albo Reddit, fala zgłoszeń staje się nie do ogarnięcia. Formularz w Discordzie rozwiązuje trzy problemy jednocześnie.

Po pierwsze: filtr przypadkowych. Ktoś, kto nie chce napisać pięciu zdań o sobie, raczej nie zostanie dłużej niż dwa dni. To naturalna selekcja.

Po drugie: zbieranie danych. Nick, wiek, wcześniejsze doświadczenie na SMP, jak znalazł serwer, co lubi w Minecraftcie. Ta informacja pomaga adminom zrozumieć grupę odbiorców, a moderatorom rozwiązywać konflikty.

Po trzecie: jeden punkt wejścia. Wszystkie zgłoszenia trafiają do kanału #applications, z przyciskami accept/deny i historią decyzji. Jeśli gracz potem złamie zasady, można otworzyć jego oryginalny formularz i sprawdzić, czy kłamał na starcie.

W większości przypadków bot Discord stawia się w parę godzin i działa latami bez modyfikacji.

Architektura: jak to działa

Podstawowy flow:

  1. Gracz dołącza do serwera Discord projektu.
  2. Wpisuje slash-komendę /apply w dowolnym kanale.
  3. Bot otwiera Modal z polami: Minecraft username, age, experience, why join.
  4. Po submicie bot publikuje zgłoszenie w prywatnym kanale #applications z dwoma przyciskami Accept i Deny.
  5. Moderator klika Accept, bot wysyła whitelist add <username> na serwer przez RCON lub REST i nadaje graczowi rolę Discord @Member.
  6. Przy Deny bot pisze graczowi DM z powodem i zapisuje zdarzenie do logów.

Połączenie z serwerem Minecraft idzie jednym z trzech kanałów: RCON (TCP na porcie 25575), plugin DiscordSRV z jego API, albo REST przez własny mały plugin. Najpewniejszy wariant to DiscordSRV, bo sam się reconnektuje i przeżywa restarty serwera.

Gotowe boty: WhitelistBot, ApplicationBot, DraftBot

Jeśli nie chcesz pisać kodu, są działające gotowce.

DraftBot to duży bot z modułem zgłoszeń. Custom forms i integracja webhookami dostępne w płatnym planie. Nadaje się do społeczności, w których Discord i tak jest główną platformą. Minus: stronę Minecrafta musisz dopiąć własnym pluginem-handlerem.

ApplyBot (apply-bot.com) to dedykowany bot do zgłoszeń. Wspiera Modal-formy, podział na kategorie (whitelist, staff, builder), własne pytania. Free tier ogranicza liczbę aktywnych form.

WhitelistBot to prosty open-source bot na discord.py, znajdziesz na GitHubie pod tą nazwą. Z pudełka: jedna forma, integracja RCON, przyciski accept/deny. Minus: nie jest aktywnie rozwijany, na nowszych wersjach discord.py rzuca deprecation warnings.

Dla większości SMP do 200 graczy ApplyBot lub DraftBot wystarczy bez kustomizacji. Jeśli projekt jest większy albo potrzebujesz nietypowej logiki (np. auto-sprawdzanie wieku konta Discord), pisz własnego.

Tani sposób: DiscordSRV-Apply

Jeśli już masz DiscordSRV do synchronizacji czatu, zainstaluj addon DiscordSRV-Apply. Forma otwiera się w Discordzie przez slash-komendę, odpowiedzi lecą do wybranego kanału, przyciski accept/deny od razu są pod nimi. Bez osobnego bota, bez hostingu, bez VPS-a.

Konfig addonu wygląda tak:

applications:
  default:
    questions:
      - "Twój nick Minecraft?"
      - "Ile masz lat?"
      - "Opisz swoje doświadczenie SMP"
      - "Dlaczego chcesz do nas?"
      - "Jak znalazłeś serwer?"
    review-channel: "1234567890"
    on-accept:
      - "whitelist add %username%"
      - "lp user %username% parent set member"
      - "discord-role-add %discord_id% Member"
    on-deny:
      - "discord-dm %discord_id% Zgłoszenie odrzucone. Powód: %reason%"

Działa od razu, DiscordSRV sam podstawia %username% i %discord_id%. W praktyce to najszybszy sposób uruchomienia formularza whitelisty na SMP do 500 graczy.

Własny bot na discord.js: szkielet w 100 liniach

Jeśli potrzebujesz własnej logiki, oto szkielet na discord.js v14. Implementuje /apply, otwiera modal, postuje na kanale, obsługuje przyciski.

const { Client, GatewayIntentBits, SlashCommandBuilder,
        ModalBuilder, TextInputBuilder, TextInputStyle,
        ActionRowBuilder, ButtonBuilder, ButtonStyle,
        EmbedBuilder } = require('discord.js');
const { Rcon } = require('rcon-client');

const client = new Client({ intents: [GatewayIntentBits.Guilds] });
const APPLICATIONS_CHANNEL = '1234567890';
const COOLDOWN = new Map();

client.on('interactionCreate', async (interaction) => {
  if (interaction.isChatInputCommand() && interaction.commandName === 'apply') {
    const last = COOLDOWN.get(interaction.user.id) || 0;
    if (Date.now() - last < 24 * 3600 * 1000) {
      return interaction.reply({ content: 'Już składałeś zgłoszenie w ciągu ostatnich 24h.', ephemeral: true });
    }
    const accountAge = Date.now() - interaction.user.createdTimestamp;
    if (accountAge < 7 * 24 * 3600 * 1000) {
      return interaction.reply({ content: 'Konto Discord za nowe (minimum 7 dni).', ephemeral: true });
    }

    const modal = new ModalBuilder().setCustomId('apply_modal').setTitle('Zgłoszenie SMP');
    const fields = ['username', 'age', 'experience', 'why'];
    const labels = ['Nick Minecraft', 'Wiek', 'Doświadczenie SMP', 'Czemu do nas?'];
    fields.forEach((id, i) => {
      const input = new TextInputBuilder()
        .setCustomId(id).setLabel(labels[i])
        .setStyle(i >= 2 ? TextInputStyle.Paragraph : TextInputStyle.Short)
        .setRequired(true).setMaxLength(i >= 2 ? 500 : 50);
      modal.addComponents(new ActionRowBuilder().addComponents(input));
    });
    return interaction.showModal(modal);
  }

  if (interaction.isModalSubmit() && interaction.customId === 'apply_modal') {
    const data = {
      username: interaction.fields.getTextInputValue('username'),
      age: interaction.fields.getTextInputValue('age'),
      experience: interaction.fields.getTextInputValue('experience'),
      why: interaction.fields.getTextInputValue('why'),
    };
    COOLDOWN.set(interaction.user.id, Date.now());

    const embed = new EmbedBuilder()
      .setTitle(`Zgłoszenie od ${interaction.user.tag}`)
      .addFields(
        { name: 'Nick', value: data.username, inline: true },
        { name: 'Wiek', value: data.age, inline: true },
        { name: 'Doświadczenie', value: data.experience },
        { name: 'Powód', value: data.why },
      ).setFooter({ text: `discord_id: ${interaction.user.id}` });

    const buttons = new ActionRowBuilder().addComponents(
      new ButtonBuilder().setCustomId(`accept_${interaction.user.id}_${data.username}`)
        .setLabel('Accept').setStyle(ButtonStyle.Success),
      new ButtonBuilder().setCustomId(`deny_${interaction.user.id}`)
        .setLabel('Deny').setStyle(ButtonStyle.Danger),
    );

    const channel = await client.channels.fetch(APPLICATIONS_CHANNEL);
    await channel.send({ embeds: [embed], components: [buttons] });
    return interaction.reply({ content: 'Zgłoszenie wysłane. Czekaj na decyzję moderatora.', ephemeral: true });
  }

  if (interaction.isButton()) {
    const [action, userId, username] = interaction.customId.split('_');
    if (action === 'accept') {
      const rcon = await Rcon.connect({ host: 'mc.example.com', port: 25575, password: process.env.RCON });
      await rcon.send(`whitelist add ${username}`);
      await rcon.send(`lp user ${username} parent set member`);
      await rcon.end();
      const member = await interaction.guild.members.fetch(userId);
      await member.roles.add('ROLE_ID_MEMBER');
      await member.send(`Zgłoszenie zaakceptowane. Łącz się: mc.example.com`).catch(() => {});
      await interaction.update({ content: `Zaakceptowane przez ${interaction.user.tag}`, components: [] });
    } else if (action === 'deny') {
      const member = await interaction.guild.members.fetch(userId);
      await member.send('Zgłoszenie odrzucone.').catch(() => {});
      await interaction.update({ content: `Odrzucone przez ${interaction.user.tag}`, components: [] });
    }
  }
});

client.login(process.env.TOKEN);

Rejestracja slash-komendy idzie osobnym skryptem register.js przez Discord REST API, dokumentacja jest na discord.js.guide. Potem deploy na dowolny VPS za 20 zł miesięcznie albo nawet na Raspberry Pi.

Łącze z Minecraftem: RCON, DiscordSRV lub REST

RCON to standard. Włączasz w server.properties:

enable-rcon=true
rcon.port=25575
rcon.password=dlugie_losowe_haslo_tutaj

Z bota wysyłasz komendy przez rcon-client (Node) lub mcrcon (Python). Minus RCON: port trzeba zamknąć firewallem na wszystko poza IP bota, inaczej każdy brute-forcer dostanie shell konsoli.

DiscordSRV udostępnia API przez swój plugin-channel, możesz wysyłać komendy przez dsrv:execute. Bezpieczniej, bo nie otwiera dodatkowych portów. Minus: wymaga DiscordSRV z całym jego stosem zależności.

Plugin REST to: piszesz mały plugin Paper (jakieś 50 linii), który nasłuchuje HTTP na localhost i akceptuje token w nagłówku. Bot pyta przez curl lub fetch. To najczystsza droga dla produkcji: brak otwartych na zewnątrz portów, token w nagłówku, dowolne logowanie.

LinkAccount: spinanie Discord z Minecraftem

Jeśli chcesz na stałe związać konto Discord z nickiem Minecraft (dla auto-ról, sklepu, statystyk), użyj wzorca link-kodów.

Gracz wchodzi na serwer, wpisuje /link, plugin zwraca kod typu A7K2-9XQ8. W Discordzie pisze do bota /link A7K2-9XQ8, bot wysyła kod na serwer przez RCON, plugin weryfikuje, wiąże discord_id z UUID i zapisuje parę w bazie.

DiscordSRV robi to z pudełka komendą /discord link. Jeśli piszesz własne, trzymaj tabelę linked_accounts(uuid, discord_id, linked_at) w SQLite lub MySQL.

Anti-spam i ochrona przed fejkami

Bez ochrony kanał #applications zapełni się śmieciem w tydzień. Minimum:

  • Cooldown na discord_id: jedno zgłoszenie na 24 godziny lub jedno na tydzień jeśli poprzednie odrzucone.
  • Minimalny wiek konta Discord: 7-30 dni. Świeżo założone konta to prawie zawsze alty albo boty.
  • Captcha-rola: przed /apply gracz musi przejść podstawową weryfikację przez bota typu Wick lub Captcha.bot.
  • Minimalna długość odpowiedzi: na pole "dlaczego" wymagaj 100+ znaków, inaczej dostaniesz odpowiedzi z jednego słowa.
  • Logowanie źródła: jeśli bot stoi za reverse proxy, można wyłapywać boty po podejrzanych wzorcach.

Większe projekty dorzucają jeszcze weryfikację nicka przez Mojang API: czy konto Minecraft istnieje, czy nie jest banowane na serwerach VPN.

Szablon pytań dla SMP

Nie rób 20 pytań, nikt tego nie skończy. Optimum to 4-6 pól:

  1. Nick Minecraft (dokładna pisownia).
  2. Wiek (liczba, bez weryfikacji prawdziwości, ale fejków jest mniej).
  3. Doświadczenie SMP: na jakich serwerach grał, co budował.
  4. Co lubisz w Minecraftcie: budowanie, redstone, farmienie, PvP.
  5. Dlaczego właśnie do nas, co cię złapało.
  6. Jak znalazłeś serwer: topki, Reddit, znajomy, YouTube.

Szóste pytanie pomaga w analityce marketingu. Jeśli 80% pisze "od znajomego", to dominuje wzrost organiczny i można pauzować płatne kanały.

Auto-role po accepcie

Po zaakceptowaniu zgłoszenia jednocześnie:

  • Nadać rolę @Member w Discordzie.
  • Przypiąć do grupy LuckPerms member na serwerze.
  • Usunąć rolę @Applicant jeśli była.
  • Zapisać decyzję w #mod-log z nazwą moderatora i czasem.

Przez DiscordSRV to jedna linia w configu group sync. Przez własny bot trzy wywołania API z rzędu.

GroupRoleSynchronizationGroupsAndRolesToSync:
  member: "1234567890"
  vip: "9876543210"
  staff: "5555555555"

DiscordSRV pilnuje zmian ról w Discordzie i grup w LuckPerms, sync działa w obie strony.

FAQ

Czy formularz zgłoszenia da się zrobić bez programowania?

Tak, przez DiscordSRV-Apply addon albo płatny ApplyBot. DiscordSRV-Apply instaluje się jak zwykły plugin Paper, konfiguracja przez yaml, zero kodu. Pasuje do serwerów do 500 aktywnych graczy. ApplyBot konfigurujesz bezpośrednio w Discordzie przez slash-komendy.

Jak chronić się przed fejkowymi zgłoszeniami i botami?

Minimum: 24-godzinny cooldown na discord_id, limit wieku konta (7+ dni), weryfikacja captchą przed dopuszczeniem do /apply. Większe projekty dodatkowo walidują nick Minecraft przez Mojang API i ręcznie moderują pierwsze 24h po rejestracji w Discordzie.

Bot ma być w Discordzie czy plugin Minecrafta?

Zależy od podejścia. Bot Discord (discord.js, discord.py) jest hostowany osobno i gada z serwerem przez RCON. Wariant pluginowy to DiscordSRV plus addon, wszystko kręci się na serwerze Minecraft. Plugin jest prostszy dla małych SMP, bot bardziej elastyczny dla dużych.

Co robić z odrzuconymi zgłoszeniami?

Loguj odrzucenia w osobnym kanale z powodem. Po 30 dniach pozwalaj automatycznie na ponowne złożenie. Nie kasuj odrzuconych wiadomości od razu, moderatorzy potrzebują historii decyzji. Jeśli odrzucenia przekraczają 50%, przejrzyj pytania, prawdopodobnie są za ostre.

Jak zrobić, żeby accept automatycznie nadawał role w Minecraftcie?

Przez RCON lp user <username> parent set member po whitelist add. Z DiscordSRV skonfiguruj group sync, rola Discord będzie automatycznie mapować się na grupę LuckPerms.

Czy pytać o prawdziwy wiek?

Prawnie nie zweryfikujesz, ale nawet samodzielnie podany odsiewa część trolli. Minimalny próg 13 lat (wymaganie Discord ToS) jest obowiązkowy. Dla SMP celującego w dorosłą publikę pisz 16+ lub 18+ w opisie i wyraźnie podawaj to w formularzu.

Czy jeden bot może obsługiwać kilka serwerów?

Tak, w configu bota podajesz kilka endpointów RCON, a w formularzu pytasz "na który serwer aplikujesz". Każde zgłoszenie leci do swojego kanału #apps-survival, #apps-creative i tak dalej. Jeden proces obsługuje wszystkich.

Co dalej

Jeśli masz już DiscordSRV, spróbuj najpierw addonu DiscordSRV-Apply, pokrywa 90% potrzeb w godzinę pracy. Jeśli potrzebujesz własnej logiki (linked accounts, multi-serwer, nietypowe checki), bierz discord.js i pisz własnego bota. Nigdy nie wystawiaj RCON do Internetu, zawsze filtruj po IP albo trzymaj plugin REST na localhost.

Formularz zgłoszenia w Discordzie oszczędza godziny moderacji tygodniowo i mocno podnosi jakość społeczności. Główna zasada: nie rób formularza z 15 pytań, prawie nikt go nie ukończy.


Chroń swój serwer przed atakami DDoS

Darmowa ochrona z konfiguracją w 5 minut. 1 TB ruchu w zestawie.

Wypróbuj za darmo


Powiązane artykuły