Krótka odpowiedź. Dodaj mysql2 do swojego bota discord.js, przy starcie bota utwórz jedną pulę połączeń ze zmiennych środowiskowych i wykonuj każde zapytanie przez nią, używając symboli zastępczych ?. W PlayNHost każdy plan dla bota zawiera jedną bazę MySQL: utwórz ją na stronie Bazy danych, wklej jej dane w panelu Środowisko, a kod poniżej zadziała bez zmian. Baza przyjmuje połączenia z Twoich serwerów PlayNHost i z phpMyAdmin, a nie z Twojego komputera. Przetestowaliśmy ten kod 6 listopada 2026 z discord.js 14.28.1, mysql2 3.24.6 i Node.js 22, na MariaDB 10.11 z kontem bazy skonfigurowanym jak nasze (20 połączeń, limit zapytania 120 sekund): start, tabelę i obsługę /warn, ale nie prawdziwe logowanie do Discorda.
Otwórz serwer swojego bota w panelu i kliknij Bazy danych.
Kliknij Nowa baza danych. Pole Etykieta jest opcjonalne; w polu Zostanie utworzona jako zobaczysz pełną nazwę. Kliknij Utwórz bazę danych.
Otworzy się strona nowej bazy (później kliknij ją w sekcji Twoje bazy danych). Karta Połączenie pokazuje Adres hosta, Port, Baza danych, Nazwa użytkownika i Hasło. W sekcji Gotowa konfiguracja pole Konfiguracja dla zawiera fragment Node.js (mysql2) z tymi samymi, już wypełnionymi wartościami.
2. Wpisz dane w sekcji Środowisko
Otwórz stronę Bot, panel Środowisko i kliknij Wklej plik .env.
Wklej te pięć linii z wartościami z karty Połączenie:
const { Client, Events, GatewayIntentBits, InteractionContextType, MessageFlags, REST, Routes, SlashCommandBuilder } = require('discord.js');
const mysql = require('mysql2/promise');
// One pool for the whole bot. The values come from the Environment pane.
const pool = mysql.createPool({
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT),
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
connectionLimit: 5,
});
const commands = [
new SlashCommandBuilder()
.setName('warn')
.setDescription('Warn a member and count their warnings')
.setContexts(InteractionContextType.Guild)
.addUserOption((o) => o.setName('user').setDescription('Who to warn').setRequired(true))
.addStringOption((o) => o.setName('reason').setDescription('Why').setRequired(true).setMaxLength(200))
.toJSON(),
];
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once(Events.ClientReady, async () => {
await new REST().setToken(process.env.DISCORD_TOKEN).put(Routes.applicationCommands(client.user.id), { body: commands });
console.log(`Logged in as ${client.user.tag}`);
});
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand() || interaction.commandName !== 'warn' || !interaction.guildId) return;
const user = interaction.options.getUser('user', true);
const reason = interaction.options.getString('reason', true);
try {
await pool.execute('INSERT INTO warnings (guild_id, user_id, reason) VALUES (?, ?, ?)', [interaction.guildId, user.id, reason]);
const [rows] = await pool.execute('SELECT COUNT(*) AS total FROM warnings WHERE guild_id = ? AND user_id = ?', [interaction.guildId, user.id]);
await interaction.reply(`${user} has ${rows[0].total} warning(s).`);
} catch (err) {
console.error(err);
if (!interaction.replied) await interaction.reply({ content: 'Could not save the warning. Try again later.', flags: MessageFlags.Ephemeral }).catch(console.error);
}
});
async function main() {
await pool.query(`CREATE TABLE IF NOT EXISTS warnings (
id INT AUTO_INCREMENT PRIMARY KEY,
guild_id VARCHAR(20) NOT NULL,
user_id VARCHAR(20) NOT NULL,
reason VARCHAR(200) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY guild_user (guild_id, user_id)
)`);
console.log('Database ready');
await client.login(process.env.DISCORD_TOKEN);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
Co robi kod: przy starcie bot tworzy tabelę warnings, jeśli jej brakuje, wypisuje Database ready, a potem loguje się. /warn zapisuje ostrzeżenie i odpowiada łączną liczbą ostrzeżeń użytkownika. discord.js podaje identyfikatory Discorda jako ciągi znaków, bo są za duże na liczbę w JavaScript, więc zapisujemy je jako tekst. Symbole zastępcze ? wysyłają tekst użytkownika do bazy osobno od SQL, więc powód taki jak '); DROP TABLE warnings; -- zostaje zapisany jako zwykły tekst.
Dwie linie utrzymują bota przy życiu. setContexts(InteractionContextType.Guild) i sprawdzenie !interaction.guildId blokują /warn w wiadomości prywatnej, gdzie nie ma serwera, do którego można przypisać ostrzeżenie. Blok try/catch odpowiada krótką prywatną wiadomością, gdy zapytanie się nie powiedzie. Bez niego discord.js przekazuje błąd do zdarzenia error, a gdy nikt tam nie nasłuchuje, Node.js zatrzymuje bota. W naszym teście usunęliśmy tabelę i wykonaliśmy /warn: bot zalogował ER_NO_SUCH_TABLE, odpowiedział Could not save the warning. Try again later. i działał dalej.
4. Uruchom bota
Wgraj index.js i package.json w sekcji Files. Nie wgrywaj node_modules.
Naciśnij Uruchom i otwórz Console. Start instaluje pakiety, a potem pokazuje [PlayNHost] Starting your bot, Database ready i Logged in as ....
W Discordzie wpisz /warn. Aby zobaczyć wiersze, otwórz stronę bazy i kliknij Otwórz w phpMyAdmin.
Limity, które mają znaczenie dla puli
20 jednoczesnych połączeń na użytkownika bazy. Liczy się każdy proces używający tego samego loginu: Twój bot, jego druga kopia, panel. Połączenie 21 zostaje odrzucone z błędem ER_USER_LIMIT_REACHED. W naszym teście pula z connectionLimit: 30 zakończyła się tym błędem, gdy tylko 30 zapytań działało naraz. Pula z 5 połączeniami nigdy nie otwiera więcej niż 5: dodatkowe zapytania czekają na wolne połączenie.
Zapytanie trwające dłużej niż 120 sekund zostaje przerwane. MariaDB kończy je błędem 1969, Query execution was interrupted (max_statement_time exceeded). W naszym teście stało się to po 120.03 sekundy. Zapytania bota zwykle trwają milisekundy, więc dotyczy to tylko zapytania, które wymknęło się spod kontroli.
500 MB na bazę danych. Strona Bazy danych pokazuje, ile jest zajęte. Przy 500 MB zapisy są wstrzymywane, dopóki nie usuniesz wierszy.
Kopie zapasowe serwera nie obejmują bazy danych. Od czasu do czasu eksportuj ją z phpMyAdmin.
Błędy i co oznaczają
To dokładne komunikaty, które mysql2 zwrócił w naszym teście:
ER_ACCESS_DENIED_ERROR ... Access denied for user: błędny DB_USER lub DB_PASSWORD. Po Zresetuj hasło na karcie Połączenie stare hasło przestaje działać od razu: zmień DB_PASSWORD i uruchom bota ponownie.
ECONNREFUSED: nic nie przyjęło połączenia pod tym hostem i portem. Sprawdź DB_HOST i DB_PORT. Brak DB_HOST też kończy się tutaj, bo mysql2 łączy się wtedy z localhost, gdzie nie działa żadna baza: zmiennej brakuje w Środowisko albo bot nie został uruchomiony ponownie po jej dodaniu.
ENOTFOUND: DB_HOST jest źle wpisany, więc taka nazwa nie istnieje.
ETIMEDOUT: nic nie odpowiedziało w ciągu 10 sekund, czyli domyślnego czasu oczekiwania mysql2. Niezależnie od komunikatu, połączenie z Twojego komputera zawiedzie: baza przyjmuje tylko połączenia z serwerów PlayNHost i z phpMyAdmin.
ER_USER_LIMIT_REACHED ... has exceeded the 'max_user_connections' resource (current value: 20): otwartych jest za dużo połączeń, patrz limity powyżej. Zwykle to nowe połączenie przy każdej komendzie, które nigdy nie jest zamykane, albo druga kopia bota.
Instalacja discord.js i mysql2 od zera osiągnęła w naszym teście szczyt 147 MB pamięci (bez cache plików), w kontenerze z limitem 512 MB jak nasz najmniejszy plan dla botów. Od startu do chwili, gdy Discord odrzucił nasz testowy token, bot zużył najwyżej 31 MB; utworzył tabelę, ale nigdy się nie zalogował. Zalogowany bot na wielu serwerach zużywa więcej: Ile RAM potrzebuje bot Discord?
Używamy plików cookie, aby strona działała, aby rozumieć sposób jej użycia i mierzyć nasz marketing. Możesz zaakceptować wszystko, zaakceptować tylko niezbędne pliki cookie lub wybrać według kategorii.Używamy plików cookie, aby strona działała, aby rozumieć sposób jej użycia i mierzyć nasz marketing. Tutaj są domyślnie włączone. Możesz wyłączyć to, czego nie chcesz.Przeczytaj naszą politykę prywatności