Mit Telepot Bots für Messenger Telegram schreiben
Der Messenger-Dienst Telegram bietet ein praktisches API, um eigene Bots zu implementieren. Mit dem Python-Wrapper Telepot gelingt das mit wenigen Zeilen Code.
Der selbst geschriebene Telegram-Bot protokolliert Gefühlslagen.
Wer über das eigene Leben reflektiert, erinnert sich schnell an die Momente großer Freude und großen Leids. Bei den kleinen Katastrophen und alltäglichen Stimmungen wird es aber schwierig: Wie oft fühle ich mich gut? Wie fühle ich mich im Rest der Zeit? Für den Moment sind das einfache Fragen, aber über die Zeit nur schwer zu quantifizieren. Ich wollte es genauer wissen und Daten sammeln, welche Stimmungen ich über den Tag durchlebe. Ein Blatt Papier oder eine Tabelle am Rechner kam dafür nicht infrage, weil ich zu schnell vergessen würde, die Stimmung aufzuschreiben. Ein Smartphone mit Telegram habe ich aber immer dabei. Und wenn mich ein Bot jede Stunde fragt, wie es mir geht, werde ich vermutlich oft darauf antworten. Da Telegram-Bots auch Tastenfelder anzeigen können, kann der Bot bei jeder geloggten Stimmung auch gleich fragen, ob sie ein gutes oder schlechtes Gefühl ist. Ausprobieren kann den Bot jeder Telegram-Nutzer: @mood_tracker_bot.
Gespräch mit dem @BotFather
Jeden neuen Telegram-Bot muss man mit einem Gespräch mit dem @BotFather anmelden. Dieser besondere Bot verwaltet die Namen aller Bots und legt deren Profilbilder und Beschreibungen fest. Vom @BotFather bekommt man auch das API-Token, mit dem Telepot sich später mit dem Bot verbindet. Im Git-Repository meines MoodTrackerBot ist der API_KEY die einzige fehlende Information. Wenn Sie den Code ausprobieren wollen, registrieren Sie beim @BotFather einen eigenen Bot und tragen dessen API-Token in einer Textdatei namens apikey.py hinter API_KEY= ein.
Erste Schritte mit Telepot
Dank Telepot müssen Sie im eigenen Python-Code keine Requests an das Telegram-API schicken. Die Bibliothek kapselt die Aufrufe so, dass Sie einfach ein Bot-Objekt erzeugen und Nachrichten verschicken können:
import telepot
from apikey import API_KEY
bot = telepot.Bot(API_KEY)
bot.sendMessage(user,
"In einem Wort: " +
"Wie fühlst du dich gerade?")
Damit das Klappt, muss in user eine Telegram-User-ID stehen. Das ist nicht etwa das eindeutige Kürzel mit @ am Anfang, sondern eine lange Nummer. Eine gültige User-ID erfährt der Bot beispielsweise, wenn er Nachrichten erhält.
User-IDs einsammeln
Nutzer können mit einem Bot ganz normal chatten. Um auf die Nachrichten zu reagieren, enthält Telepot eine message_loop, in der man Callback-Funktionen registriert. Die Callback-Funktion bekommt die Nachricht msg als Parameter, wobei die neben dem reinen Text auch Absender und Typ enthält. Um an die Metadaten der Nachricht zu kommen, bietet Telepot eine praktische Funktion:
content_type, chat_type, chat_id = \
telepot.glance(msg)
Die User-ID erfährt man mit:
user_id = msg['chat']['id']
Für Bot-Kommandos gibt es bei Telegram und viele anderen Chat-Systemen die Konvention, dass sie mit einem Slash beginnen. Telegram hebt die so formatierte Kommandos automatisch hervor und "tippt" sie automatisch ein, wenn man sie wie einen Link anklickt. Sendet man dem MoodTrackerBot das Kommando /start, erfährt er mit der Nachricht auch die User-ID und speichert sie in einer Textdatei. Einmal pro Stunde fragt er dann alle User-IDs aus der Textdatei mit sendMessage(), wie es ihnen gerade geht.
Nachfrage mit Buttons
Telegram-Bots können anders als menschliche Chat-Partner Antwortmöglichkeiten über Buttons vorgeben. Sie definieren die Buttons über ein InlineKeyboardMarkup, das die Telegram-Apps dann anzeigen:
keyboard = InlineKeyboardMarkup(
inline_keyboard=[
[InlineKeyboardButton(
text='mies',
callback_data='-2'),
InlineKeyboardButton(
text='schlecht',
callback_data='-1'),
InlineKeyboardButton(
text='neutral',
callback_data='0'),
InlineKeyboardButton(
text='gut',
callback_data='1'),
InlineKeyboardButton(
text='super',
callback_data='2')],
])
Schickt man ein solches Objekt mit einer Chat-Antwort mit, zeigt Telegram zuerst den Antworttext und dann die Buttons an:
bot.sendMessage(chat_id,
'Wie gut fühlt sich das an?',
reply_markup=keyboard)
Beim Antippen eines Buttons erzeugt Telegram keine normale Chat-Antwort, sondern eine callback_query. Für die ruft Telepot eine andere Callback-Funktion auf, die aber ganz ähnlich funktioniert: Wieder steht in msg sowohl User-ID als auch Antwort und Antwortzeit:
query_id, from_id, query_data = telepot.glance(msg,
flavor='callback_query')
append_quality(query_data, from_id, int(utc_time()))
Die selbst geschriebene Funktion append_quality() speichert den gesendeten Wert ab.
Nachdem alles gespeichert ist, sagt der Bot noch Bescheid, dass alles okay ist:
bot.answerCallbackQuery(query_id, text='OK, gespeichert.')
Diese Antwort erscheint als kurzer Hinweis im Telegram-UI, bleibt aber nicht als Antwort im Chat-Verlauf stehen.
Bot-Baukasten
Mit diesen Einzelteilen lässt sich in weniger als 100 Zeilen Python-Code ein Bot zusammenbauen, der die Stimmungslage abfragt. Er registriert dafür zuerst die Callback-Funktionen für normale Chats und für die Antworten auf Inline-Keyboards:
bot.message_loop({
'chat': on_chat_message,
'callback_query': on_callback_query
})
In on_chat_message(), der Callback-Funktion für normale Chats, müssen der Bot zunächst prüfen, ob content_type == 'text', da Telegram auch Bilder, Videos, Dateien und Audio verschicken kann. Wenn es eine Textnachricht ist, könnte es aber auch ein Befehl sein. Sollen mehrere Antworten das Gleiche auslösen, kann man alle mit einer solchen Abfrage prüfen:
if msg['text'] in ["/start",
"/start start", "Hallo",
"Hi", "Start"]:
So eine Behandlung brauchen Sie dann für alle Befehle:
elif msg['text'] in ["/stop",
"Hör auf", "Stop", "Ende"]:
Ist es kein Befehl, geht der MoodTrackerBot davon aus, dass man ihm ein Gefühl geschrieben hat, das er abspeichern muss. Um die Befehle einzelnen Nutzern zuordnen zu können, speichert er die User-ID, einen Zeitstempel und die Beschreibung des Gefühls – Kommas werden entfernt, damit sie das CSV-Format der Datei nicht durcheinanderbringen:
else: # It must be a mood
with open("mood_data.csv", 'a') as data_file:
data_file.write("\n" + ", ".join([str(user_id), str(msg['date']),
"".join(msg['text'].split(','))]))
Danach fragt der Bot über Buttons nach, ob es ein mieses, schlechtes, neutrales, gutes Gefühl oder sogar richtig super war. Die Antwort erreicht den Bot in Form einer ganzen Zahl zwischen -2 und 2, die er an den aktuell gespeicherten Eintrag in der CSV-Datei anhängt, wenn dort noch keine Zahl steht. Falls dort schon etwas steht, legt der Bot einen identischen Eintrag mit einem neuen Timestamp und der neuen Bewertung des Gefühls an.
Einmal fragen pro Stunde
Damit der Bot einmal pro Stunde fragt, reicht es, in einer Endlosschleife sendMessage() aufzurufen und nach jedem Schleifendurchlauf sleep(60*60) aus dem time-Modul aufzurufen.
Der Aufruf von sleep() blockiert ein Python-Programm normalerweise für die komplette Zeit. Telepot nutzt für die message_loop aber eigene Threads, sodass es nichts macht, wenn der Haupt-Thread mit Warten komplett ausgelastet ist.
Bot statt App
Der MoodTrackerBot zeigt, wie schnell man mit Telepot zu einem nützlichen Helferlein kommt. Richtige Konversationen führt man mit den eigenen Bots dabei nicht; sie ersetzen eher kleine Apps. Für viele Aufgaben reichen die Befehle und Buttons eines Bots nämlich aus. Die aktuelle c't 9/17 zeigt beispielsweise, wie Sie einen Bot programmieren, der zu Hause die Bilder und Videos von Überwachungskameras per Telegram versendet. Natürlich gehen Push-Benachrichtigungen, Bildversand, Auswahlmenüs und Sprachaufnahmen auch mit einer eigenen App – aber nur mit tausenden Zeilen Java-Code und einigen Monaten für die Implementierung. Die Zeit können Sie sich sparen. (pmk)