telethon-plus: Je Telegram-Account als HTTP-API en MCP-Server

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-stopped

Haal 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 login

Of, 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-key

De 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-helper
  • TELETHON_AUTH_KEY: bearer token voor REST en MCP. Leeg is geen auth. Zet hem.
  • TELETHON_HTTP_LISTEN_ADDRESS: standaard 0.0.0.0:8080
  • TELETHON_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 aanziet
  • TELETHON_DOWNLOAD_DIR: werkruimte voor send_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 test

De 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_SESSION nooit 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 8080 niet 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@psyb0t

Codex 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.