Minimalistische Illustration einer Waage mit einem Papierflieger darüber, cremefarbener Hintergrund und korallenrote Akzentfarbe

Wenn der Bot nicht mit dem Bot reden darf

Withings-Gewicht via Telethon an einen fremden Telegram-Bot

Der Auslöser

Am letzten Wochenende stand ich morgens auf der Waage, tippte die Zahl ab, öffnete Telegram und schickte sie an einen Bot, der genau dafür gebaut ist. /gewicht 78,3. Slash-Command, eine Nachkommastelle, fertig. Der Bot quittiert, trägt die Messung in sein Backend, alles gut.

Nur: das Abtippen nervt. Die Withings-Waage kennt mein Gewicht eh schon, drei Sekunden später liegt der Wert in der Withings-Cloud. Warum muss ich ihn dann noch einmal in eine andere App tippen?

Naheliegender Plan: n8n hat einen Withings-Trigger, einen Telegram-Bot-Node — ein Workflow in zehn Minuten und gut. Habe ich gebaut. Habe ich getestet. Funktioniert technisch alles.

Klingt überschaubar. War es nicht.

Die Wand

Was nicht funktioniert hat: die Nachricht kam beim Zielbot nicht an. Mein eigener Bot sendete brav in die Gruppe, in der beide Bots Mitglied sind. Logs sahen sauber aus, Telegram bestätigte den Send. Der andere Bot reagierte nicht.

Ich habe alles geprüft, was naheliegend war. Privacy-Mode des fremden Bots — vielleicht steht der auf „nur an mich gerichtete Nachrichten”. Berechtigungen — ist mein Bot Admin in der Gruppe? Format der Nachricht — exakt /gewicht 78,3, kein Zusatzzeichen, keine andere Codierung.

Nichts half. Stundenlange Suche, dann fand ich es in einem Forum-Posting: Telegram-Bots ignorieren standardmäßig Nachrichten anderer Bots. In Privatchats kommen sie gar nicht erst durch, in Gruppen sieht ein Bot Nachrichten anderer Bots nur, wenn der Empfänger-Bot explizit über die Bot-API anfragt — was die meisten nicht tun.

Mit anderen Worten: ich kann zwischen mir und einem fremden Bot keinen zweiten Bot als Boten einklemmen. Die Plattform verbietet das. Die einzige Adresse, von der aus der fremde Bot zuverlässig hört, ist die eines echten Telegram-Users.

Der andere Weg

Telegram hat zwei APIs. Die Bot-API ist der HTTP-Wrapper, mit dem die meisten Bots gebaut sind — sie ist einfach, weit dokumentiert, klar limitiert. Daneben gibt es die Client-API: dasselbe MTProto-Protokoll, das die offiziellen Apps auf iPhone und Desktop sprechen. Mit der Client-API kannst du dich aus einem eigenen Skript heraus als regulärer User einloggen und Nachrichten senden, als hättest du sie selbst getippt.

Klingt nach Grauzone. Ist es nicht. Telegram bietet die api_id und den api_hash für solche Anwendungen auf my.telegram.org ganz offiziell an — der Zweck ist explizit, dass Drittentwickler eigene Clients bauen können. Solange du keinen Spam betreibst und keine 50.000 Nachrichten pro Stunde verschickst, ist persönliche Automatisierung toleriert.

Für Python ist die ausgereifteste Bibliothek Telethon. Implementiert MTProto vollständig, async-fähig, eine Zeile zum Senden. Nach einem einmaligen Login (Telefonnummer, SMS-Code, optional Zwei-Faktor-Passwort) legt Telethon eine SQLite-Datei .session ab, die wie ein zusätzlich angemeldetes Gerät funktioniert. Ab dann sendet das Skript ohne Interaktion.

Zwei Minuten ausprobiert, eine Probe-Nachricht an den fremden Bot. Kam an. Wurde verarbeitet.

Die my.telegram.org-Hürden

Bevor du Telethon nutzen kannst, brauchst du eine eigene App-Registrierung. Auf my.telegram.org einloggen, „API development tools” öffnen, Formular ausfüllen. So weit, so trivial.

