Discord bot je online přesně tak dlouho, jak dlouho běží jeho proces. Neexistuje fronta, která ho probudí, ani požadavek, který ho spustí na vyžádání: pokud proces není připojený ke gateway, bot se zobrazí jako offline a každý slash příkaz skončí hláškou „The application did not respond“. Hostovat bota 24/7 proto znamená jedinou nenápadnou věc - udržet malý proces Node naživu na stroji, který není tvůj notebook, restartovat ho, když spadne, a znovu ho nasadit, když ho změníš.
Tohle je celá cesta pro bota v discord.js: aplikace a její token, intents, které deklaruješ, struktura projektu, která se na serveru čistě spustí, registrace slash příkazů, nasazení z GitHubu a hrstka způsobů selhání, kvůli kterým boti ve tři ráno padají offline. Dimenzování je samostatná otázka, na kterou odpovídá článek kolik RAM a CPU potřebuje Discord bot; zkráceně: příkazový bot na pár desítkách serverů se pohodlně vejde do nejmenšího tarifu, jaký kdo prodává.
Co ve skutečnosti obnáší běh 24 hodin denně#
Tvůj bot otevře jeden odchozí WebSocket ke gateway Discordu a drží ho otevřený. Discord pošle hello payload s hodnotou heartbeat_interval (obvykle něco přes čtyřicet sekund) a knihovna v tomto intervalu posílá heartbeat, dokud spojení žije. Události přicházejí po tomto socketu; všechno, co bot dělá zpátky - odpovědi, úpravy, registrace příkazů - jde přes REST API jako obyčejné HTTPS požadavky.
Z toho plynou dvě věci, které určují všechno kolem hostingu bota.
- K botovi není potřeba zvenku nic dopravit. Žádný příchozí port, žádná doména, žádný certifikát, žádné pravidlo firewallu. Bot funguje bez problémů i za NATem. Pokud později přidáš webový dashboard, je to druhá, jiná služba s vlastními požadavky.
- Proces musí být rezidentní. Platformy, které aplikaci uspí po období bez HTTP provozu, se nehodí, protože bot žádný HTTP provoz nedostává. Jakékoli řešení, kde se proces zastavuje a spouští pro každý požadavek, má pro tuto zátěž špatný tvar.
| Co bot potřebuje | Co nepotřebuje |
|---|---|
| Proces, který zůstává spuštěný | Příchozí port |
| Odchozí HTTPS a WebSocket | Doménu nebo certifikát |
| Restart, když skončí | Load balancer |
| Místo pro uložení stavu | Víc než jeden proces, pod 2 500 guild |
Další věc, kterou je dobré vědět před nasazením: opětovné připojení není zadarmo. Když socket spadne, knihovna se nejdřív pokusí o resume: pošle ID relace a poslední číslo sekvence, které viděla, a Discord přehraje zmeškané události. Pokud relaci nelze obnovit, musí se bot znovu identifikovat a identifikace mají rate limit - většina botů má 1 000 za den. Svůj vlastní limit zjistíš autentizovaným GET /gateway/bot, který vrátí objekt session_start_limit s poli total, remaining a reset_after. Bot ve smyčce pádů tenhle limit spálí, a proto je smyčka restartů skutečný problém, a ne jen šum v logu.
Aplikace, token a pozvánka#
Všechno začíná v Discord Developer Portal. Vytvoř aplikaci, otevři její záložku Bot a resetuj token. Zobrazí se jen jednou. Pokud ho ztratíš, resetuj ho znovu, což starý token okamžitě zneplatní a bota vypne, dokud nenasadíš novou hodnotu.
Token bota se skládá ze tří částí oddělených tečkami, podobných base64, a první část je zakódované ID tvé aplikace, takže uniklý token není anonymní: kdokoli, kdo ho uvidí, ví, ke kterému botovi patří. Discord se účastní secret scanningu na GitHubu, takže token odeslaný do veřejného repozitáře bývá zneplatněn během minut. To je ten dobrý výsledek. Špatný je token v soukromém repozitáři, který se později zveřejní. Ber ho jako heslo, které dává plnou kontrolu nad botem, a drž ho v proměnné prostředí - obecný případ rozebírá článek kam ukládat tajemství na aplikačním serveru.
Dva přepínače na té stránce lidi překvapí:
- Public bot rozhoduje, zda může bota pozvat kdokoli. U soukromého bota ho vypni a jen ty ho přidáš na servery.
- Requires OAuth2 code grant by mělo být vypnuté. Je pro postup, který téměř nikdo nepoužívá, a když je zapnuté, běžný odkaz s pozvánkou selže se zmatečnou chybou.
Pozvánku sestav v OAuth2, URL Generator. Jako scopes zaškrtni bot a applications.commands - bot pozvaný bez applications.commands se připojí, objeví se online a nebude mít žádné slash příkazy, což je jedna z nejčastějších příčin hlášky „moje příkazy se nezobrazují“. Pak vyber oprávnění, která bot opravdu potřebuje. Oprávnění lze doplnit později jen novou pozvánkou nebo úpravou role bota na serveru, takže se vyplatí promyslet je jednou a nežádat každého majitele serveru, aby to udělal dvakrát.
Intents a na co se vlastně přihlašuješ#
Intents se deklarují při připojení a rozhodují, které události ti Discord vůbec pošle. Když je nastavíš špatně, výsledkem je ticho, ne chyba, takže se vyplatí postupovat záměrně.
import { Client, GatewayIntentBits, Partials } from "discord.js";const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, ], partials: [Partials.Message, Partials.Channel, Partials.Reaction],});Guilds je prakticky povinný: bez něj knihovna nemá cache guild, kanálů a rolí a většina API přestane fungovat. Tři intents jsou privilegované a musí se navíc zapnout na záložce Bot v portálu: GUILD_MEMBERS, GUILD_PRESENCES a MESSAGE_CONTENT. Jakmile je bot ve více než 100 serverech, musí projít verifikací, aby si je ponechal, což je proces s čekací lhůtou, takže se rozhodni včas, jestli je potřebuješ. Bot postavený celý na slash příkazech MESSAGE_CONTENT nepotřebuje vůbec, protože data příkazu přijdou s interakcí.
Pole partials je druhá polovina. Pokud se událost týká něčeho, co knihovna nemá v cache - třeba reakce na zprávu odeslanou před startem bota - discord.js událost vůbec nevyvolá, pokud sis nezapnul částečné struktury. S partials dostaneš zástupný objekt a když potřebuješ úplný, zavoláš na něm .fetch().
Projekt, který se spustí sám#
Na serveru nikdo nepíše startovací příkaz. Musí fungovat z čistého kontejneru, v adresáři, který byl právě stažen z gitu, bez interaktivního shellu.
{ "name": "my-bot", "type": "module", "engines": { "node": ">=20" }, "scripts": { "start": "node src/index.js", "register": "node scripts/register-commands.js" }, "dependencies": { "discord.js": "^14.16.3" }}Startovací příkaz na hostingu by měl nainstalovat závislosti z lockfile a pak spustit bota:
npm ci --omit=dev && node src/index.jsnpm ci smaže node_modules a nainstaluje přesně to, co říká lockfile, a hlasitě selže, pokud se lockfile a manifest rozcházejí. npm install tiše vyřeší něco nového, a tak bot, který fungoval u tebe, na serveru natáhne rozbitou minor verzi. Commitni package-lock.json. Rozdíl podrobně popisuje npm ci vs npm install.
Zmíním dvě pasti s verzemi. Zaprvé, --omit=dev odstraní devDependencies, takže bot v TypeScriptu se s tímto přepínačem na serveru nezkompiluje: buď builduj v CI a nahraj dist, nebo --omit=dev vynech a smiř se s větší instalací. Zadruhé, discord.js během života verze 14 zvedl minimální verzi Node a engines je jen poznámka pro lidi, kontejner ji nevynucuje. Verzi běhového prostředí raději vypiš při startu, než ji předpokládat:
import { Events } from "discord.js";client.once(Events.ClientReady, () => { console.log(`node ${process.version}, logged in as ${client.user.tag}`); console.log(`guilds: ${client.guilds.cache.size}`);});Tyhle dva řádky zodpoví tři dotazy na podporu dřív, než padnou: jaký Node běží, jestli token fungoval a kolik serverů bot vidí. Používej konstantu Events, ne řetězcový literál, protože názvy událostí se mezi vydáními měnily a konstanta sleduje nainstalovanou verzi.
Ještě jeden detail prostředí: serverové kontejnery běží v UTC, pokud se neřekne jinak. Pokud tvůj bot posílá denní zprávu v 09:00, pošle ji v 09:00 UTC. Nastav proměnnou prostředí TZ nebo čas explicitně přepočítej v kódu. Tichý posun časového pásma je nejotravnější chyba v této kategorii, protože je špatně jen o pevný počet hodin.
Registrace slash příkazů bez rozbití čehokoli#
Příkazy se registrují přes REST API, ne přes gateway, a zůstávají uložené na straně Discordu. To znamená, že registrace je krok nasazení, ne krok startu.
import { REST, Routes } from "discord.js";const commands = [ { name: "ping", description: "Check that the bot is alive" },];const rest = new REST().setToken(process.env.DISCORD_TOKEN);await rest.put( Routes.applicationGuildCommands(process.env.APP_ID, process.env.GUILD_ID), { body: commands },);applicationGuildCommands registruje do jednoho serveru a projeví se okamžitě, což chceš při vývoji. applicationCommands registruje globálně; Discord v dokumentaci uvádí, že globální příkazy se šíří až hodinu, takže příkaz, který chybí hned po globální registraci, je obvykle jen předčasný. PUT nahradí celou sadu, takže cokoli, co v poli chybí, se smaže - tak odstraníš starý příkaz, ale také tak si lidé omylem vymažou seznam příkazů registrací neúplného pole.
Neprováděj registraci při každém startu. Discord omezuje, kolik příkazů lze za den vytvořit, a bot, který se registruje při bootu a zároveň uvízl ve smyčce restartů, se může zablokovat do konce dne. Spouštěj ji jako samostatný příkaz, když se změní definice příkazů, což je u většiny botů zřídka.
Pokud registrace selže s Invalid Form Body a kódem chyby 50035, přečti si cestu k poli ve zprávě. Názvy příkazů musí být malými písmeny, 1-32 znaků, bez mezer; popisy mají 1-100 znaků a u chat input příkazů jsou povinné. Chyba je přesná, jakmile víš, že ukazuje na konkrétní volbu.
Nasazení z GitHubu#
Nahrávání zipu je cesta k serveru, jehož obsah nikdo nedokáže identifikovat. Připoj místo toho repozitář.
Na RE:NODE je integrace Gitu pouze s GitHubem, přes GitHub App, která vydává krátkodobé tokeny, místo aby po tobě chtěla vložit osobní přístupový token, který nikdy nevyprší, takže soukromé repozitáře fungují bez přihlašovacích údajů, které by na serveru ležely navěky. Existují dva nezávislé přepínače: stáhnout větev při každém startu kontejneru a nasadit při push, což restartuje server, když GitHub ohlásí push do dané větve - a jen pokud už běžel, takže server, který jsi záměrně zastavil, zůstane zastavený. Každé nasazení se zaznamená jako jeden řádek, otevřený při příchodu pushe a uzavřený ve chvíli, kdy je kontejner znovu vidět běžet, podle čehož poznáš nasazení, které proběhlo, od toho, které spadlo. Postup najdeš v článku nasazení aplikace v Node.js z GitHubu.
Nasazení stojí krátkou mezeru: proces se zastaví, proběhne pull a instalace a bot se znovu připojí, typicky za pět až dvacet sekund. Během toho okna každá interakce poslaná botovi selže. S jedním procesem se tomu nedá vyhnout, takže nasazuj, když tví uživatelé spí, a drž instalaci rychlou - nasazení bez výpadku na serveru, který má od všeho jen jedno upřímně říká, které techniky se na bota na gateway hodí.
Pokud na botovi záleží, dej mu stagingové dvojče: druhou aplikaci v Developer Portal s vlastním tokenem, pozvanou jen na tvůj testovací server. Vzor najdeš v článku Staging a produkce na jednom účtu.
Tajemství, stav a soubory, které bot zapisuje#
Proměnné prostředí patří na záložku Startup, ne do repozitáře. DISCORD_TOKEN, ID aplikace, API klíče, URL databáze. Přidej .env do .gitignore hned první den a hodnoty čti tak, aby chybějící hodnota způsobila tvrdé selhání:
const token = process.env.DISCORD_TOKEN;if (!token) throw new Error("DISCORD_TOKEN is not set");Selhat při startu s jasnou zprávou je mnohem lepší než se připojit s undefined a v konzoli číst An invalid token was provided.
Stav je druhá polovina. Cokoli, co bot zapisuje do adresáře svého repozitáře, překáží příštímu pullu, takže runtime data drž tam, kam nasazení nesahá: v adresáři mimo sledovaný strom, nebo v databázi. Soubor SQLite je správná odpověď pro překvapivě mnoho botů - jeden soubor, žádný server, a objem zápisů bota zvládne, aniž by si toho všiml. Na síťovou databázi přejdi, až zapisuje víc než jeden proces, nebo když se data přestanou pohodlně vejít do paměti. Tarify aplikací zahrnují dva sloty databáze vytvářené v panelu s vygenerovaným hostem, uživatelem a heslem a na stránce hosting databází jsou samostatné řady pro případ, že úložiště bota přeroste; connection pooly a limity pokrývají chybu, kterou dělá většina botů jako první, totiž otevírání spojení pro každý příkaz.
Ať používáš cokoli, zálohuj podle plánu, ne když si vzpomeneš. Sloty pro backupy jsou součástí každého tarifu aplikací a záložka Schedules je spouští podle výrazu cron. Backup, který nikdo neobnovil, je hypotéza: backupy, které se opravdu obnoví to vysvětluje pořádně.
Zůstat online: pády, smyčky a limit paměti#
Běžícího bota shodí tři věci a všechny se dají předejít.
Neošetřený rejection ukončí proces. V současném Node neošetřené odmítnutí promise ve výchozím stavu proces ukončí. Stačí jeden neúspěšný fetch v handleru příkazu bez catch. Nainstaluj handlery a zařiď, aby se řádek logu zapsal dřív, než cokoli skončí:
process.on("unhandledRejection", (error) => { console.error("unhandled rejection:", error);});process.on("SIGTERM", async () => { await client.destroy(); process.exit(0);});Zavření klienta při SIGTERM záleží víc, než se zdá. Bot zabitý bez zavření socketu zůstává až minutu viditelný jako online, takže desetisekundový restart vypadá pro uživatele jako minuta, kdy je bot ignoruje. Obecný vzor najdeš v článku graceful shutdown a health checky.
Limit paměti není elegantní událost. Na limitu kontejneru jádro proces zastaví a ten se restartuje načisto místo toho, aby ho nechalo swapovat. Pro bota je to správné výchozí chování, ale znamená to, že neomezená cache nebo rostoucí Map se projeví jako záhadný restart každých pár hodin, ne jako pomalý úpadek. Po každé změně sleduj graf paměti v konzoli celý den; proč vaše aplikace v Node umírá na 2 GB u tarifu se 4 GB vysvětluje druhý strop, haldu V8, o který se proces Node obvykle opře jako o první.
Smyčky restartů si všimnou. Na RE:NODE hlídač každé dvě minuty kontroluje, zda server nespadl offline nebo zda jeho uptime nejde pozpátku, a restarty, které jsi vyžádal, ignoruje. Tři neočekávané restarty za hodinu dají na stránku serveru varování a otevřou ticket; šest vede k pozastavení. Není to svévole: bot restartující se každých dvacet sekund zahlcuje gateway a spaluje limit identifikací popsaný výše. Diagnostiku smyčky pokrývá článek Proč se tvůj herní server pořád restartuje.
Co se týká monitoringu, pamatuj, že bot nemá žádný endpoint, který by šlo dotazovat. Buď čti konzoli, nebo na přidělený port tarifu připoj malý HTTP server a nasměruj na něj uptime monitor přes slot proxy. Monitoring, který ti něco řekne je o tom, jak ten seznam udržet krátký.
Řešení potíží#
Bot je online, ale nemá příkazy. Buď v pozvánce chyběl scope applications.commands, nebo byly příkazy registrovány globálně a ještě se nerozšířily. Zaregistruj je do testovací guildy a ověříš to za pár sekund.
„The application did not respond“. Tvůj handler neodpověděl do tří sekund. Buď vyhodil chybu, nebo dělá skutečnou práci. Nejdřív zavolej interaction.deferReply(), což okno prodlouží na patnáct minut, a pak editReply(), až budeš mít odpověď.
Kód uzavření 4004 ve smyčce. Špatný token. Obvykle resetovaný token, který se nikdy nenasadil, překlep v názvu proměnné, nebo vložený client secret místo tokenu bota.
Kód uzavření 4014 ve smyčce. Privilegovaný intent, který není zapnutý v Developer Portal.
`Missing Permissions` (50013) nebo `Missing Access` (50001). Nejčastěji hierarchie rolí. Bot nemůže moderovat člena, jehož nejvyšší role je nad rolí bota, bez ohledu na oprávnění, a nemůže psát do kanálu, kde mu to přepsání kanálu zakazuje.
HTTP 429 a pak nic. Narazil jsi na rate limit; odpověď nese retry_after a knihovna počká. Trvalé záplavy odmítnutých požadavků jsou horší než pomalé, protože hraniční síť Discordu blokuje adresy, které v krátkém okně posílají mnoho neplatných požadavků. Oprav smyčku místo toho, abys zkoušel silněji - obecný obraz najdeš v článku rate limity a zneužití.
FAQ#
Mohu hostovat Discord bota zdarma?
Můžeš ho pustit na starém počítači doma a bude offline pokaždé, když ten stroj usne, aktualizuje se nebo má nespolehlivé připojení. Bezplatné tarify aplikací obvykle zastaví proces, který nedostává HTTP provoz, což je přesně to, jak vypadá bot na gateway. Bezplatný tarif tu není; nejmenší tarif aplikací má 1 GB s 0,5 vCPU od $4 měsíčně, což je víc, než příkazový bot potřebuje.
Potřebuje Discord bot otevřený port nebo doménu?
Ne. Spojení k Discordu je odchozí, takže bot nepotřebuje příchozí port ani certifikát. Doménu potřebuješ, jen když přidáš dashboard nebo OAuth callback, a v takovém případě se o jméno hostitele i certifikát postará slot proxy v tarifu aplikací.
Proč se můj bot zobrazuje jako offline, když proces pořád běží?
Protože Discord vidí spojení ke gateway, ne proces. Zablokovaná smyčka událostí - dlouhý synchronní cyklus, parsování obřího JSONu, synchronní čtení souboru - zastaví odesílání heartbeatů, Discord socket zavře a bot se tváří offline, zatímco kontejner ukazuje naprosto zdravé CPU i paměť. Pomalou práci přesuň z hlavní cesty.
Musím slash příkazy registrovat znovu při každém nasazení?
Ne, a neměl bys. Registrace žijí na straně Discordu, dokud je nezměníš. Registrační skript spusť, když se změní definice příkazů, a nedávej ho do startovacího příkazu.
Jak přenesu bota z PC na server?
Nahraj kód na GitHub bez tokenu, vytvoř app server, připoj repozitář, nastav proměnné prostředí na záložce Startup, nastav startovací příkaz a spusť ho. Potom zastav kopii na svém PC - dva procesy na jednom tokenu se perou o stejnou relaci a vytvářejí velmi zmatečné chování.
Co se stane, když bot narazí na limit paměti?
Kontejner se zastaví a restartuje načisto místo swapování. Přijdeš o všechno, co bylo jen v paměti, takže důležité věci ukládej trvale. Pokud se to opakuje, omez cache knihovny a vlastní kolekce dřív, než koupíš víc paměti; kdy upgradovat tarif je právě o rozlišení těchto dvou případů.




Komentáře
Zcela anonymně: žádný účet, žádný e-mail, žádné cookie. Ukládáme jen jméno, které napíšeš, text a čas - nic dalšího. Počet odkazů je omezený a značkování se nevykresluje.