senäh

17senäh und so…

Coding-Logo

Allgemein, WordPress
23. Jan 2013
Kommentare: 3

Google Reader API: Kommentar-Feed abonnieren und direkt in Ordner verschieben

Kategorien: Allgemein, WordPress | 23. Jan 2013 | Kommentare: 3

Wie man das hin und wieder als Programmierer eben so macht löse ich manchmal Probleme einfach aus Lust und Laune. Lukas hatte über Twitter gefragt, ob es nicht eine Möglichkeit gäbe mit einem Klick den RSS-Feed der Kommentare zu einem Blogartikel zu abonnieren. Zusätzlich sollte der Feed gleich in einen extra dafür vorgesehenen Ordner verschoben werden.

Ich habe absolut kein Bedarf für etwas derartiges. Die Realisierung hat mich trotzdem interessiert. Dabei habe ich die Chance genutzt und mich endlich mal mit einer Google API beschäftigt (obviously mit der des Google Readers).

Dabei rausgekommen ist etwas, das ich label-love taufte. Ein Automator-Service bzw. Keyboard Maestro Makro (also Mac-Only) für das Abonnieren eines Kommentar-Feeds (WordPress only!).

Die Einzelheiten der Umsetzung beschreibe ich hier. Warum? Erstmal weil ich’s kann 😀 Ein Blog will mit Inhalten befüllt werden. Außerdem wartet die Reader API bzw. das, was dazu so im Netz rumschwirrt, mit einigen Überraschungen auf. Vielleicht hilft dieser Blogpost dabei, ein paar Unklarheiten zu beseitigen.

Lessons to be learned

Einerseits geht es natürlich um die Verwendung der Google Reader API, die bis heute als inoffiziell gilt. Die Reihenfolge dabei lautet:

  1. Authentication-Prozess (a.k.a. Login),
  2. API-Token holen und
  3. API-Methoden aufrufen.

Die Kommunikation erfolgt über GET- und POST-Requests, die wir mithilfe von cURL, einer Bibliothek für den Aufruf von URLs über die Konsole, umsetzen. Der Großteil der Magic geschieht also über ein Shell-Script. Ich bin auf dem Gebiet ziemlicher Newbie, dementsprechend waren viele Konzepte für mich neu. Weil ich sowas als verdammt hilfreich empfunden hätte, werde ich daher mit ein paar Shell-Basics beginne, die Pros natürlich gern überspringen dürfen.

Shell-Basics

Man kommt nicht drum herum. So anti-sexy die Erscheinung der meisten Terminal-Fenster auch sein mag und so geekig man sich auch dabei fühlt – die Konsole ist ein sehr, sehr mächtiges Tool. Man kann sie für allerhand sinnvolle Aufgaben verwenden. Oder eben für ein bisschen Reader-API-Spielerei.

Pipes und sed

Pipes befähigen zum Verketten von Output. Dadurch lässt sich ein Kommando ausführen, dessen Output direkt als Input des nächsten dient. Beispiel:

echo "Pipo + Enno = senäh" | sed -E "s/Pipo/Ginger/g"
# output: "Ginger + Enno = senäh"

Die Pipe selbst ist dabei der weder-normale-noch-back-Slash, den man vielleicht vom logischen Oder (||) kennt. Im Beispiel wird die Ausgabe Pipo + Enno = senäh an den Eingang des sed-Kommandos weitergegeben. Dabei wird der String Pipo durch Ginger ersetzt.

sed selbst ist ein nützliches Tool zum Ersetzen durch reguläre Ausdrücke. Bevor mir die-hard-Unixer jetzt den Blog DDoSen: natürlich ist sed nicht nur dafür gemacht, ich ziehe daraus aber den primären Nutzen. Schauen wir uns noch mal die Bestandteile im Einzelnen an:

sed -E "s/Pipo/Ginger/g"
  • sed ist das eigentliche Kommando
  • die Option -E sagt uns, dass wir Regexen im erweiterten (mächtigeren) Modus verwenden wollen
  • der folgende String setzt sich zusammen aus s/RegexPattern/Replacement/g (s und g sagen sed, dass wir das Pattern auch wirklich ersetzen wollen)

Noch Fragen?

cURL

