RE:NODE

Provoz13 min čtení

Discord webhooky pro upozornění herního serveru

Vytvoř webhook, pošli embed, upravuj jednu stavovou zprávu místo spamu v kanálu, zvládni chyby 429 a poznej, kdy už potřebuješ opravdového bota.

11 čtenářů

Discord webhook je URL adresa, která změní HTTP POST na zprávu v jednom kanálu. Žádný bot, žádný token, žádné spojení s gateway ani přihlášení: URL vytvoříš v nastavení kanálu, pošleš na ni pomocí POST kousek JSONu a zpráva se objeví. Pro stav serveru, upozornění na restart, potvrzení záloh a hlášení „Minecraft server je zase online“ je to celý mechanismus a nastavení zabere asi čtyři minuty.

Zajímavé jsou ale věci, ve kterých lidé později chybují. Webhook umí jen psát, takže cokoli, co potřebuje číst chat nebo odpovídat na příkaz, vyžaduje opravdového bota. Zpráva každou minutu zaplní kanál šumem, který nikdo nečte; úprava jedné a téže zprávy drží jeden živý stavový řádek. Discord webhooky omezuje a vrací 429 s přesným počtem sekund, které máš počkat, což většina domácích skriptů ignoruje. A ta URL je přístupový údaj - kdokoli ji má, může psát jako tvůj server, dokud webhook nesmažeš.

Co webhook je a co neumí#

Webhook patří kanálu, ne tobě. Má id a token a dohromady tvoří celou URL:

code
https://discord.com/api/webhooks/1234567890123456789/aBcD...token

Žádné další ověření neexistuje. Ta URL je přístupový údaj a zároveň jediný. Kdokoli ji má, může do toho kanálu psát jako ten webhook, s libovolným jménem a avatarem, dokud někdo webhook nesmaže.

Co webhook umí:

  • Poslat zprávu do svého jediného kanálu - s content, embedy, soubory, nebo vším dohromady.
  • Přepsat zobrazované jméno a avatar u každé zprávy, takže jeden webhook může psát jako „Survival“ i jako „Lobby“.
  • Psát do vlákna v tomto kanálu pomocí parametru dotazu thread_id.
  • Upravovat a mazat zprávy, které sám dříve poslal.

Co neumí:

  • Nic číst. Ani chat, ani reakce, ani seznam členů.
  • Reagovat na slash příkaz, tlačítko nebo zmínku.
  • Psát kamkoli jinam než do svého kanálu.
  • Přidělovat role, kickovat, banovat ani dělat cokoli, co dělá administrátor.

Ten seznam je celé rozhodnutí. Jednosměrná upozornění jsou webhook. Cokoli obousměrného je bot s tokenem, spojením s gateway a nastavením intents, tedy jiný software s jinými nároky na hosting - hostování Discord bota 24/7 pokrývá tuto stranu a výběr tarifu pro Discord bota říká, kolik stojí udržet ho online.

Vytvoření a první zpráva#

V Discordu: klikni pravým tlačítkem na kanál, Edit Channel, Integrations, Webhooks, New Webhook. Pojmenuj ho po serveru, o kterém bude hlásit, ne „Webhook 1“, protože tohle jméno se ve výchozím stavu zobrazí jako autor zprávy. Zkopíruj URL. Kanál může mít až 15 webhooků, což stačí, aby měl každý server svůj.

Nejjednodušší možná zpráva:

bash
$ curl -X POST "$WEBHOOK_URL" \    -H "Content-Type: application/json" \    -d '{"content": "Survival server is back up."}'

Úspěšné odeslání vrátí 204 No Content a prázdné tělo. Pokud chceš zprávu zpět - konkrétně její id, abys ji mohl později upravit - přidej ?wait=true a dostaneš 200 s celým objektem zprávy:

bash
$ curl -X POST "$WEBHOOK_URL?wait=true" \    -H "Content-Type: application/json" \    -d '{"content": "Restarting for updates in 5 minutes."}'

Dva příznaky stojí za to nastavit téměř u každé automatické zprávy. allowed_mentions s prázdným polem parse zabrání tomu, aby řádek logu, který náhodou obsahuje @everyone, pingnul šest set lidí - klasická nehoda u propojení konzole. A flags: 4096 potlačí náhledy odkazů, takže zpráva s URL nevtáhne do kanálu nechtěnou kartu.

json
{  "username": "Survival",  "content": "Backup finished: 412 MB in 38s",  "allowed_mentions": { "parse": [] },  "flags": 4096}

Embedy: JSON, díky kterému je to čitelné#

Prostý content stačí pro jednořádková upozornění. Pro stavový panel chceš embed: orámovanou kartu s barevným pruhem, nadpisem, poli rozloženými do sloupců a časovým razítkem. Struktura je pevná a limity jsou skutečné.

