Telegram heeft twee APIs. De Bot API, gecastreerd, dichtgetimmerd, beperkt tot wat Telegram besloot dat bots mogen doen. En MTProto, het echte protocol dat je telefoon gebruikt, met volledige accounttoegang, zonder beperkingen. Bots kunnen de berichtgeschiedenis van voor hun komst in een chat niet lezen. Bots kunnen niet zien wie er in een privégroep zit waar ze zelf niet in zitten. Bots kunnen niet namens jou handelen. MTProto wel.
Telethon is een MTProto-client in Python. telethon-plus stopt die in een Docker-container met een REST-API over HTTP en een MCP-server. Je POST wat JSON, of je wijst een AI-agent naar /mcp/, en dat ding praat namens jou met Telegram, met volledige accounttoegang. Eén login. Eén session string. Nooit meer een code intypen.
Userbot, Geen Bot
Dit is een userbot, geen bot. Dat onderscheid doet ertoe.
Een bot is wat BotFather je geeft. Beperkte schrijftoegang, geen berichtgeschiedenis, geen groepslidmaatschap zonder expliciete uitnodiging, niet in staat op de meeste events te reageren. De Bot API is prima om notificaties te versturen. Voor alles waar je moet lezen wat er echt in je chats gebeurt, is hij waardeloos.
Een userbot is jouw account, programmatisch bestuurd. Dezelfde toegang die je hebt als je Telegram op je telefoon opent. Elk bericht lezen in elke chat waar je in zit. Berichten sturen als jezelf. Doorsturen, verwijderen, bewerken. Groepen joinen. Kanalen aanmaken. Handelen met je echte identiteit, volledige geschiedenis, volledig zicht.
De keerzijde: het is jouw account. Misbruik het en je account vliegt eruit. Telegram knijpt agressief af (FloodWaitError). Spam niet. Gebruik het waar het voor is: programmatische toegang tot jouw Telegram, geen massaberichten.
Snelle Start
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-stoppedHaal API_ID en API_HASH op bij my.telegram.org/apps. De session string komt uit de login-helper.
Eerste Login
Telegram dwingt je één keer te bewijzen dat je een mens bent: telefoonnummer, code via sms, optioneel 2FA. De container heeft een interactieve login-modus die je er doorheen loodst en aan het eind een session string uitspuugt:
docker run --rm -it
-e TELETHON_API_ID=123456
-e TELETHON_API_HASH=your-api-hash
psyb0t/telethon-plus loginOf, als je de repo hebt gekloond: cp .env.example .env, vul TELETHON_API_ID en TELETHON_API_HASH in, en dan make login, wat het image bouwt, de flow start en TELETHON_SESSION direct in je .env schrijft.
De session string is volledige accounttoegang. Wie hem heeft, bent jij. Niet committen. Niet in Slack plakken. Nergens laten tatoeëren.
De Tools
Hetzelfde toolregister, twee oppervlakken. Elke tool draait zowel als REST-endpoint als als MCP-tool: JSON erin, JSON eruit, gevalideerd via pydantic-schema. Stuur rotzooi en je krijgt een 400 terug met precies wat er mis is.
- get_me: geeft het profiel van het geautoriseerde account
- get_entity: zet een username, ID of link om naar een volledig profiel
- send_message: stuurt een tekstbericht (Markdown/HTML, antwoord, stil, linkvoorbeeld)
- get_messages: leest recente berichten uit een chat (nieuwste eerst, zoeken, paginering)
- get_dialogs: somt je chats, groepen en kanalen op
- forward_messages: stuurt berichten door tussen chats
- delete_messages: verwijdert berichten op ID (intrekken voor iedereen of alleen voor jou)
- edit_message: bewerkt een bericht dat je hebt gestuurd
- mark_read: markeert berichten in een chat als gelezen
- send_file: downloadt een bestand van een HTTPS-URL en stuurt het naar een chat
- get_participants: somt de leden van een groep of kanaal op
- create_group: maakt een nieuwe supergroep of een nieuw broadcastkanaal aan
- delete_chat: verwijdert een supergroep of kanaal dat van jou is
- join_chat: joint een openbaar kanaal of supergroep
- leave_chat: verlaat een kanaal of supergroep
- get_message: haalt één enkel bericht op via ID
- download_media: haalt de media van een bericht op als base64
- bulk_resolve: zet veel usernames of IDs om in één call in plaats van N rondjes
- set_reaction / remove_reaction: reageren op berichten
- pin_message / unpin_message: beheer van vastgezette berichten
- create_poll, vote_poll, get_poll_results: polls van begin tot eind
- promote_user, demote_user, ban_user, unban_user, kick_user: kanaalbeheer
- join_via_invite: joinen via een privé-uitnodigingslink
- get_linked_chat: de discussiegroep achter een kanaal, of andersom
- account_health: of dit account op dit moment gedoe heeft met Telegram
- throttle_status: wat de rate limiter er op dit moment van vindt
Vierendertig tools in totaal, en elke tool werkt identiek via REST en via MCP.
Chatverwijzingen accepteren wat Telethon accepteert: @username, telefoonnummers, t.me/...-links, numerieke IDs als strings, supergroep- of kanaal-IDs (negatieve getallen zoals -1001234567890), of me voor je eigen Opgeslagen berichten.
HTTP-API
Standaard REST. /api/me, /api/messages, /api/dialogs, enzovoort. Sinds v0.4.0 geeft een 2xx de resource direct terug, de oude {"result": ...}-envelop is weg. Heb je een client tegen v0.3.x geschreven, pak dan een laag minder uit.
# 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"}'Telegram detecteert het mediatype automatisch aan de bestandsextensie. .jpg wordt een foto, .mp4 een video, .mp3 audio. Gebruik force_document: true om het anders af te dwingen.
De validatie is streng, pydantic-schema’s weigeren extra velden en verkeerde types met een 400 en een gedetailleerde foutbody. Telegrams RPC-fouten (FloodWaitError, ChatWriteForbiddenError) komen terug als 502, met foutklasse en melding intact.
De health check op /healthz is altijd publiek, geen auth nodig. Hij geeft {"status": "ok", "authorized": true}, en authorized: false betekent dat de sessie naar de klote is (verkeerde string, ingetrokken, of Telegram onbereikbaar).
MCP-Server
Gemount op /mcp/ via het streamable HTTP-transport. Elke tool uit de lijst hierboven verschijnt automatisch als MCP-tool, met dezelfde naam en hetzelfde schema. Stateless, elk verzoek staat op zichzelf, geen gegoochel met sessies.
http://your-host:8080/mcp/Prik het in Claude Desktop, in een eigen agent, in een LLM-gateway, in alles wat MCP over streamable HTTP spreekt. Werkt meteen.
Dit is het stuk dat ertoe doet. Elk model met function calling kan nu je Telegram lezen, erin schrijven, groepen beheren, content doorsturen. Het model bepaalt wanneer. De agent handelt. Wil je dat Claude de laatste 50 berichten van een chat samenvat als dagelijkse digest? Prik de MCP-endpoint erin en vraag het. Wil je dat een model supportvragen uit een openbaar kanaal doorstuurt naar de chat van je team? Net zo. Wil je een agent die een groep in de gaten houdt op vermeldingen en namens jou antwoordt? Geregeld.
Hoe Je Je Account Niet Laat Bannen
Dit is het stuk dat ertoe doet zodra je automatisering op je eigen Telegram-account richt: Telegram merkt het. Ram op de API en je krijgt een FLOOD_WAIT, daarna beperkingen, daarna een dood account.
Dus er zit nu een echte rate limiter in. Token buckets per methode, verstuur- en leesintervallen per chat, en adaptieve backoff wanneer Telegram daadwerkelijk een FLOOD_WAIT teruggeeft. De standaardwaarden zijn zo afgesteld dat je ruim onder Telegrams waargenomen zachte limieten blijft, zonder dat je iets bijstelt. throttle_status vertelt je waar je staat, account_health vertelt je of het account al gedoe heeft.
Twee veiligheidsschakelaars voor als je iets nieuws aan het bedraden bent: TELETHON_READ_ONLY weigert elke wijzigende call, en TELETHON_DRY_RUN maakt de bewegingen zonder echt te versturen. Richt de eerste keer een LLM op je account met één van de twee aan.
Entity-cache. Een username omzetten kost een echte API-call, en dezelfde keer op keer omzetten is hoe je je quota voor niets opstookt. De cache overleeft herstarts van de container, dus herhaalde opzoekingen worden gratis. v0.4.1 heeft op dat pad een lelijke bug gefixt: de refresh riep get_entity aan met een kale int, wat Telethon als PeerUser behandelt, dus gecachete kanalen en groepen gooiden een fout, invalideerden zichzelf en verbrandden elke keer een ResolveUsername-slot. Precies de operatie die de cache moest voorkomen. Nu verpakt hij het juiste Peer-type.
Observability
/metrics levert Prometheus-formaat. /ws/updates is een websocket die updates streamt zodra ze binnenkomen. Zet TELETHON_POST_TO_URL en elke update gaat er daarnaast als uitgaande webhook uit, wat de makkelijke manier is om dit aan n8n of je eigen service te hangen zonder een socket open te houden.
Er is een suite van 44 unit tests die draait zonder Telegram überhaupt aan te raken (make test-unit), los van de integratietests die een echt account aanspreken.
Skill en Plugin
De repo levert een agent-skill plus een OpenClaw-plugin onder .agents/, door CI gepubliceerd op ClawHub bij tag-pushes. Een agent installeert dat en kent het hele tooloppervlak zonder dat jij de lijst in een prompt plakt.
Auth
Zet TELETHON_AUTH_KEY om bearer-token-auth aan te zetten op elk endpoint behalve /healthz. Zet je hem niet, dan draait de API ongeauthenticeerd, wat prima is op localhost en zelfmoord zodra het aan een netwerk hangt. Zet hem altijd.
Authorization: Bearer your-secret-keyDe session string is het echte geheim, hem kwijtraken is je account kwijtraken. Stop hem niet eens in een compose-YAML die in een repo wordt gecommit. .env-bestand, in gitignore, op runtime geladen.
Configuratie
Alles via omgevingsvariabelen.
TELETHON_API_ID,TELETHON_API_HASH,TELETHON_SESSION: verplicht, van my.telegram.org en uit de login-helperTELETHON_AUTH_KEY: bearer token voor REST en MCP. Leeg is geen auth. Zet hem.TELETHON_HTTP_LISTEN_ADDRESS: standaard0.0.0.0:8080TELETHON_REQUEST_TIMEOUT: timeout per verzoek (standaard 60 seconden)TELETHON_FLOOD_SLEEP_THRESHOLD: verslaapt FloodWait-fouten onder dit aantal seconden automatisch (standaard 60). De veiligheidsklep.TELETHON_DEVICE_MODEL,TELETHON_SYSTEM_VERSION,TELETHON_APP_VERSION: waar Telegram je client voor aanzietTELETHON_DOWNLOAD_DIR: werkruimte voorsend_file-uploads (standaard/tmp/telethon-plus)TELETHON_PROXY: optionele SOCKS5-proxy (socks5://user:pass@host:port)
De flood sleep-drempel is de veiligheidsklep, Telegram gaat je afknijpen als je te hard duwt. Onder de drempel slaapt de client en probeert het transparant opnieuw. Daarboven krijg je een 502. Stel hem af op je use case.
De Tests Gaan Tegen Het Echte Telegram
Geen mocks. De testsuite start de container tegen je echte account, met berichten die verstuurd en verwijderd worden in een chat die jij opgeeft (TEST_CHAT, neem me voor Opgeslagen berichten, dan ziet niemand anders de herrie).
cp .env.example .env
$EDITOR .env # TELETHON_API_ID, TELETHON_API_HASH, TELETHON_SESSION, TEST_CHAT
make testDe tests dekken validatiefouten, versturen-bewerken-ophalen-verwijderen-rondjes, het opsommen van dialogen, entity-resolutie, lezen uit openbare kanalen, groepen aanmaken en verwijderen, deelnemerslijsten, MCP-tooldiscovery en -aanroep, de auth-middleware (401 bij een ontbrekend of fout token, healthz altijd publiek). Staan de credentials niet ingesteld, dan slaat de suite netjes over.
Beveiliging
De session string staat gelijk aan je account. Behandel hem als een creditcardnummer dat tegelijk je paspoort is. Concreet:
- Zet altijd
TELETHON_AUTH_KEY, draai nooit ongeauthenticeerd behalve op geïsoleerde localhost - Commit
TELETHON_SESSIONnooit naar git,.env-bestand, in gitignore - Vermoed je een lek, log dan op je telefoon in bij Telegram en trek de actieve sessies onmiddellijk in
- Zet poort
8080niet op het publieke internet, hang er een reverse proxy met TLS voor, of een Cloudflare Tunnel - Roteer de auth key als er ooit iets lekt
De container zelf is klein: Python 3.12, Telethon, FastAPI, de MCP-SDK. /healthz is het enige publieke endpoint. Al het andere vereist het bearer token als TELETHON_AUTH_KEY is gezet.
Aangesloten Op aigate
Dit ding zit in aigate bedraad als een van zijn MCP-servers. Zet TELETHON=1 aan in de .env van aigate, zet de credentials erin, en elk model met function calling dat door de gateway loopt kan nu met je Telegram praten. Groq roept een tool aan, de tool gaat naar Telegram, jij krijgt een bericht. Het model bepaalt wanneer.
Een Groq-model uit het gratis tier dat het web doorzoekt, een pagina scrapet, een afbeelding genereert en het resultaat naar je Opgeslagen berichten stuurt, nul betaalde tokens, één gesprek, meerdere toolaanroepen. Dat is precies het punt.
Kort Samengevat
De Bot API is prima als je alleen notificaties hoeft te versturen. Userbots zijn wat je wilt zodra je op Telegram echt dingen moet doen: berichten lezen, groepen beheren, programmatisch handelen als jezelf. telethon-plus zet een echte MTProto-client achter HTTP en MCP, dus elke tool die JSON spreekt en elke agent die MCP kent kan je account besturen.
Pak het hier: github.com/psyb0t/docker-telethon-plus
Onder de WTFPL-licentie, doe ermee wat je verdomme wilt.
Zo Zet Je Het In Je Agent
De skill installeert nu rechtstreeks in Claude Code en Codex. Alles onder .agents/ staat in één marketplace gecatalogiseerd, dus het zijn twee commando’s:
claude plugin marketplace add psyb0t/agents
claude plugin install telethon-plus@psyb0tCodex gebruikt dezelfde marketplace met een ander werkwoord, codex plugin add telethon-plus@psyb0t, omdat codex plugin install niet bestaat. Hij vindt de skill ook uit zichzelf in een checkout van de repo, aangezien hij .agents/skills/ native scant zonder dat er iets geïnstalleerd is. Het staat nu ook in het officiële MCP Registry, dus een client die zijn servers daarvandaan oplost kan het vinden zonder dat je hem een URL geeft.