Mit cURL können wir wie eingangs erwähnt URLs von der Kommandozeile aufrufen. Die hier gezeigten Befehle folgen i.d.R. folgender Form:

curl -s -d "param1=value1&param17=value17" http://www.senaeh.de

-s wie silent befiehlt cURL sich darüber auszuschweigen, was es im Hintergrund so macht. Es soll die Ausgabe gefälligst für sich behalten, da wir nur die Server-Response von der aufgerufenen URL haben wollen, keine Fortschrittsanzeige und auch keine sonstigen Infos. -d wie data erlaubt uns Query-Parameter mitzuschicken. Schlussendlich folgt dann die URL, die es aufzurufen gilt.

Der Output des Befehls ist dann der Inhalt der von der aufgerufenen URL zurückgegeben wird. Im obigen Beispiel wäre es also das HTML der senäh-Startseite.

In der konkreten Anwendung folgen noch einige weitere Optionen, die ich dann bei Bedarf erkläre.

Von Backticks, Kommandoception und Backslashes

Variablenzuweisung in der Shell geht so:

variablen_name="deine Mudda"

Und wenn ich die Ausgabe eines Programms in eine Variable schreiben will? Es gibt sicher elegantere Lösungen, aber ich habe es so gemacht:

variablen_name="$(echo "deine Mudda")"