json
{  "username": "Valheim",  "embeds": [    {      "title": "Longship Crew",      "description": "Online - world saved 4 minutes ago",      "color": 3066993,      "fields": [        { "name": "Players", "value": "3 / 10", "inline": true },        { "name": "Version", "value": "0.220.5", "inline": true },        { "name": "Uptime", "value": "6d 4h", "inline": true }      ],      "footer": { "text": "checked every 60s" },      "timestamp": "2026-09-21T14:03:00.000Z"    }  ],  "allowed_mentions": { "parse": [] }}

Na čem se lidé nachytají:

  • `color` je desetinné celé číslo, ne hexadecimální řetězec. 0x2ECC71 je 3066993. Řetězec je tady 400.
  • `timestamp` musí být ve formátu ISO 8601. Discord ho každému divákovi zobrazí v jeho místním čase, což je nejlepší důvod používat pro stavovou zprávu embed - nikdo nemusí zjišťovat, v jakém časovém pásmu jsi.
  • `inline: true` řadí pole vedle sebe, na desktopu po třech v řádku, na mobilu méně. Míchání inline a ne-inline polí dává rozvržení, které jsi nechtěl, proto vol jedno pro každou sekci.

Pevné limity, které vracejí 400 místo ořezání:

PoleLimit
content2000 znaků
embeds na zprávu10
title256 znaků
description4096 znaků
fields25
name / value pole256 / 1024 znaků
footer.text2048 znaků
Veškerý text ve všech embedech6000 znaků

Celkový limit 6000 znaků je ten, který kousne propojení konzole. Dlouhý stack trace vložený do popisu ho při běžném provozu tiše překročí a pak selže právě při incidentu, kvůli kterému jsi propojení stavěl. Ořezávej na pevnou délku na své straně a plný text ukládej tam, kde se plný text drží - viz logy, které stojí za uchování.

Existují dvě zkratky kompatibility a jsou opravdu užitečné. Přidáním /slack k URL webhooku přijme payload ve formátu Slacku a /github payload ve formátu GitHubu. To znamená, že nástroj, který umí mluvit jen se Slackem - monitorovací systém, CI runner - může psát do Discordu bez jakéhokoli adaptéru. Nasměruj ho na https://discord.com/api/webhooks/ID/TOKEN/slack a funguje to.

Jedna upravovaná zpráva místo stovky příspěvků#

Výchozí návrh stavového webhooku je posílat zprávu každou minutu. Za den je to 1 440 zpráv, kanál je nečitelný a každý, kdo má zapnutá oznámení, ho ztlumil. Lepší návrh je jedna zpráva upravovaná na místě.

Pošli ji jednou s ?wait=true, ulož si vrácené id a pak ji upravuj pomocí PATCH:

bash
# First run: create and remember the id$ MESSAGE_ID=$(curl -s -X POST "$WEBHOOK_URL?wait=true" \    -H "Content-Type: application/json" \    -d @status.json | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])")# Every run after that: edit the same message$ curl -s -X PATCH "$WEBHOOK_URL/messages/$MESSAGE_ID" \    -H "Content-Type: application/json" \    -d @status.json

Id zprávy ulož někam, kde přežije restart - stačí soubor vedle skriptu. Pokud id chybí nebo byla zpráva smazána, PATCH vrátí 404 a ty pošleš novou. To je celý stavový automat:

  1. Načti uložené id zprávy. Pokud žádné není, jdi na krok 4.
  2. Pomocí PATCH uprav zprávu novým stavem.
  3. Pokud je odpověď 404, zapomeň id a jdi na krok 4. Jinak skonči.
  4. Pošli POST s ?wait=true, ulož vrácené id a skonči.

Upravená zpráva nikoho neupozorní, což je přesně správně pro stavový řádek, který se mění každou minutu. Druhý webhook, v jiném kanálu, si nech pro události, které upozornit mají: server spadl, záloha selhala, disk je skoro plný. Úpravy pro stav, příspěvky pro události - to je rozdíl, díky kterému lidé nechávají kanál nezatlumený.

UDP or TCPPOST or PATCHedit, no pingonly on changePollerapp tarif, každých 60 sQuery portA2S nebo ping serveruURL webhookuid a tokenHerní serverkonzole, logyStavová zprávaupravená na místěKanál upozorněnínový příspěvek, ping
Jak se stavová zpráva dostane do Discordu

Rate limity, 429 a opakování, které musíš respektovat#

Discord omezuje každou routu. U webhooku jsou praktická čísla zhruba pět požadavků za dvě sekundy na webhook a kolem třiceti zpráv za minutu do jednoho kanálu. Ta čísla nejsou závazná a Discord je mění, takže je nezakódovávej jako pevný sleep a nepovažuj to za vyřešené. Místo toho zpracuj odpověď.

