Externer Handler
Der externe Handler ist eine Brücke zwischen Ihrem Code und Telegram. Wenn die Logik des Baukastens nicht ausreicht, übergeben Sie eine einzelne Reaktion an Ihren Server: Er empfängt Ereignisse und antwortet mit Befehlen im Format der Telegram Bot API, während GetMyBot Transport, Zustellung und die gesamte Telegram-Anbindung übernimmt.
Was das ist
Reaktionen in GetMyBot werden visuell zusammengestellt: Trigger → Bedingungen → Aktionen. Komplexe oder ungewöhnliche Logik — eigene Datenbank, Berechnungen, Aufrufe externer Systeme, ML, verzweigte Szenarien — lässt sich im Baukasten nur schwer abbilden. Der externe Handler hebt diese Grenze auf: Sie binden Ihr eigenes Backend an und übergeben ihm ganze Reaktionen zur Verarbeitung.
Der externe Dienst erhält von GetMyBot Ereignisse (Trigger ausgelöst, Nutzer hat Text gesendet, Schaltfläche gedrückt) und weist den Bot an, was er senden soll. GetMyBot führt die Befehle mit seinem Bot-Token aus — darüber laufen Rate-Limiting, Wiederholungen, Deduplizierung und das Dialog-Protokoll.
Betriebsmodell
- GetMyBot ist der WebSocket-Server. Ihr Dienst verbindet sich selbst mit ihm und benötigt keine öffentliche Adresse oder einen Webhook-Endpunkt. GetMyBot stellt keine ausgehenden Verbindungen zu Ihrem Dienst her, daher gibt es keine SSRF-Angriffsfläche.
- Authentifizierung per Integrations-Token. Der Dienst präsentiert das Token beim Verbindungsaufbau; GetMyBot prüft es gegen den Hash und hält die Verbindung aufrecht.
- Ereignisse und Befehle im Format der Telegram Bot API. Der Dienst empfängt ein Ereignis und antwortet mit einem Array von Aufrufen der Form „Methode + Parameter", genau wie beim Arbeiten mit einem gewöhnlichen Bot.
- Befehle werden von GetMyBot mit seinem Token ausgeführt. Der Dienst kommuniziert niemals direkt mit Telegram: Befehle werden validiert und in den internen Outbox von GetMyBot gelegt, von wo sie mit dem Bot-Token gesendet werden — mit Rate-Limiting, Wiederholungen, Deduplizierung und Protokollierung.
Eine Verbindung bedient alle Dialoge einer Integration — Ereignisse verschiedener Nutzer werden gemultiplext und anhand des Feldes session_id unterschieden.
Integration konfigurieren
- Öffnen Sie den Abschnitt Integrationen im linken Menü und klicken Sie auf Integration hinzufügen.
- Wählen Sie den Dienst Externer Handler und vergeben Sie einen Namen.
- Nach dem Erstellen zeigt die Karte:
- WebSocket-Adresse — in der Form
wss://api.mybot.app/ext/ws/<integrationID>, wobei<integrationID>die UUID dieser Integration ist. Über diese Adresse verbindet sich Ihr Dienst. Ohne ID im Pfad wird die Verbindung nicht hergestellt. - Verbindungs-Token — wird beim Erstellen einmalig angezeigt. Kopieren und sichern Sie ihn; das Token kann nicht erneut eingesehen werden (es wird nur als Hash gespeichert).
- Token erneuern — die Schaltfläche generiert ein neues Token und macht das alte sofort ungültig. Aktualisieren Sie das Token nach der Erneuerung in Ihrem Dienst.
- Online-Indikator — zeigt an, ob Ihr Dienst gerade eine aktive Verbindung hält.
- WebSocket-Adresse — in der Form
Zusätzliche Integrationsparameter, die das Protokoll beeinflussen:
session_ttl_seconds— Lebensdauer der Sitzung in Sekunden (Standard:3600).on_unavailable_reaction_id— Fallback-Reaktion, die gestartet wird, wenn der Dienst beim Auslösen offline ist (siehe Zuverlässigkeit).
Verknüpfung mit einer Reaktion
Der externe Handler wird nicht als separater Trigger, sondern als Aktion eingebunden. Fügen Sie im Reaktions-Editor die Aktion An externen Handler übergeben hinzu und wählen Sie die gewünschte Integration.
Einen Dialog kann jeder bestehende GetMyBot-Trigger öffnen — Befehl, Text, Schaltflächenklick, Parameter aus einem Link, eingehende Web-Anfrage, Zeitplan. Wenn ein solcher Trigger ausgelöst wird und die Aktion erreicht, öffnet GetMyBot eine Proxy-Sitzung und sendet dem Dienst das Ereignis session.open.
Ist der Dienst in diesem Moment nicht verbunden, startet GetMyBot die Fallback-Reaktion aus der Integrationseinstellung (on_unavailable_reaction_id). Ist keine Fallback-Reaktion festgelegt, geschieht nichts (still, ohne Fehlermeldung an den Nutzer).
Verbindung und Autorisierung
Endpunkt: GET /ext/ws/{integrationID} über WebSocket (wss). integrationID — UUID der Integration vom Typ „Externer Handler". Maximale Framegröße: 256 KiB.
Die Autorisierung erfolgt auf eine von zwei Arten.
1. Header beim Handshake (empfohlen). Übergeben Sie das Token im Header Authorization:
Authorization: Bearer <token>
2. Auth-Frame. Wenn kein Bearer-Header übermittelt wird, senden Sie innerhalb von 10 Sekunden nach dem Verbindungsaufbau folgenden Frame:
{ "type": "auth", "token": "<token>" }
Wenn der Auth-Frame innerhalb von 10 Sekunden ausbleibt, wird die Verbindung mit Code 4401 geschlossen. Ein falsches Token führt bei beiden Methoden ebenfalls zu close 4401.
Das Token wird beim Erstellen der Integration generiert, einmalig angezeigt und nur als Hash (sha256) gespeichert; der Vergleich erfolgt in konstanter Zeit. Token-Rotation:
POST /api/bots/{botID}/integrations/{integrationID}/rotate-token
Der Endpunkt gibt das neue Klartext-Token zurück; das alte hört sofort auf zu funktionieren.
Ereignisse: Bot → Dienst
Jedes Ereignis ist ein JSON-Frame (EventEnvelope). Das Feld type ist eines von: session.open, message, callback, session.cancel, session.expired.
Vollständiger Satz der Envelope-Felder:
{
"type": "session.open",
"session_id": "string",
"bot_id": "string",
"user": {
"tg_user_id": 123456789,
"first_name": "Max",
"username": "max",
"params": { "utm": "promo" }
},
"chat": { "id": 123456789, "type": "private" },
"update": { "...": "raw Telegram Update (raw JSON)" },
"event": { "...": "normalisiertes GetMyBot-Ereignis (raw JSON)" }
}
session_id— Sitzungskennung (Dialog).bot_id— Bot-Kennung.user— Nutzerdaten:tg_user_id(int64), optionalefirst_name,usernamesowieparams— Nutzerparameter (u. a. aus dem Link), Map Schlüssel→Wert.chat— Chat:id(int64) undtype(z. B."private").update— roher Telegram-Update (raw JSON). Vorhanden beisession.open,message,callback.event— normalisiertes GetMyBot-Ereignis (raw JSON).
Der Inhalt unterscheidet sich je nach Typ:
session.open— Trigger ausgelöst, neuer Dialog geöffnet. Enthältupdate(roher Update) undevent(vollständiges normalisiertes Ereignis),user.paramssind befüllt.message— trifft ein, wenn der Dienst eine „Erwartung" hält (expect=text/any) und der Nutzer eine Nachricht gesendet hat. Inhalt wie beisession.open.callback— der Nutzer hat eine Schaltfläche gedrückt, die der Dienst zuvor gesendet hat.updateist roh; inuserist nurtg_user_idbefüllt;evententhält nur den ursprünglichen callback_data, den der Dienst in der Schaltfläche angegeben hat:
{ "callback_data": "<ursprünglicher Wert, den der Dienst in der Schaltfläche angegeben hat>" }
session.cancel/session.expired— minimaler Envelope:session_id,bot_id,user.tg_user_id,chat.id(beicancelzusätzlichchat.type). Die Felderupdate/eventfehlen. Diese Ereignisse werden nur zugestellt, wenn der Dienst online ist — sie werden nicht in die Offline-Warteschlange gelegt.
Befehle: Dienst → Bot
Ein Befehl ist ein JSON-Frame (CommandEnvelope). Das Feld type ist eines von: execute, session.close, auth, ping, pong.
{
"type": "execute",
"session_id": "string",
"methods": [
{ "method": "sendMessage", "params": { "text": "Hallo" } }
],
"expect": "none"
}
session_id— Sitzung, zu der der Befehl gehört.token— nur fürtype: "auth"(siehe Verbindung und Autorisierung).methods— Array von Objekten{ "method": "<Name der Telegram Bot API-Methode>", "params": { ... } }. Der Methodenname entspricht genau der Telegram Bot API (z. B.sendMessage),paramsist das Parameterobjekt dieser Methode.expect— ob der Dienst auf eine Nutzerantwort wartet:"none","text"oder"any"(ein leerer Wert wird alsnoneinterpretiert).session.close— Dialog explizit schließen.
Befehle gehen nicht direkt an Telegram: GetMyBot validiert sie und legt sie in seinen Outbox, von wo sie mit dem Bot-Token gesendet werden (mit Rate-Limiting, Wiederholungen, Deduplizierung und Protokollierung).
Verarbeitung von execute
Der Befehl execute wird vollständig verworfen, wenn: session_id leer ist; die Sitzung nicht gefunden oder nicht im Status open ist; die Sitzung zu einer anderen Integration gehört; die Sitzung abgelaufen ist.
Danach gilt:
- Rate-Limit: bis zu 60
executepro Minute pro Sitzung (gleitendes Fenster). Anfragen über dem Limit werden verworfen. - Bis zu 30 Methoden in einem
execute; überzählige werden still verworfen. - Für jede Methode wird eine Whitelist geprüft (siehe Erlaubte Methoden) — eine Methode, die nicht auf der Liste steht, wird abgelehnt.
- Chat-Guard: Wenn
paramseinchat_idoderfrom_chat_idmit einem Wert enthält, der nicht0und nicht gleich derchat_idder Sitzung ist, wird die gesamte Methode abgelehnt (kein Senden in fremde Chats).chat_idmuss gar nicht angegeben werden: GetMyBot setzt den Chat der Sitzung zwangsweise ein. - Inline-Schaltflächen mit
callback_datawerden automatisch tokenisiert (internes Formatx:<token>); die Lebensdauer der Schaltfläche entspricht dem Ablauf der Sitzung. Schaltflächen vom Typurl/webapp/switch_inlinewerden nicht verändert.
Erlaubte Methoden
Da der Dienst den Bot über Befehle steuert, ist der Methodensatz auf eine Whitelist beschränkt — ausschließlich Senden und Bearbeiten von Nachrichten im aktuellen Dialog. Methoden, die nicht auf der Liste stehen, werden still ignoriert (kein Fehler).
- Senden:
sendMessage,sendPhoto,sendDocument,sendVideo,sendAudio,sendMediaGroup,sendAnimation,sendVoice,sendLocation,sendChatAction; - Bearbeiten und Löschen:
editMessageText,editMessageCaption,editMessageReplyMarkup,deleteMessage; - Antwort auf Klick:
answerCallbackQuery; - Weiterleiten und Kopieren:
forwardMessage,copyMessage; - Anheften:
pinChatMessage,unpinChatMessage.
Methoden auf Account-Ebene (setWebhook, getUpdates, logOut, close, setMyCommands u. a.) sind nicht erlaubt.
Warten und Sitzungslebenszyklus
Nach jedem execute hängt das Schicksal der Sitzung von expect und davon ab, ob der Befehl Inline-Schaltflächen mit callback_data enthielt:
expect=textoderany— die nächste Nachricht des Nutzers wird dem Dienst als Ereignismessageweitergeleitet.expect=noneUND der Befehl enthielt KEINE Inline-Schaltflächen mit Callback — die Sitzung wird automatisch geschlossen. Dies ist eine „terminale" Nachricht.expect=none, aber Callback-Schaltflächen sind vorhanden — die Sitzung bleibt offen: Klicks werden erfasst, solange die Schaltflächen-Tokens gültig sind (bis TTL).
Wichtig: expect hat keinen Einfluss auf den Empfang von Schaltflächenklicks. Klicks auf tokenisierte Schaltflächen werden unabhängig vom Wert expect empfangen — solange die Sitzung und der Schaltflächen-Token gültig sind. expect steuert nur, ob der Dienst auf eine Text-/beliebige Antwort wartet.
Dialoggrenzen
Der Dialogkontext lebt genau so lange, wie die Sitzung geöffnet ist:
- Textantwort bei aktiver Erwartung (
expect=text/any) — geht als Ereignismessagean den Dienst, löst keine gewöhnlichen Bot-Reaktionen aus. - Klick auf tokenisierte Schaltfläche — geht als Ereignis
callbackmit dem ursprünglichencallback_dataan den Dienst. GetMyBot bestätigt den Klick immer selbst (answerCallbackQuery); die Zugehörigkeit des Klicks (Bot + Nutzer + Chat der Sitzung) wird geprüft. Ein Schaltflächenklick schließt die Sitzung nicht. /cancel(sowie/cancel@botund „abbrechen") bei einer aktiven externen Sitzung — die Sitzung wird geschlossen, dem Dienst wirdsession.cancelgesendet, dem Nutzer wird „Abgebrochen." angezeigt.- Ein Dialog pro Nutzer. Das Öffnen einer neuen Sitzung verdrängt die vorherige offene Sitzung dieses Nutzers — ihr wird
session.cancelgesendet. - TTL-Ablauf. Durch einen Timer wird die Sitzung als
expiredmarkiert, und dem Dienst wird (sofern er online ist)session.expiredgesendet.
Limits und Timeouts
- Framegröße: 256 KiB.
- Sitzungs-TTL: standardmäßig 3600 Sekunden (konfigurierbar über den Parameter
session_ttl_secondsder Integration). - Befehlsrate: 60
executepro Minute pro Sitzung (gleitendes Fenster). - Methoden in einem
execute: bis zu 30 (überzählige werden verworfen). - Offene Sitzungen pro Integration: bis zu 1000.
- Offline-Warteschlange: bis zu 100 Ereignisse pro Sitzung.
- Heartbeat:
pingalle 30 Sekunden; Timeout bei fehlender Aktivität — 60 Sekunden. - Timeout des Auth-Frames: 10 Sekunden.
Zuverlässigkeit (Heartbeat, Offline, Warteschlange, Fallback)
- Heartbeat. Der Server sendet alle 30 Sekunden einen Frame
{"type":"ping"}. Wenn vom Client länger als 60 Sekunden keine Aktivität eingeht, wird die Verbindung geschlossen. Der Client kann eigeneping/pong-Frames senden, um die Verbindung aktiv zu halten. Ein Verbindungsabbruch beendet keine Dialoge: Sitzungen leben in der Datenbank bis zum TTL oder bis zur Wiederverbindung. - Wiederverbindung — auf der Seite des Dienstes. Wenn die Verbindung abgebrochen ist, muss Ihr Dienst sich neu verbinden. Offene Sitzungen in der Datenbank überleben Neustarts bis zum Ablauf des TTL.
- Fallback bei Offline beim Start. Wenn der Dienst beim Auslösen der Reaktion nicht verbunden ist, wird die Fallback-Reaktion aus der Integrationseinstellung gestartet (
on_unavailable_reaction_id). Ist sie nicht festgelegt, geschieht nichts (still). - Offline-Warteschlange. Ereignisse
message/callback, die wegen Offline nicht zugestellt wurden, werden in die Offline-Warteschlange gelegt (bis zu 100 Ereignisse, FIFO, gespeichert bis zum Ablauf der Sitzung) und bei Wiederverbindung nachgeliefert. Die Ereignissesession.open/session.cancel/session.expiredwerden nicht in die Warteschlange gelegt. - Zustellungsgarantie — at-most-once. Bei Überlauf oder Ablauf der Warteschlange werden Ereignisse verworfen. Gestalten Sie die Logik so, dass das Auslassen einzelner Ereignisse das Szenario nicht bricht.
Online-Indikator
Ob der Dienst eine aktive Verbindung hält, können Sie über diesen Endpunkt prüfen:
GET /api/bots/{botID}/integrations/{integrationID}/status
Antwort:
{ "online": true }
Derselbe Indikator ist auf der Integrationskarte im Dashboard verfügbar. Das MCP-Äquivalent ist das Tool get_integration_status.
Sicherheit
GetMyBot führt Befehle eines externen Dienstes mit seinem eigenen Token aus — daher sind die Schutzmaßnahmen streng.
- Keine SSRF-Angriffsfläche. GetMyBot fungiert als WebSocket-Server und stellt selbst keine ausgehenden Verbindungen zum Dienst her — es ist der Dienst, der sich mit GetMyBot verbindet. Es gibt keine von außen steuerbare URL, unter der die Plattform irgendwo hingeht.
- Token wird als Hash gespeichert (sha256), Vergleich in konstanter Zeit, Rotation wird unterstützt.
- Mandantenisolierung. Ein Befehl ist fest an seine Integration gebunden; eine Sitzung — an Bot + Nutzer + Chat. Ereignisse fremder Bots gelangen nicht in Ihren Socket.
- Methoden-Whitelist + erzwungener
chat_idverhindern, dass der Bot als Massen-Sender in beliebige Chats missbraucht wird. - Bot-Token wird nicht nach außen übermittelt — der Dienst kommuniziert niemals direkt mit Telegram.
- Maskierung von Geheimnissen. Integrations-Token und sensible Werte werden in Protokollen und Logs ausgeblendet.
Beispiele
Minimaler Handler: Beim Öffnen eines Dialogs wird eine Nachricht gesendet und die Sitzung geschlossen (expect: "none"). Ersetzen Sie die Integrations-ID und das Token. Die Adresse muss unbedingt <integrationID> im Pfad enthalten.
Node.js
import WebSocket from "ws";
const URL = `wss://api.mybot.app/ext/ws/${process.env.MYBOT_INTEGRATION_ID}`;
const ws = new WebSocket(URL, {
headers: { Authorization: `Bearer ${process.env.MYBOT_TOKEN}` },
});
ws.on("message", (raw) => {
const ev = JSON.parse(raw);
if (ev.type === "session.open") {
ws.send(JSON.stringify({
type: "execute",
session_id: ev.session_id,
expect: "none",
methods: [{ method: "sendMessage", params: { text: "Hallo vom externen Handler!" } }],
}));
}
});
Python
import json, os, asyncio, websockets
async def main():
url = f"wss://api.mybot.app/ext/ws/{os.environ['MYBOT_INTEGRATION_ID']}"
headers = {"Authorization": f"Bearer {os.environ['MYBOT_TOKEN']}"}
async with websockets.connect(url, additional_headers=headers) as ws:
async for raw in ws:
ev = json.loads(raw)
if ev["type"] == "session.open":
await ws.send(json.dumps({
"type": "execute",
"session_id": ev["session_id"],
"expect": "none",
"methods": [
{"method": "sendMessage", "params": {"text": "Hallo vom externen Handler!"}}
],
}))
asyncio.run(main())
Inline-Schaltfläche und Callback-Verarbeitung
Um einen Klick zu erfassen, senden Sie eine Schaltfläche mit callback_data (GetMyBot tokenisiert sie automatisch). Bei expect: "none" mit einer Schaltfläche bleibt die Sitzung offen, solange der Schaltflächen-Token gültig ist — der Klick kommt als Ereignis callback, in dem event.callback_data dem ursprünglichen Wert entspricht. Das Bestätigen des Klicks (answerCallbackQuery) ist nicht erforderlich — GetMyBot erledigt das selbst.
if (ev.type === "session.open") {
ws.send(JSON.stringify({
type: "execute",
session_id: ev.session_id,
expect: "none",
methods: [{
method: "sendMessage",
params: {
text: "Schaltfläche drücken",
reply_markup: { inline_keyboard: [[{ text: "Los", callback_data: "go" }]] },
},
}],
}));
} else if (ev.type === "callback" && ev.event.callback_data === "go") {
ws.send(JSON.stringify({
type: "execute",
session_id: ev.session_id,
expect: "none",
methods: [{ method: "sendMessage", params: { text: "Schaltfläche gedrückt!" } }],
}));
}
Nächste Schritte
- Integrationen — allgemeiner Überblick über anbindbare Dienste.
- Reaktionen — wie Trigger, Bedingungen und Aktionen zusammengestellt werden.
- Web-Anfragen und Webhooks — einfachere Möglichkeit, eine externe URL ohne Dialog aufzurufen.