Den auszuführenden Befehl also einfach in “$()” gewrappt. Und wenn man jetzt statt einem String im echo die Ausgabe eines weiteren Programms haben möchte? Backticks (`) to the rescue.

variablen_name="$(echo `curl -s -d "param1=value1&param17=value17" http://www.senaeh.de`)"

Übersicht kann man durch Backslahes schaffen.

variablen_name="$(echo `curl -s \
-d "param1=value1&param17=value17" \
http://www.senaeh.de`)"

So. Fit genug? Dann mal ran an die eigentliche Lösung.

Das Shell-Script

Config und Feed-URL

Fangen wir an mit etwas Config. Wir brauchen Email-Adresse und Passwort für den Login. Außerdem den Namen des Ordners, der natürlich im Reader bereits angelegt ist.

mail="MY_EMAIL"
pass="MY_PASSWORD"
label="MY_FOLDER"

Dann machen wir aus der aktuellen URL, die an das Skript übergeben werden muss (später mehr dazu), eine Feed-URL. Das bedeutet der URL das Präfix feed/ (das will der Google-Reader so) und Suffix /feed/ (das will WordPress so) zu verpassen. Außerdem müssen ggf. Parameter und Hashes entfernt werden. Aus

http://www.senaeh.de/google-reader-api-abonnieren-ordner#readerShellScript?comment_send=true

wird dann

feed/http://www.senaeh.de/google-reader-api-abonnieren-ordner#readerShellScript?comment_send=true/feed/

Der Befehl dazu:

url="feed/"$(echo $url | sed -E 's/^([^#?]+)(\?.*)?(#.*)?$/\1/g' | sed -E 's/\/$//g')"/feed/"

Viel Regex-Magic am Werk. Für ein erstklassiges Regex-Tutorial bitte hier entlang.

Authentication

Als nächstes müssen wir uns bei Google einloggen um einen Auth-Token zu erhalten, mit dem wir anschließend einen API-Token bekommen.

Zuerst müssen wir einen POST-Request auf die URL https://www.google.com/accounts/ClientLogin abschicken. Dabei mitzugeben:

  • accountType (GOOGLE) und service (reader) sowie
  • URL-kodierte Versionen von Email und Passwort.

Beim URL-encoden hilft uns cURL mit der Option –data-urlencode. Die Request-Methode (POST) ändern wir über die Option -X.

Daraufhin erhalten wir eine Antwort mit 3 Tokens (sowohl Name als auch Wert):

  • SID
  • LSID
  • Auth

Wir brauchen den letzten. Darum schließt sich dem cURL-Aufruf mal wieder eine Ersetzung durch meine neuen besten Freunde sed und Regex an, damit wir am Ende nur den Wert des Auth-Tokens extrahieren. Konkret:

auth="$(echo `curl -sX POST \
-d "accountType=GOOGLE&service=reader&" \
--data-urlencode "Email=$mail" \
--data-urlencode "Passwd=$pass" \
https://www.google.com/accounts/ClientLogin`| \
sed -E 's/^.*Auth=([^ ]+).*$/\1/g')"

API-Token

Um API-Calls ausführen zu dürfen, brauchen wir einen entsprechenden Token. API-Endpoint: /api/0/token. Den soeben gewonnenen Auth-Token geben wir dabei in einem Google-eigenen Header mit. Eigene Header lassen sich in cURL über die -H Option angeben:

token="$(echo `curl -s \
-H "Authorization:GoogleLogin auth=$auth" \
http://www.google.com/reader/api/0/token`)"

Das Ergebnis ist ein String, der tatsächlich mit 2 Slashes beginnt. Nicht darüber wundern. Das muss so.

Feed abonnieren und in den richtigen Ordner verfrachten

So, endlich der API-Call um den Feed zu abonnieren. Der Endpoint dazu lautet /api/0/subscription/edit Der Google-Header mit dem Auth-Token muss mit. Außerdem muss die Aktion beschrieben werden (ac=subscribe) und der API-Token darf nicht fehlen (T=$token). Die zu abonnierende URL muss kodiert als Parameter s übertragen werden.

Interessant wird es beim Ordner, in dem der Feed landen soll. Hier handelt es sich um eine sogenannte User-created tag stream ID. Weeßte Bescheid. Sieht dann so aus:

user/-/label/meinOrdner

Anstelle des Minus sollte eigentlich die User-ID stehen. Der aktuell zum API-Token gehörende User kann aber über das Minus referenziert werden. Ein dankbarer Shortcut, da wir uns die ID sonst auch erst über die API hätten holen müssen.

Der konkrete Befehl sieht so hier aus.

resp="$(echo `curl -s \
-H "Authorization:GoogleLogin auth=$auth" \
--data-urlencode "s=$url" \
--data-urlencode "a=user/-/label/$label" \
-d "ac=subscribe&T=$token" \
http://www.google.com/reader/api/0/subscription/edit`)"

Bei Erfolg kommt ein simples OK zurück, dass wir in der Variable resp wie Response speichern.

Der Rest ist Formsache

Was jetzt noch folgt ist die Auswertung der Response und ein entsprechendes Feedback an den Benutzer. Das ist Aufgabe von AppleScript und recht trivial, weswegen ich es hier nicht erklären werde. Die eigentliche URL kommt ebenfalls durch AppleScript.

Schaut euch bei Interesse einfach die Daten auf GitHub an. In der Readme stehen noch ein paar weitere Anweisungen, falls ihr das ganze bei euch zum Laufen bringen möchtet.

Zum Abschluss gibt es hier noch mal das komplette Shell-Script nach dem zum Zeitpunkt der Veröffentlichung aktuellen Stand.

# Config
mail="MY_EMAIL"
pass="MY_PASSWORD"
label="MY_FOLDER"

# fetch Login-Auth for Token
auth="$(echo `curl -sX POST \
-d "accountType=GOOGLE&service=reader&" \
--data-urlencode "Email=$mail" \
--data-urlencode "Passwd=$pass" \
https://www.google.com/accounts/ClientLogin`| \
sed -E 's/^.*Auth=([^ ]+).*$/\1/g')"

# fetch Token
token="$(echo `curl -s \
-H "Authorization:GoogleLogin auth=$auth" \
http://www.google.com/reader/api/0/token`)"

# subscribe to feed
resp="$(echo `curl -s \
-H "Authorization:GoogleLogin auth=$auth" \
--data-urlencode "s=$url" \
--data-urlencode "a=user/-/label/$label" \
-d "ac=subscribe&T=$token" \
http://www.google.com/reader/api/0/subscription/edit`)"

# output for Keyboard Maestro
echo $resp

Autor: Enno

Ich bin Enno. PHP ist mein Ding, aber auch alles Neue rund um die Themen HTML5, CSS3 & Co finde ich interessant. Ich mag es Leuten zu helfen und mein Wissen weiterzugeben. Sollte dir mein Beitrag gefallen haben, lass doch nen Kommentar da oder benutze einen der Social Buttons, um deinen Dank auszudrücken ;)