Když limit překročíš, dostaneš 429 s JSON tělem a hlavičkami:

code
HTTP/1.1 429 Too Many RequestsX-RateLimit-Limit: 5X-RateLimit-Remaining: 0X-RateLimit-Reset-After: 0.529Retry-After: 1{"message": "You are being rate limited.", "retry_after": 0.529, "global": false}

retry_after v těle jsou sekundy jako desetinné číslo a je to směrodatná hodnota. Počkej tak dlouho a zkus to jednou znovu. Pokud je global rovno true, narazil jsi na limit celého účtu - 50 požadavků za sekundu - a máš větší problém než tuhle jednu zprávu.

Tři pravidla, která webhook trvale udrží mimo potíže:

  • Nikdy neopakuj v těsné smyčce. Skript, který při 429 okamžitě zkouší znovu, změní půlsekundové zpoždění v trvalou záplavu a může vést k deaktivaci webhooku.
  • Slučuj. Deset řádků logu v jedné zprávě odděleně novými řádky je jeden požadavek. Deset zpráv je deset. Propojení konzole by mělo vteřinu či dvě sbírat a poslat dávku.
  • Zpomal i při `5xx`. Discord pod zátěží vrací 500 a 502. Použij exponenciální backoff s horní mezí, pak to vzdej a loguj lokálně. Monitoring, který spadne, protože jeho kanál upozornění je vytížený, je horší než žádný monitoring.

Odkud se stav bere: dotazování herního serveru#

Webhook je ta snadná půlka. Něco musí vědět, kolik hráčů je online, a to něco je dotaz na samotný herní server.

Skupina herProtokolKde
Minecraft JavaServer List Ping, TCPHerní port, obvykle 25565
Source a GoldSrcA2S_INFO, UDPQuery port
ValheimSteam query, UDPHerní port plus jedna, výchozí 2457
Většina her na Unreal a UnityA2S nebo REST endpointRůzné, zkontroluj hru
Cokoli s RCONRCON, TCPPort RCON

Knihovny existují pro všechny tyto případy a psát vlastní parser A2S není dobré využití večera. Důležitější je, kde poller běží. Nemůže smysluplně běžet na herním serveru, který sleduje, protože zajímavý případ je, když je ten server mimo provoz. Musí to být samostatný, malý, trvale běžící proces.

Naplánovaná úloha v tarifu herního serveru spouští příkazy konzole, zálohy a akce napájení - HTTP požadavek poslat neumí. Poller proto patří tam, kde běží tvůj vlastní kód: tarif aplikace pro Node nebo Python, VDS nebo stroj, který už máš. Na RE:NODE začínají tarify aplikací na $4 měsíčně a dostaneš stejný panel, konzoli a deploy z GitHubu, což stačí pro padesátiřádkový skript na časovači. Aktuální tarify najdeš na stránce hostingu aplikací a v článku výběr tarifu pro Discord bota, jak malé „malé“ může být bezpečně.

Tvar skriptu je v každém jazyce stejný:

python
import json, os, urllib.requestdef post(payload, url=os.environ["WEBHOOK_URL"]):    body = json.dumps(payload).encode()    req = urllib.request.Request(        url, data=body, headers={"Content-Type": "application/json"}    )    with urllib.request.urlopen(req) as res:        return res.status

Dotazuj se v pevném intervalu, porovnej výsledek s předchozím, stavovou zprávu uprav pokaždé a do kanálu upozornění piš jen tehdy, když se stav změnil. Právě ta poslední podmínka dělí užitečný kanál od ztlumeného. Monitoring, který ti něco řekne je delší argumentace pro to, aby se stavěl takhle.

Propojení konzole, pluginy a kdy potřebuješ opravdového bota#

Většina lidí nic z výše uvedeného nepíše, protože plugin už existuje.

  • Minecraft. DiscordSRV je zavedené propojení chatu. Pro svou hlavní funkci používá token bota místo webhooku - protože potřebuje číst zprávy z Discordu a vkládat je do herního chatu, což webhook nezvládne - a uvnitř pak používá webhooky, aby se hráči ze hry zobrazovali se svými jmény a skiny. Proto potřebuje obojí.
  • FiveM. Resources, které posílají připojení, odchody, bany a akce adminů do webhooku, jsou standard a txAdmin umí hlásit do Discordu. FiveM a txAdmin popisuje nastavení.
  • Hry na Source. Pluginy pro SourceMod posílají výsledky zápasů a akce adminů do webhooku.
  • Cokoli jiného. Sleduj konec souboru logu a posílej řádky odpovídající vzoru. Je to univerzální záloha, funguje u každé hry a je to asi dvacet řádků kódu.

U propojení buď selektivní. Celý výstup konzole v Discordu je do dne nečitelný a nepřetržitě spotřebovává rate limit. Vyber události, kvůli kterým bys vstal: server se zastavil, řádek o pádu, počet hráčů překročil práh, záloha selhala. Pokud se tvůj server restartuje tak často, že je to hlučné, problém není upozornění - problém řeší článek proč se tvůj herní server pořád restartuje.

Jakmile budeš chtít cokoli z následujícího, potřebuješ bota, ne webhook: příkaz /status, tlačítko, které server restartuje, čtení chatu z Discordu do hry, přidělení role, když si někdo propojí účet, nebo reakci na zprávu. To je plnohodnotná aplikace s tokenem, který musíš chránit, a procesem, který musíš udržet naživu.

Ber URL jako heslo a posílej upozornění, která stojí za přečtení#

URL webhooku je přístupový údaj předkladatele bez expirace a bez rozsahu mimo kanál. Zacházej s ní podle toho.

  • Nikdy ji necommituj. Dej ji do proměnné prostředí, načti ji při startu a drž ji mimo repozitář. Záložka Startup v panelu drží proměnné prostředí přesně pro tohle. Proměnné prostředí a tajné údaje pokrývá obecný případ.
  • Nikdy ji nevkládej do zprávy na Discordu, do snímku obrazovky, pastebinu ani do tiketu podpory. Pokud potřebuješ, aby se někdo podíval na tvou konfiguraci, zakryj tokenovou polovinu URL.
  • Rotuj smazáním. Reset hesla u webhooku neexistuje. Pokud unikne, smaž ho v nastavení kanálu a vytvoř nový; stará URL přestane fungovat okamžitě.
  • Jeden webhook na účel. Oddělené webhooky pro stav, upozornění a propojení konzole znamenají, že únik nebo chyba postihne jen jeden kanál, a na první pohled vidíš, který skript je hlučný.

Pak buď nemilosrdný v tom, co posíláš. Selhání upozorňování není zmeškané upozornění; je to posílání tolika upozornění, že se lidé přestanou dívat. Kanál, který píše jednou týdně, se čte. Kanál, který píše každých deset minut, je do dvou týdnů ztlumený a bude ztlumený i v noci, kdy se něco opravdu rozbije.

Dobrá výchozí sada pro herní server: server se neočekávaně zastavil, server se vrátil, záloha selhala, disk nad 85 procent, počet hráčů překročil práh, na kterém ti záleží, a byla nasazena aktualizace. To je šest typů událostí a většina z nich se spouští zřídka. Všechno ostatní může žít v upravované stavové zprávě, kde se mění potichu, nebo v logu, kam se můžeš podívat, když k tomu máš důvod.

FAQ#

Potřebuju token bota, abych mohl používat webhook?

Ne. URL webhooku obsahuje vlastní token a nic dalšího nepotřebuje. Token bota potřebuješ jen tehdy, když něco musí z Discordu číst - obousměrné propojení chatu, slash příkazy, tlačítka nebo správu rolí.

Jak často můžu do webhooku posílat?

Zhruba pět požadavků za dvě sekundy na webhook a asi třicet zpráv za minutu do kanálu, ale ber to jako orientaci. Spolehlivý postup je zpracovat odpověď 429 a počkat tolik, kolik říká hodnota retry_after.

Můžu zprávu aktualizovat místo posílání nové?

Ano. Pošli ji s ?wait=true, ulož id zprávy z odpovědi a pak pošli PATCH na URL webhooku s připojeným /messages/{id}. Úprava nikoho neupozorní, takže je ideální pro živý stavový řádek.

Proč můj webhook pinguje všechny?

Protože tělo zprávy obsahovalo @everyone nebo zmínku role a nic to nepotlačilo. U každé automatické zprávy posílej "allowed_mentions": {"parse": []}, zvlášť u všeho, co předává chat nebo text z logů.

Kde má běžet poller stavu?

Ne na serveru, který sleduje, protože ho potřebuješ funkční, když ten server neběží. Správný je malý tarif aplikace, VDS nebo jakýkoli trvale zapnutý stroj. Naplánovaná úloha v tarifu herního serveru umí příkazy konzole a zálohy, ale HTTP požadavek poslat neumí.

Můj webhook přestal fungovat. Co se stalo?

Buď byl smazán v nastavení kanálu, byl smazán kanál, nebo byla URL znovu vygenerována. Žádná expirace ani trvalý ban za rate limit neexistuje. Zkontroluj, že webhook stále existuje, a pak že posíláš celou URL včetně tokenu.


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.

0/2000