Telegram hat zwei APIs. Die Bot API, kastriert, eingeschränkt, begrenzt auf das, was Telegram Bots zugestanden hat. Und MTProto, das echte Protokoll, das dein Telefon benutzt, mit vollem Kontozugriff, ohne Einschränkungen. Bots können den Nachrichtenverlauf von vor ihrem Beitritt zu einem Chat nicht lesen. Bots können nicht sehen, wer in einer privaten Gruppe ist, in der sie nicht drin sind. Bots können nicht in deinem Namen handeln. MTProto schon.
Telethon ist ein MTProto-Client in Python. telethon-plus packt ihn in einen Docker-Container mit einer REST-API über HTTP und einem MCP-Server. Du POSTest etwas JSON, oder du richtest einen KI-Agenten auf /mcp/, und das Ding redet in deinem Namen mit Telegram, mit vollem Kontozugriff. Ein Login. Ein Session String. Nie wieder einen Code eintippen.
Userbot, Kein Bot
Das ist ein Userbot, kein Bot. Der Unterschied zählt.
Ein Bot ist das, was BotFather dir gibt. Begrenzter Schreibzugriff, kein Nachrichtenverlauf, keine Gruppenmitgliedschaft ohne ausdrückliche Einladung, unfähig, auf die meisten Events zu reagieren. Die Bot API taugt fürs Verschicken von Benachrichtigungen. Für alles, was verlangt zu lesen, was in deinen Chats tatsächlich passiert, ist sie nutzlos.
Ein Userbot ist dein Konto, programmatisch gesteuert. Derselbe Zugriff, den du hast, wenn du Telegram auf dem Handy aufmachst. Jede Nachricht in jedem Chat lesen, in dem du bist. Nachrichten als du selbst verschicken. Weiterleiten, löschen, bearbeiten. Gruppen beitreten. Kanäle anlegen. Mit deiner echten Identität handeln, voller Verlauf, volle Sicht.
Der Haken: es ist dein Konto. Missbrauch es, und dein Konto fliegt raus. Telegram drosselt aggressiv (FloodWaitError). Spam nicht. Nimm es für das, wofür es da ist: programmatischen Zugriff auf dein Telegram, nicht für Massennachrichten.
Schnellstart
services:
telethon-plus:
image: psyb0t/telethon-plus
ports:
- "8080:8080"
environment:
TELETHON_API_ID: "123456"
TELETHON_API_HASH: "your-api-hash"
TELETHON_SESSION: "1Aa...long-string-from-login..."
TELETHON_AUTH_KEY: "your-bearer-token"
restart: unless-stoppedHol dir API_ID und API_HASH von my.telegram.org/apps. Der Session String kommt aus dem Login-Helfer.
Erster Login
Telegram zwingt dich einmal, zu beweisen, dass du ein Mensch bist: Telefonnummer, Code per SMS, optional 2FA. Der Container hat einen interaktiven Login-Modus, der dich durchführt und am Ende einen Session String ausspuckt:
docker run --rm -it \
-e TELETHON_API_ID=123456 \
-e TELETHON_API_HASH=your-api-hash \
psyb0t/telethon-plus loginOder wenn du das Repo geklont hast: cp .env.example .env, TELETHON_API_ID und TELETHON_API_HASH ausfüllen, dann make login, was das Image baut, den Ablauf startet und TELETHON_SESSION direkt in deine .env schreibt.
Der Session String ist voller Kontozugriff. Wer ihn hat, bist du. Nicht committen. Nicht in Slack pasten. Nirgendwo hintätowieren.
Die Tools
Dasselbe Tool-Register, zwei Oberflächen. Jedes Tool läuft sowohl als REST-Endpoint als auch als MCP-Tool: JSON rein, JSON raus, per pydantic-Schema validiert. Schick Müll, und du kriegst einen 400 mit genau dem, was falsch ist.
- get_me: liefert das Profil des autorisierten Kontos
- get_entity: löst einen Username, eine ID oder einen Link zu einem vollen Profil auf
- send_message: schickt eine Textnachricht (Markdown/HTML, Antwort, stumm, Link-Vorschau)
- get_messages: liest die letzten Nachrichten aus einem Chat (neueste zuerst, Suche, Pagination)
- get_dialogs: listet deine Chats, Gruppen und Kanäle
- forward_messages: leitet Nachrichten zwischen Chats weiter
- delete_messages: löscht Nachrichten per ID (für alle widerrufen oder nur für dich)
- edit_message: bearbeitet eine Nachricht, die du verschickt hast
- mark_read: markiert Nachrichten in einem Chat als gelesen
- send_file: lädt eine Datei von einer HTTPS-URL und schickt sie in einen Chat
- get_participants: listet die Mitglieder einer Gruppe oder eines Kanals
- create_group: legt eine neue Supergruppe oder einen Broadcast-Kanal an
- delete_chat: löscht eine Supergruppe oder einen Kanal, der dir gehört
- join_chat: tritt einem öffentlichen Kanal oder einer Supergruppe bei
- leave_chat: verlässt einen Kanal oder eine Supergruppe
- get_message: holt eine einzelne Nachricht per ID
- download_media: zieht das Medium einer Nachricht als base64
- bulk_resolve: löst viele Usernames oder IDs in einem Aufruf auf statt in N Runden
- set_reaction / remove_reaction: auf Nachrichten reagieren
- pin_message / unpin_message: Verwaltung angehefteter Nachrichten
- create_poll, vote_poll, get_poll_results: Umfragen von Anfang bis Ende
- promote_user, demote_user, ban_user, unban_user, kick_user: Kanalverwaltung
- join_via_invite: über einen privaten Einladungslink beitreten
- get_linked_chat: die Diskussionsgruppe hinter einem Kanal, oder andersrum
- account_health: wie oft dich Telegram zuletzt mit FLOOD_WAIT ausgebremst hat, eingedampft auf ok, warning oder high
- throttle_status: was der Rate Limiter in diesem Moment denkt
Vierunddreißig Tools insgesamt, und jedes gibt es auf beiden Oberflächen. Das einzige, das sich anders verhält, ist download_media: über MCP kriegst du base64, über REST die rohen Bytes mit einem ordentlichen Content-Type.
Chat-Referenzen nehmen das, was Telethon nimmt: @username, Telefonnummern, t.me/...-Links, numerische IDs als Strings, Supergruppen- oder Kanal-IDs (negative Zahlen wie -1001234567890), oder me für deine eigenen Gespeicherten Nachrichten.
HTTP-API
Standard-REST. /api/me, /api/messages, /api/dialogs und so weiter. Seit v0.4.0 gibt ein 2xx die Ressource direkt zurück, der alte {"result": ...}-Umschlag ist weg. Wenn du einen Client gegen v0.3.x geschrieben hast, pack eine Ebene weniger aus.
# who am I
curl https://ciprian.51k.eu80/api/me \
-H "Authorization: Bearer $TELETHON_AUTH_KEY"
# send a message with Markdown
curl -X POST https://ciprian.51k.eu80/api/messages \
-H "Authorization: Bearer $TELETHON_AUTH_KEY" \
-H "Content-Type: application/json" \
-d '{"chat": "@somebody", "text": "**hello** from a container", "parse_mode": "md"}'
# read recent messages with full-text search
curl "https://ciprian.51k.eu80/api/messages?chat=me&limit=5&search=hello" \
-H "Authorization: Bearer $TELETHON_AUTH_KEY"
# send a file from an HTTPS URL — never touches your disk
# (v0.4.0 folded /api/files into /api/messages; `caption` is now `text`)
curl -X POST https://ciprian.51k.eu80/api/messages \
-H "Authorization: Bearer $TELETHON_AUTH_KEY" \
-H "Content-Type: application/json" \
-d '{"chat": "@psyb0t", "file_url": "https://example.com/photo.jpg", "text": "look at this"}'Theoretisch wählt Telegram den Medientyp anhand der Dateiendung: .jpg wird ein Foto, .mp4 ein Video, .mp3 Audio, und force_document: true hebelt das aus. Praktisch legt der Container den Download unter einem temporären Namen ganz ohne Endung ab, Telethon hat also nichts, woran es sich halten kann, und alles kommt als generisches Dokument an. force_document: true liefert dir gerade genau das, was du sowieso bekommen hättest.
Die Validierung ist streng, pydantic-Schemas lehnen Extra-Felder und falsche Typen mit einem 400 und einem detaillierten Fehler-Body ab. Telegrams RPC-Fehler (FloodWaitError, ChatWriteForbiddenError) kommen als 502 zurück, Fehlerklasse und Meldung bleiben erhalten.
Der Health Check auf /healthz ist immer öffentlich, keine Auth nötig. Er liefert {"status": "ok", "authorized": true}, aber verlass dich nicht zu sehr auf das Flag. Ein falscher oder nicht autorisierter Session-String lässt den Dienst gar nicht erst starten, und sobald er läuft, sagt das Flag nur, dass ein Client-Objekt existiert. Es bleibt also true, auch wenn die Session später widerrufen wird oder Telegram aus dem Netz verschwindet.
MCP-Server
Gemountet auf /mcp/ über den streamable HTTP-Transport. Jedes Tool aus der Liste oben taucht automatisch als MCP-Tool auf, mit demselben Namen und demselben Schema. Zustandslos, jede Anfrage steht für sich, kein Session-Jonglieren.
http://your-host:8080/mcp/Steck ihn in Claude Desktop, in einen eigenen Agenten, in ein LLM-Gateway, in alles, was MCP über streamable HTTP spricht. Läuft sofort.
Das ist der Teil, der zählt. Jedes Modell mit Function Calling kann jetzt dein Telegram lesen, hineinschreiben, Gruppen verwalten, Inhalte weiterleiten. Das Modell entscheidet wann. Der Agent handelt. Du willst, dass Claude dir die letzten 50 Nachrichten eines Chats als Tagesdigest zusammenfasst? MCP-Endpoint anschließen, fragen. Du willst, dass ein Modell Support-Fragen aus einem öffentlichen Kanal in den Chat deines Teams weiterleitet? Genauso. Du willst einen Agenten, der eine Gruppe nach Erwähnungen absucht und in deinem Namen antwortet? Erledigt.
Wie Du Dir Dein Konto Nicht Sperren Lässt
Das ist der Teil, der zählt, wenn du Automatisierung auf dein eigenes Telegram-Konto richtest: Telegram merkt das. Hämmer auf die API ein, und du kriegst ein FLOOD_WAIT, dann Einschränkungen, dann ein totes Konto.
Also gibt es jetzt einen echten Rate Limiter. Token Buckets pro Methode, Sende- und Leseintervalle pro Chat, und adaptiver Backoff, wenn Telegram tatsächlich ein FLOOD_WAIT zurückgibt. Die Defaults sind so kalibriert, dass sie deutlich unter Telegrams beobachteten weichen Grenzen bleiben, ohne dass du irgendwas einstellst. throttle_status sagt dir, wo du stehst, account_health macht aus den letzten FLOOD_WAITs eine Risikostufe ok, warning oder high.
Zwei Sicherheitsschalter für den Moment, in dem du etwas Neues verdrahtest: TELETHON_READ_ONLY lehnt jeden verändernden Aufruf ab, und TELETHON_DRY_RUN macht die Bewegungen, ohne wirklich zu senden. Richte ein LLM beim ersten Mal mit einem von beiden eingeschaltet auf dein Konto.
Entity-Cache. Einen Username aufzulösen kostet einen echten API-Aufruf, und denselben immer wieder aufzulösen ist der Weg, dein Kontingent für nichts zu verbrennen. Der Cache überlebt Container-Neustarts, wiederholte Abfragen werden also kostenlos. v0.4.1 hat auf dem Pfad einen hässlichen Bug gefixt: der Refresh rief get_entity mit einem nackten int auf, was Telethon als PeerUser behandelt, also warfen gecachte Kanäle und Gruppen einen Fehler, invalidierten sich und verbrannten jedes Mal einen ResolveUsername-Slot. Genau die Operation, die der Cache vermeiden sollte. Jetzt verpackt er den richtigen Peer-Typ.
Observability
/metrics liefert Prometheus-Format. /ws/updates ist ein Websocket, der Updates streamt, sobald sie eintreffen. Setz TELETHON_POST_TO_URL, und jedes Update geht zusätzlich als ausgehender Webhook raus, was der einfache Weg ist, das an n8n oder deinen eigenen Service zu hängen, ohne einen Socket offenzuhalten.
Es gibt eine Suite aus 44 Unit-Tests, die läuft, ohne Telegram überhaupt anzufassen (make test-unit), getrennt von den Integrationstests, die ein echtes Konto ansprechen.
Skill und Plugin
Das Repo liefert einen Agent-Skill plus ein OpenClaw-Plugin unter .agents/, von der CI bei Tag-Pushes auf ClawHub veröffentlicht. Ein Agent installiert das und kennt die komplette Tool-Oberfläche, ohne dass du die Liste in einen Prompt klebst.
Auth
Setz TELETHON_AUTH_KEY, um Bearer-Token-Auth auf jedem Endpoint außer /healthz und /metrics einzuschalten. Der Websocket /ws/updates will denselben Key, aber als Query-Parameter ?token= statt im Header. Setzt du es nicht, läuft die API unauthentifiziert, was auf localhost in Ordnung ist und in einem Netz Selbstmord. Setz es immer.
Authorization: Bearer your-secret-keyDer Session String ist das eigentliche Geheimnis, ihn zu verlieren heißt, dein Konto zu verlieren. Pack ihn nicht mal in ein Compose-YAML, das in einem Repo committet wird. .env-Datei, gitignoriert, zur Laufzeit geladen.
Konfiguration
Alles über Umgebungsvariablen.
TELETHON_API_ID,TELETHON_API_HASH,TELETHON_SESSION: Pflicht, von my.telegram.org und aus dem Login-HelferTELETHON_AUTH_KEY: Bearer Token für REST und MCP. Leer heißt keine Auth. Setz es.TELETHON_HTTP_LISTEN_ADDRESS: Standard0.0.0.0:8080TELETHON_REQUEST_TIMEOUT: klingt nach Timeout pro Anfrage, geht an Telethon aber als Verbindungs-Timeout, und Telethons eigene Doku sagt ausdrücklich, dass das nicht der Timeout für Anfragen ist (Standard 60 Sekunden)TELETHON_FLOOD_SLEEP_THRESHOLD: verschläft FloodWait-Fehler unterhalb dieser Sekundenzahl automatisch (Standard 60). Das Sicherheitsventil.TELETHON_DEVICE_MODEL,TELETHON_SYSTEM_VERSION,TELETHON_APP_VERSION: wofür Telegram deinen Client hältTELETHON_DOWNLOAD_DIR: soll eigentlich den Arbeitsbereich fürsend_file-Uploads festlegen, aber der Code ignoriert die Variable derzeit: Uploads landen immer in/tmp/telethon-plus, Setzen bringt also einen ScheißTELETHON_PROXY: soll eigentlich ein optionaler SOCKS5-Proxy sein (socks5://user:pass@host:port), aber dieselbe Nummer wie beim Verzeichnis drüber: Die Config liest ihn ein, und nichts reicht ihn je an Telethon weiter, dein Traffic geht also direkt zu Telegram, egal was du hier reinschreibst
Die Flood-Sleep-Schwelle ist das Sicherheitsventil, Telegram wird dich drosseln, wenn du zu hart drückst. Unterhalb der Schwelle schläft der Client und versucht es transparent nochmal. Darüber kriegst du einen 502. Stell sie auf deinen Anwendungsfall ein.
Die Tests Gehen Gegen Das Echte Telegram
Keine Mocks. Die Testsuite startet den Container gegen dein echtes Konto, mit Nachrichten, die in einem von dir angegebenen Chat verschickt und gelöscht werden (TEST_CHAT, nimm me für Gespeicherte Nachrichten, dann sieht sonst niemand den Lärm).
cp .env.example .env
$EDITOR .env # TELETHON_API_ID, TELETHON_API_HASH, TELETHON_SESSION, TEST_CHAT
make testDie Tests decken Validierungsfehler ab, Senden-Bearbeiten-Holen-Löschen-Runden, Dialogauflistung, Entity-Auflösung, Lesen aus öffentlichen Kanälen, Gruppen anlegen und löschen, Teilnehmerlisten, MCP-Tool-Discovery und -Aufruf, die Auth-Middleware (401 bei fehlendem oder falschem Token, healthz immer öffentlich). Sind die Credentials nicht gesetzt, überspringt die Suite sauber.
Sicherheit
Der Session String ist gleichbedeutend mit deinem Konto. Behandle ihn wie eine Kreditkartennummer, die gleichzeitig dein Ausweis ist. Konkret:
- Setz immer
TELETHON_AUTH_KEY, lauf nie unauthentifiziert außer auf isoliertem localhost - Committe
TELETHON_SESSIONniemals in git,.env-Datei, gitignoriert - Wenn du ein Leck vermutest, melde dich auf dem Handy bei Telegram an und widerrufe sofort die aktiven Sessions
- Stell Port
8080nicht ins öffentliche Internet, setz einen Reverse Proxy mit TLS davor, oder einen Cloudflare Tunnel - Rotiere den Auth Key, falls jemals irgendwas leckt
Der Container selbst ist klein: Python 3.12, Telethon, FastAPI, das MCP-SDK. /healthz und /metrics sind die einzigen öffentlichen Endpoints, also mach den zweiten mit TELETHON_METRICS_ENABLED=false platt, wenn ihn keiner scrapt. Alles andere braucht den Bearer Token, wenn TELETHON_AUTH_KEY gesetzt ist.
An aigate Angeschlossen
Das Ding ist in aigate als einer seiner MCP-Server verdrahtet. Schalt TELETHON=1 in aigates .env um, setz die Credentials, und jedes Modell mit Function Calling, das durch das Gateway läuft, kann jetzt mit deinem Telegram reden. Groq ruft ein Tool auf, das Tool geht an Telegram, du kriegst eine Nachricht. Das Modell entscheidet wann.
Ein Groq-Modell aus dem kostenlosen Kontingent, das im Web sucht, eine Seite scrapt, ein Bild generiert und das Ergebnis in deine Gespeicherten Nachrichten schickt, null bezahlte Tokens, eine Unterhaltung, mehrere Tool-Aufrufe. Das ist der ganze Punkt.
Unterm Strich
Die Bot API taugt, wenn du nur Benachrichtigungen verschicken musst. Userbots sind das, was du willst, wenn du auf Telegram wirklich Dinge tun musst: Nachrichten lesen, Gruppen verwalten, programmatisch als du selbst handeln. telethon-plus setzt einen echten MTProto-Client hinter HTTP und MCP, also kann jedes Tool, das JSON spricht, und jeder Agent, der MCP kennt, dein Konto steuern.
Hol es dir: github.com/psyb0t/docker-telethon-plus
Lizenziert unter der WTFPL, mach damit, was immer du verdammt nochmal willst.
Rein Damit In Deinen Agenten
Der Skill installiert sich jetzt direkt in Claude Code und Codex. Alles unter .agents/ ist in einem einzigen Marketplace katalogisiert, also sind es zwei Befehle:
claude plugin marketplace add psyb0t/agents
claude plugin install telethon-plus@psyb0tCodex nutzt denselben Marketplace mit einem anderen Verb, codex plugin add telethon-plus@psyb0t, weil es kein codex plugin install gibt. Er findet den Skill außerdem von allein in einem Checkout des Repos, da er .agents/skills/ nativ scannt, ganz ohne Installation. Es ist jetzt auch im offiziellen MCP Registry gelistet, ein Client, der seine Server von dort auflöst, kann es also finden, ohne dass man ihm eine URL gibt.