Sprachnachrichten sind so eine Sache… Viele meiner Freunde nutzen sie, um fix und ohne Tippen eine Nachricht zu versenden. Ich persönlich finde sie ätzend!
Zum einen habe ich selten etwas dabei, mit der ich sie direkt abrufen kann. Meine Smartwatch spielt sie nicht ab und Whatsapp Web lässt das ganze Büro mithören.
Viel Schlimmer ist aber der Umstand, dass die meisten Sender einer Sprachnachricht sich nicht kurzfassen. Ich bin viel zu ungeduldig, um jemanden 5 Minuten beim Denken zuzuhören. Wer mir so lange etwas mitteilen will, kann auch anrufen.
Letzter Punkt ist, dass ich Sprachnachrichten nicht mit einem Blick erfassen kann. Wer mir Sprachnachrichten mit wichtigen Details wie Treffpunkt, Uhrzeit oder irgendwelchen Erinnerungen schickt, zwingt mich den ganzen Brei wieder und wieder anzuhören.
Es musste eine Lösung her! Kann mir Whatsapp nicht mit Hilfe von Spracherkennung den Inhalt einer Sprachnachricht verraten?
Das wär doch mal ein Projekt für einen kalten Winterabend!
Gesagt getan, Kaffee, Notebook und Kekse geschnappt und ab ins Bettchen: Hackerabend!
Was brauche ich:
- Whatsapp Nachrichten auf einem Server empfangen – Kann Twilio glaub ich
- Sprache in Text verwandeln – Müsste Google irgendwie können – Andersherum hab ich ja schon gemacht.
- Beides Verbinden – Geht hoffentlich auf dem Webserver von mobfish oder spätestens mit Google Cloud Run
Whatsapp Api über Twilio
Facebook tut sich schwer damit, Whatsapp für Bots und andere spamverdächtige Services zu öffnen. Was auf der einen Seite gut ist, ist irgendwo auch schade.
Früher gab es noch inoffizielle APIs, mit der man den einen oder anderen Bot entwickeln konnte. Facebook geht aber vehement dagegen vor und so lässt sich damit nichts mehr nachhaltig umsetzen.
Aktuell ist mir kein API-Projekt bekannt dass noch funktioniert. Alleine schon, weil man inzwischen nicht mehr jede Nummer bei anmelden kann. Früher habe ich z.B. meinen Festnetzanschluss bei Whatsapp angemeldet.
Whatsapp bietet inzwischen aber doch eine Business Api an, diese ist restriktiert:
- Man kann Nachrichten empfangen aber nur innerhalb von 24 Stunden zurückschreiben. Nach diesem Zeitfenster lässt sich keine Mitteilung mehr senden -> Damit wird Spam unterbunden.
- Will man außerhalb des Zeitfensters eine Nachricht senden, muss ein Template benutzt werden, dass Facebook vorher freigeben muss. Damit lassen sich Nachrichten wie „Dein Paket kommt {{morgen}} an“ versenden.
Reicht für mich!
Ich will ja nur eine Sprachnachricht empfangen und anschließend den transkribierten Text zurückschicken. Und Google sollte in der Lage sein, selbst den langweiligsten Sprachnachrichten-Autor innerhalb 24 Stunden in Text zu verwandeln.
Warum dann Twilio?
Man kann vermutlich die Whatsapp Business API auch direkt an seine Software anbinden. Habe mir die Zeit nicht genommen, das einmal genauer anzusehen. Facebook bietet dafür ein Formular an und da hatte ich keine Lust drauf, es auszufüllen. Das bremst mich heute nur und ein Hacker-Abend erlaubt mir auch Umwege und Tricks zu nutzen, mit denen ich schneller ans Ziel komme.
Bei Twilio gibt es eine Entwicklersandbox, wo man direkt loslegen kann und eine bereits verifizierte Nummer verwendet. Den Antrag auf meine eigene Whatsapp for Business Nummer kann ich in den nächsten Tagen noch stellen.
Die ganze Magie funktioniert dann wie folgt:
Wenn ich eine Nachricht an meinen Sandbox-Kontakt in Whatsapp schicke, wird automatisch ein Webhook ausgelöst. Twilio fragt also einen von mir gewählten Webserver, was er mit der Nachricht anfangen soll.
Im einfachsten Fall liefert man ein simples XML-File zurück, das die Antwort beinhaltet. Man bekommt aber auch alle Informationen, die man braucht, um dem Nutzer später noch eine Nachricht zuschicken zu können.
Hier mal die POST-Daten des Webhooks:
{
"MediaContentType0":"audio\/ogg",
"SmsMessageSid":"XXX",
"NumMedia":"1",
"SmsSid":"XXX",
"SmsStatus":"received",
"Body":"",
"To":"whatsapp:+1415523XXXX",
"NumSegments":"1",
"MessageSid":"XXXX",
"AccountSid":"XXXX",
"From":"whatsapp:+491516732XXXX",
"MediaUrl0":"https:\/\/api.twilio.com\/2010-04-01\/Accounts\/XXX\/Messages\/XXX\/Media\/XXX",
"ApiVersion":"2010-04-01"
}
Super! Alles drin! Ich kann mit „MediaContentType0“ erkennen, dass ich eine Audio-Datei erhalten habe. Es gibt die Abendernummer und das beste:
Es gibt direkt einen Link zum Attachment! (Siehe „MediaUrl0“)
Sprachnachricht aus Whatsapp exportieren
Ich dachte eigentlich, dass ich überraschend schnell fertig werde mit meinem kleinen Projekt. Doch dieser Teil hat es mir echt nicht leicht gemacht. Mein erster Versuch war, die Datei stumpf herunter zu laden:
file_put_contents('audio.ogg', file_get_contents($_REQUEST['MediaUrl0']));
Nur gemeiner Weise bekommt man dann eine XML-Datei zurück. Anschließend habe ich das Twilio-SDK installiert und ewig herum probiert, an die eigentliche Mediendatei zu kommen.
Nichts zu machen.
Immer bin ich wieder zu der Adresse hinter „MediaUrl0“ zurückgekehrt. Die Dokumentation schreibt auch, dass es die Adresse zur Mediendatei ist.
WARUM BEKOMME ICH DANN DIESE BEKLOPPTE XML-DATEI?!
Bis ich die Url am Ende mal in den Browser kopiert habe und wupps, bekam ich die Sound-Datei. Es gibt also noch einen Redirect-Header, der von „file_get_contents“ ignoriert wird. Dann war alles super einfach:
$fp = fopen ("audio.ogg", 'w+');
$ch = curl_init($_REQUEST['MediaUrl0']);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
fclose($fp);
Spracherkennung durchführen
Jetzt liegt die Sprachdatei bei mir auf der Platte (oder auf meinem Webserver). Nun muss mir Google nur noch sagen, was da drin ist.
Über den Speech-Client ist das ein Kinderspiel, bis auf…
Google Frist die OGG-Datei der Sprachnachricht nicht. Auch wenn es den passenden Parameter dafür gibt. Es bleibt mir also nichts anderes Übrig, als die Datei vorher umzuwandeln.
Dabei hilft mir ffmpeg:
shell_exec("ffmpeg -y -i audio.ogg -c:a flac audio.flac");
Dann die Spracherkennung starten:
$client = new SpeechClient(array('keyFile' => json_decode(file_get_contents('key.json'), true)));
$config=(new RecognitionConfig())
->setEncoding(AudioEncoding::FLAC)
->setSampleRateHertz(48000)
->setLanguageCode("de-DE");
$audio = (new RecognitionAudio())->setContent(file_get_contents('audio.flac'));
$response=$client->recognize($config, $audio);
$result=$response->getResults()->getIterator()->current();
$transcribed=$result->getAlternatives()[0]->getTranscript();
$client->close();
Und am Ende den erkannten Text als XML ausgeben, damit das Ganze als Whatsapp-Antwort direkt angezeigt wird.
echo '<?xml version="1.0" encoding="UTF-8"?>';
?>
<Response>
<Message><Body><?php echo $transcribed;?></Body></Message>
</Response>
Twilio und Google Spreech2Text über Cloud Run verbinden
Da ich ffmpeg für die Konvertierung verwenden muss, kann ich den ganzen Krempel leider nicht mehr direkt auf unserem Firmen-Webserver hosten. Dort lässt sich ffmpeg nur gegen Aufpreis installieren. Da dies ein privates Hacking-Projekt von mir ist, muss eine andere Lösung her.
Seit geraumer Zeit nutze ich für solche Dinge, den Cloud-Run Service von Google.
Im Endeffekt baut man sich einen kleinen Docker-Container der auf Port 80 reagiert und kann damit, quasi jede beliebige Serverumgebung als Webservice anbieten. Das Gute dran ist, das der Container nur während der Ausführung läuft und somit sonst keine Kosten verursacht. Dieses Projekt dürfte nicht mal die Freigrenze überschreiten.
Das Dockerfile ist recht übersichtlich:
FROM php:7.2-apache
# Kram reinkopieren
COPY index.php /var/www/html/
COPY key.json /var/www/html/
COPY composer.json /var/www/html/
# Install requirements
RUN sed -i 's/80/${PORT}/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf && \
apt-get update && \
apt-get install wget -y && \
apt-get install zip -y && \
apt-get install unzip -y && \
curl -sS https://getcomposer.org/installer -o composer-setup.php && \
php composer-setup.php --install-dir=/usr/local/bin --filename=composer && \
apt-get install ffmpeg -y && \
cd /var/www/html/ && \
composer install && \
apt-get -y clean
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
Anschließend, das Ganze in die Cloud schubsen:
gcloud config set project whatsappbot-XXX
gcloud builds submit --tag gcr.io/whatsappbot-XXX/whatsappbot
gcloud beta run deploy --region europe-west1 --platform managed --image gcr.io/whatsappbot-XXX/whatsappbot
Das fertige Ergebnis:
Am Ende hat es mich doch die eine oder andere Stunde mehr gekostet als ursprünglich geplant. Vor allem, diese Fummelei an die Sounddatei zu kommen. Dabei war es eigentlich so einfach, ich war nur zu lange auf dem falschen Weg.
Wer sich nicht die Mühe machen will das Projekt nachzubauen, kann sich das kleine Video hier anschauen. Es zeigt den Whatsapp-Bot in Aktion.
Beim Testen im Nachgang ist mir noch aufgefallen, dass ich doch den API-Endpunkt für „Lange Transkriptionen“ verwenden muss. Manche Sprachnachrichten, die ich bekomme, sind viel zu lang für den „recognize“ Endpunkt.
Es war auf jeden Fall ein spannendes Projekt, dass Lust auf weitere Basteleien macht.
Zum Beispiel:
- Ein Bot der Fotos von Visitenkarten auswertet und einem einen Kontakt zurückschickt
- Ein Bot der eine Bitcoin-Wallet abbildet (habe ich bei Telegram mal gesehen)
- Ein Bot der Bilder über Whatsapp empfängt und auf einer Fotowand darstellt (was für die nächste Hochzeit)
…im neuen Jahr vielleicht. Erholsame Feiertage euch!
Beitragsphoto by Christian Wiediger on Unsplash
Cool gemacht, toll geschrieben und auch sehr interessant (für Leute wie mich) ?
Auch sehr cool. Es gab schon so einige momente wo ich das auch gerne hätte.