In der Praxis kommen die ersten Anläufe mit einem generischen Dialog zurück: nur das Wort ERROR, kein Hinweis, was schiefging. Drei Versuche, drei ERROR. Recherche im Forum ergab:

  • Short name darf nur Buchstaben und Zahlen enthalten, keine Leerzeichen — die UI nimmt sie an, das Backend lehnt ab
  • URL und Description sind faktisch Pflicht, auch wenn die UI das nicht markiert
  • Nach zwei oder drei Fehlversuchen greift ein 24-Stunden-Rate-Limit, danach lehnt das Backend pauschal alles ab
  • Im Inkognito-Fenster und ohne Browser-Extensions klappt es zuverlässiger — manche Adblocker stören die Submit-Antwort

Beim vierten Anlauf, nach 24 Stunden Pause und mit ausgefüllten Pflichtfeldern, ging es. App-Eintrag erstellt, api_id und api_hash notiert.

Das Skript

Architektur ist schlicht: das Skript pollt regelmäßig die Withings-API, holt neue Messungen, schickt jede einzelne an Telegram, merkt sich was schon gesendet wurde. Datenfluss in einer Zeile: Withings-Cloud → Polling-Skript → Telethon → eigener User-Account → Ziel-Bot. Daneben zwei Zustands-Dateien, state.json für den Withings-Refresh-Token und den letzten verarbeiteten Zeitstempel, und telegram.session für die Telethon-Session.

Der Withings-OAuth-Flow läuft einmalig über init_oauth.py. Das Skript startet einen lokalen HTTP-Server auf localhost:8765, öffnet die Withings-Authorize-URL im Browser, fängt den Callback und tauscht den Authorization-Code gegen Access- und Refresh-Token. Letzterer landet in state.json.

Wichtig dabei: Withings rotiert den Refresh-Token bei jedem Tausch. Wer den neuen Token nicht sofort persistiert und stattdessen mit dem alten weitermacht, wird beim nächsten Lauf abgelehnt. Daher schreibt das Skript den neuen Token sofort nach dem Refresh-Aufruf zurück, vor allem anderen.

Der Telegram-Login ist init_telegram.py. Telefonnummer und API-Credentials aus der .env, beim ersten Lauf kommt ein Code per Telegram-App rein (nicht SMS — die kommt erst nach Cooldown). Bei aktivem Zwei-Faktor zusätzlich das Passwort. Telethon schreibt telegram.session, ab dann läuft jeder Aufruf ohne Interaktion.

Das eigentliche Sende-Stück ist kurz:

async def send_messages_via_user(api_id, api_hash, target, messages):
    client = TelegramClient(str(SESSION_PATH), api_id, api_hash)
    await client.connect()
    try:
        if not await client.is_user_authorized():
            raise RuntimeError("Session nicht autorisiert.")
        for text in messages:
            await client.send_message(target, text)
    finally:
        await client.disconnect()

target ist entweder der Username des Zielbots mit @-Präfix oder eine numerische Chat-ID. Mehr nicht.

Die Formatierung der Nachricht ist trivial, aber wichtig: Withings liefert Gewicht als Integer plus Exponent (value=783, unit=-1 bedeutet 78,3). Daraus wird /gewicht 78,3 — Komma deutsch, eine Nachkommastelle. Der Zielbot interpretiert das genauso, als hätte ich es selbst getippt.

Was bleibt

Die ganze Sache hat ein Wochenende gekostet. Davon vielleicht zwei Stunden für den Code selbst — der Rest war: verstehen, warum n8n schweigt; my.telegram.org-Rate-Limit aussitzen; den richtigen Weg zur User-API erst finden. Wenn ich das vorher beschrieben bekommen hätte, hätte ich gesagt „das mache ich Sonntagvormittag fertig”. Sonntagvormittag war ich noch nicht beim Aha.

Zwei Sachen nehme ich mit. Erstens: wenn eine Plattform Stille zurückgibt, statt einen Fehler, lohnt das Forum mehr als der eigene Code. Bot-zu-Bot-Block hätte ich aus keiner Doku gelernt, nur aus Berichten anderer, die in dieselbe Wand gerannt sind. Zweitens: User-APIs sind nicht zwielichtig, sie sind nur weniger beworben. MTProto plus Telethon ist eine erwachsene Toolchain, die ohne Tricks funktioniert.

Der Code liegt auf GitHub unter staude/withings-to-telegram, GPL v3. Wer einen eigenen Bot-zu-User-Brückenfall hat: clone, .env ausfüllen, init_oauth.py plus init_telegram.py laufen lassen, fertig.

Heute morgen war meine erste Wiegung ohne manuelles Eintippen. Der fremde Bot hat freundlich quittiert.

Links und weiterführende Quellen

Eigene Repos

Telegram

Withings

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert