Unblock-Us Auto-Update Skript auf Python portiert

Kurz vor Weihnachten noch eine kurze Nachricht:

Ich habe mein Unblock-Us Auto-Update Skript auf Python portiert. Der ein oder andere hat vielleicht eine Abneigung gegen Shell-Skripte, deshalb gibt es jetzt eine Alternative.

Zu finden ist das Ganze nach wie vor auf GitHub und steht nach wie vor unter MIT-Lizenz.

Junecloud Delivery Status per PHP auslesen

Ich habe mich mal wieder hin gesetzt und einen sehr unsauberen, unschönen und schlecht dokumentierten Scraper in PHP geschrieben. Diesmal geht es um die Delivery Status Sendungsverfolgung von den Jungs und Mädels von Junecloud. Delivery Status Touch ist eine meiner „First-Homescreen“-Apps auf iPhone und iPad. Es gibt auch ein Dashboard-Widget für OS X (aber wer benutzt schon noch das Dashboard?).

Ich hatte jetzt das Problem, dass ich gerne alle Informationen zu Sendungsverfolgungen unterschiedlicher Versandunternehmen, die ich sowieso alle bei Delivery Status pflege, in auswertbarer Form auslesen wollte. Leider gibt es von Junecloud selbst dafür keine API, also musste ich, wie schon bei Runtastic, selbst Hand anlegen.

Das Ergebnis nennt sich „php-deliverystatus“ und steht ab sofort unter MIT Lizenz auf GitHub zum Download bereit.

Und so funktioniert es:

<?php         
include('class.deliverystatus.php');         
$ds = New Deliverystatus();         
$ds->setUsername('USERNAME');
$ds->setPassword('PASSWORD');
$ds->setTimeout(20);
 
$ds->login();
$myDeliveries = $ds->getDeliveries();
$ds->logout();
 
var_dump($myDeliveries);
?>

Als Ergebnis bekommt wird ein Array aus Objekten zurückgeliefert. Jedes Array-Element spiegelt eine gespeicherte Lieferung wieder. Alle Infos, die auf der Delivery Status Website angezeigt werden können, können anschließend auch weiterverarbeitet werden.

Hier mal ein Beispiel meiner aktuellen Delivery Status Lieferungen:

array(3) {
  [0]=>
  object(stdClass)#9 (4) {
    ["title"]=>
    string(11) "iPad Air 2 "
    ["tracking_url"]=>
    string(56) "https://tracking.hermesworld.com/?TrackID=71295223100561"
    ["tracking_number"]=>
    string(14) "71295223100561"
    ["information"]=>
    string(10) "www.hlg.de"
  }
  [1]=>
  object(stdClass)#18 (4) {
    ["title"]=>
    string(17) "Bench Jacke eBay "
    ["tracking_url"]=>
    string(83) "http://nolp.dhl.de/nextt-online-public/set_identcodes.do?idc=966939672912&amp;zip=95028"
    ["tracking_number"]=>
    string(12) "966939672912"
    ["information"]=>
    string(18) "nolp.dhl.de, 95028"
  }
  [2]=>
  object(stdClass)#17 (4) {
    ["title"]=>
    string(15) "TNF Jacke eBay "
    ["tracking_url"]=>
    string(83) "http://nolp.dhl.de/nextt-online-public/set_identcodes.do?idc=966939632104&amp;zip=80469"
    ["tracking_number"]=>
    string(12) "966939632104"
    ["information"]=>
    string(18) "nolp.dhl.de, 80469"
  }
}

Wie immer gilt: Viel Spaß!

Runtastic-Aktivitäten per PHP auslesen

Wenn ich mich in meinem Leben dann doch mal sportlich betätige, dokumentiere ich sämtliche Aktivitäten mit Runtastic. Runtastic ist ein ziemlich umfangreiches Portal um seine Aktivitäten und Fitness zu erfassen und eine gute Übersicht zu bekommen.

Was mich schon länger stört: Runtastic bietet keine Entwickler-API an. Ich habe bislang vergeblich versucht, irgendwie an die Daten zu kommen. Selbst ein Gold-Mitgliedschaft bringt einem hier nichts: Was auf Runtastic steht, bleibt auf Runtastic. Um dieses Problem zu umgehen habe ich spontan eine PHP-Klasse geschrieben, die sich bei Runtastic einloggt und sämtliche Aktivitäten als Objekt zurück gibt. Diese Daten kann man dann natürlich auch in Datenbanken importieren oder sich eine schnelle Excel-Export Funktion basteln.

Die Klasse steht seit heute unter dem Namen „php-runtastic“ auf GitHub zum Download bereit und steht unter MIT-Lizenz.

Hier mal ein kleines Beispiel:

<?php
	include("class.runtastic.php");
 
	$runtastic = New Runtastic();
	$runtastic->setUsername("user@example.com");
	$runtastic->setPassword("verysecurepassword");
	$runtastic->setTimeout(20);
 
	if ($runtastic->login()) {
		echo "Username: " . $runtastic->getUsername();
		echo "<br />";
		echo "UID: " . $runtastic->getUid();
		echo "<br />";
		echo "<br />";
 
		$myRuntasticActivities = $runtastic->getActivities();
		echo "My latest <b>" . $myRuntasticActivities[0]->type . "</b> activity was <b>" . $myRuntasticActivities[0]->feeling . "</b>!";
	}
 
?>

Viel Spaß!

Notifications von Icinga oder Nagios per WhatsApp verschicken

Ich habe in einem anderen Post bereits erklärt, wie man WhatsApp-Nachrichten per Kommandozeile verschicken kann. Wenn die Voraussetzungen erfüllt sind, sind der Fantasie quasi keine Grenzen mehr gesetzt. Was ich als erstes mit den neuen Möglichkeiten gemacht habe ist, sämtliche Host- und Service-Notification meines Icinga-Servers von SMS auf WhatsApp umzustellen.

Das Ganze lässt sich ziemlich leicht umsetzen, wenn man erst einmal die Einrichtung von Yowsup, dem Python-Skript zum Verschicken von Nachrichten, abgeschlossen hat.

Ich erkläre hier kurz, wie man sich also in Zukunft seine Host- und Service-Notifications per WhatsApp zuschicken lassen kann.
In diesem Beispiel gehe ich davon aus, das sämtliche Yowsup-Komponenten Nagios/Icinga Plugin-Verzeichnis liegen. In diesem Fall ist es das Verzeichnis „/usr/lib/nagios/plugins/“ und wurde in der Nagios/Icinga-Konfiguration auf die „$USER1“ Variable gesetzt.

Contact-Definition erweitern

Bereits bestehende Contact-Definitionen, über die bisher Notifications an Nutzer des Nagios/Icinga ausgelöst werden, müssen um ein weiteres Attribut erweitert werden: Die Handynummer. Anfangs habe ich in meinem Fall das bereits vorhanden Attribut „pager“ genutzt, um die Handynummer für WhatsApp dem Kontakt/Nutzer zuzuweisen. Sauberer ist es allerdings, wenn dazu eine benutzerdefinierte Variable benutzt wird. Diese so genannten „Custom-Variables“ zeichnen sich durch den am Anfang stehenden Unterstrich (_) aus und können in beliebiger Anzahl angelegt werden.

Eine Contact-Definiton (/etc/icinga/objects/contacts.cfg) könnte wie folgt aussehen:

define contact{
        contact_name                    timo
	use				generic-contact
        alias                           Timo Schlueter
        email                           me@timo.in
	_whatsapp			4915777818466
	}

Um diese Benutzerdefinierte Variable (_whatsapp) nun in anderen Objekt-Definitionen von Nagios/Icinga zu nutzen, muss fortan die Variable $_CONTACTSWHATSAPP$ verwendet werden.

Command-Definition anlegen

Nach der Anpassung der Contact-Definition muss eine neue Command-Definition angelegt werden, über die der Versand der Nachrichten ausgelöst wird. An sich unterscheidet sich diese Definition kaum von der bereits im Umfang von Nagios/Icinga enthaltenen E-Mail-Notification.

Eine Command-Definition (/etc/icinga/objects/commands.cfg) könnte wie folgt aussehen:

define command {
        command_name    notify-host-by-whatsapp
        command_line    /usr/bin/python $USER1$/yowsup/yowsup-cli -c $USER1$/yowsup/config -s $_CONTACTWHATSAPP$ '$NOTIFICATIONTYPE$ Host : $HOSTNAME$ is $HOSTSTATE$ @ $LONGDATETIME$'
}
 
define command {
        command_name    notify-service-by-whatsapp
        command_line    /usr/bin/python $USER1$/yowsup/yowsup-cli -c $USER1$/yowsup/config -s $_CONTACTWHATSAPP$ '$NOTIFICATIONTYPE$ Host : $HOSTNAME$ - Service : $SERVICEDESC$ is $SERVICESTATE$ @ $LONGDATETIME$'
}

Host- und Service-Commands können selbstverständlich auch zusammengefasst werden. Da in diesem Beispiel allerdings unterschiedliche Ausgaben erfolgen sollen, bietet sich die Trennung zwischen „notify-host-by-whatsapp“ und „notify-service-by-whatsapp“ an.

Command-Definition zuordnen

Sind Contact-Definition erweitert und die Command-Definition angelegt, muss anschließend noch die Command-Definition einem Contact zugeordnet werden. In meinem Fall nutze ich Templates für die Zuordnung von Optionen zu Kontakten. Die Zuordnung kann allerdings auch direkt am einzelnen Kontakt erfolgen. Zur Vereinfachung hier die Contact-Definition von oben, welche um die Command-Definiton erweitert wurde:

        contact_name                    timo
	use				generic-contact
        alias                           Timo Schlueter
        email                           me@timo.in
	_whatsapp			4915777818466
	service_notification_commands   notify-service-by-email notify-service-by-whatsapp
	host_notification_commands      notify-host-by-email notify-host-by-whatsapp

Durch diese Definition wird dem Benutzer „timo“ ab sofort im Falle einer Status-Änderung eines Hosts oder Services nicht nur eine Mail, sondern auch eine Nachricht per WhatsApp zugeschickt.

Noch einige Hinweise:

  • Es sollten immer zwei Wege des Versands von Notifications geben. Gerade WhatsApp hat in letzter Zeit häufiger mit Serverproblemen zu kämpfen, weshalb es sich anbietet, neben WhatsApp auch noch eine Mail oder SMS zu versenden.
  • Aus Sicht des Datenschutzes ist der Versand von Nachrichten über WhatsApp gesondert zu betrachten. WhatsApp wird im Ausland betrieben und hält sich daher mit hoher Wahrscheinlichkeit nicht an die Datenschutzrichtlinien Deutschlands. Ich würde daher nicht empfehlen, sensible Daten (Server-IPs, Hostnamen etc.) über WhatsApp zu verschicken. Ich verwende Nagios/Icinga hauptsächlich zur Überwachung von Web-Anwendungen und Websites und einigen Web-Servern. In diesem Fall versende ich also ohnehin nur öffentlich zugängliche Informationen. Dies sollte im Vorfeld auf jeden Fall abgewogen werden.

Fragen oder Feedback zu der Anleitung bitte jederzeit gerne als Kommentar, Tweet oder Mail hinterlassen.

WhatsApp-Nachrichten über Kommandozeile verschicken

Es gibt mittlerweile eine ziemlich einfache Möglichkeit, per Kommandozeile Nachrichten per WhatsApp zu verschicken. Möglich ist das Ganze über die ein kleines Python-Tool namens „Yowsup„. Mit diesem Tool ist es möglich, die WhatsApp-Registrierung durchzuführen und anschließend Nachrichten zu versenden und zu empfangen. Da ich dem Empfang noch nicht getestet habe, beschreibe ich hier erst einmal, wie der Versand von Nachrichten funktioniert.

Voraussetzungen

  • eine gültige Handynummer
  • Python
    • python-dateutil
    • argparse for python < 2.7
    • libxml2
  • Yowsup

Yowsup downloaden und entpacken

Zuerst einfach das git Repository von Yowsup auf den lokalen Rechner/Server klonen. Das erledigt man über folgenden Befehl:

git clone https://github.com/tgalal/yowsup

Anschließend in den Unterordner „src“ von „yoswum“ wechseln, da dort die Kommandozeilen-Tools (yowsup-cli) liegen, die nun benötigt werden:

cd yowsup/src/

Registrierung

Bevor man Nachrichten versenden kann, muss die eigene Handynummer bei WhatsApp registriert werden. Als erstes muss die Handynummer, die zum versenden von Nachrichten genutzt werden soll, in der Config-Datei angegeben werde. Ich habe dazu einfach, weil Nerds wie ich ja tendenziell eher faul sind, einfach die Beispiel-Datei (config.example) geöffnet und angepasst:

vi config.example
cc=49 #if not specified it will be autodetected
phone=491751234321
id=
password=

Wichtig sind hier im ersten Schritt nur die Parameter „cc“ und „phone“. Unter „cc“ muss die Landeskennung angegeben werden. Für Deutschland wäre dies die „49“. Unter „phone“ muss die Handynummer angegeben werden, die für WhatsApp registriert werden soll. Das Format lautet in meinem Fall: 491751234321.

Anschließend die Datei speichern und folgenden Befehl ausführen (wir befinden uns immer noch im Ordner yowsup/src/):

python yowsup-cli -c config.example -r sms

Die Antwort könnte wie folgt aussehen:

status: sent
retry_after: 25205
length: 6
method: sms

Auf dem angegeben Handy sollte nun eine SMS mit einem Freischaltcode für WhatsApp angekommen sein. Als Beispiel dient hier mal der Code „123-987“.
Abgeschlossen wird die Registrierung mit dem Befehl:

python yowsup-cli -c config.example -R 123-987

Das Ergebnis sollte wie folgt aussehen:

Detected cc: 49
status: ok
kind: free
pw: 74beaae5d1af0c04c57c37ff0045573603ff34dd=
price: 0,89 €
price_expiration: 1389688778
currency: EUR
cost: 0.89
expiration: 1418553624
login: 491751234321
type: existing

Wichtig ist nun das Attribut „pw“. Dort steht das Passwort, mit dem der Login auf den WhatsApp-Servern durchgeführt wird. Dieses Passwort muss nun in der Config-Datei angegeben werden. Also wieder die Datei öffnen und anpassen:

vi config.example
cc=49 #if not specified it will be autodetected
phone=491751234321
id=
password=74beaae5d1af0c04c57c37ff0045573603ff34dd=

Die Registrierung und Konfiguration ist abgeschlossen. Jetzt kommt der lustige Teil.

Nachricht versenden

Nachdem die Konfiguration abgeschlossen ist, kann nun eine Nachricht versendet werden. Hierzu gibt es zwei Möglichkeiten: Zum einen kann man eine Nachricht stumpf versenden, ohne auf Antworten zu warten („Fire-and-forget“) und zum anderen kann man den interaktiven Modus starten, über welchen man im klassischen Chat-Stil Nachrichten empfangen und versenden kann.

Der Befehl für ein einfaches verschicken lautet:

python yowsup-cli -c config.example -s 4915777818466 'Hallo Welt'

Das Ergebnis dürfte in etwa so aussehen:

Detected cc: 49
Authed 491751234321
Sent message

Es wurde somit eine Nachricht vom Absender 491751234321 (der registrierten Nummer) mit dem Inhalt „Hallo Welt“ an die Nummer 4915777818466 geschickt.

Wenn der interaktive „Chat-Modus“ gestartet werden soll, genügt der Befehl:

python yowsup-cli -c config.example -i 4915777818466

Jetzt wird ein Chat-ähnlicher Dialog gestartet, über den nicht nur Nachrichten abgeschickt, sondern auch empfangen werden können.

Diese Spielerei kann nun für alle möglichen Schweinereien genutzt werden. Der Fantasie sind keine Grenzen gesetzt. Ich selbst spiele gerade mit dem Gedanken, Nagios/Icinga Notifications aus meinem Monitoring-System über WhatsApp zu verschicken.

Fragen oder Feedback zu der Anleitung bitte jederzeit gerne als Kommentar, Tweet oder Mail hinterlassen.

Unblock-Us Auto-Update Skript

Ich bin seit ca. einem Jahr absoluter Fan von Netflix. Leider gibt es in Deutschland noch immer keine vernünftige Möglichkeit, ein Abo abzuschließen und Netflix ohne Umwege zu empfangen. Man benötigt nach wie vor einen VPN- oder DNS-Anbieter der Netflix vorgaukelt, man befinde sich in den USA oder Kanada.
Es gibt sicherlich viele dieser Dienste, allerdings habe ich mich ziemlich schnell für Unblock-Us entschieden. Der Dienst stellt alternative DNS-Server ein, die man in seinem Gerät (in meinem Fall Apple TV) einstellt und somit sofort die entsprechenden Angebote von Netflix nutzen kann. Das Ganze hat gegenüber den meisten VPN-Providern den Vorteil, dass man mit nahezu voller Geschwindigkeit auf die Inhalte zugreifen kann.

Einen Nachteil hat das Ganze jedoch: Unblock-Us nutzt zur Authentifizierung die eigene IP-Adresse. Sprich: Ändert sich die IP-Adresse des eigenen Internetanschluss, so muss man diese im Unblock-Us Webinterface erst seinem Account zuteilen. Da mir das allerdings zu stressig war, habe ich mich hin gesetzt und ein kurze Shell-Skript geschrieben. Das Skript läuft nun per Cronjob auf meiner Synology DS212j NAS um 03:00 Uhr morgens und schickt meine aktuelle IP-Adresse an Unblock-Us. Somit brauche ich mich um die Aktualisierungen der Adresse nicht mehr zu kümmern.

Das hat doch was, oder? Wer Interesse an dem Skript hat, kann sich gerne bedienen. Zu ändern sind lediglich die Variablen „userlogin“ und „userpassword“. Dort gehören eure Unblock-Us Zugangsdaten rein.

Viel Spaß.

Unblock-Us Auto-Update Skript auf GitHub

#!/bin/sh                                                                                                                                                                                                   
#                                                                                                                                                                                                           
# Unblock-Us Update-Script  
#                                                                                                                                                                                
# This script automatically sends your current IP address to the Unblock-Us api.                                                                                                                             
# It can be used to update your IP adress via cron.                                                                                                                                                         
#                                                                                                                                                                                                           
# Author:       Timo Schlueter                                                                                                                                                                              
# Mail:         me@timo.in   
# Web:          www.timo.in                                                                                                                                                                                 
# Twitter:      twitter.com/tmuuh                                                                                                                                                                           
#                                                                                                                                                                                                           
# Version:      0.2                                                                                                                                                                                         
# Date:         20-12-2013                                                                                                                                                                                  
#                                                                                                                                                                                                           
# Notes:        I am not affiliated directly or indirectly with Unblock-Us                                                                                                                                  
#                                                                                                                                                                                                           
 
# Variables (user specific)                                                                                                                                                                                 
userlogin="email@example.com"                                                                                                                                                                                   
userpassword="password"                                                                                                                                                                                    
 
# Environment                                                                                                                                                                                              
apiurl="https://api.unblock-us.com/login?$userlogin:$userpassword"                                                                                                                                          
wgetcmd=$(which wget)                                                                                                                                                                                       
 
# Check if username and password are set.                                                                                                                                                                   
if [ -z $userlogin ]                                                                                                                                                                                        
        then                                                                                                                                                                                                
                echo "No password set."                                                                                                                                                                     
                exit 1                                                                                                                                                                                      
elif [ -z $userpassword ]                                                                                                                                                                                   
        then                                                                                                                                                                                                
                echo "No password set."                                                                                                                                                                     
                exit 1                                                                                                                                                                                      
else                                                                                                                                                                                                        
        # Call the api                                                                                                                                                                                      
        response=$($wgetcmd --no-check-certificate -qO- $apiurl)                                                                                                                                            
 
        # Check response from api                                                                                                                                                                           
        if [ $response == "active" ]                                                                                                                                                                        
                then                                                                                                                                                                                        
                        echo "IP address is active. You are good to go!"                                                                                                                                     
                        exit 0                                                                                                                                                                              
        elif [ $response == "bad_password" ]                                                                                                                                                                
                then                                                                                                                                                                                        
                        echo "Wrong username or password."                                                                                                                                                  
                        exit 1                                                                                                                                                                              
        elif [ $response == "not_found" ]                                                                                                                                                                   
                then                                                                                                                                                                                        
                        echo "Username not found."                                                                                                                                                          
                        exit 1                                                                                                                                                                              
        else                                                                                                                                                                                                
                echo "Unknown error. Check api url or documentantion."                                                                                                                                      
                exit 1                                                                                                                                                                                      
        fi                                                                                                                                                                                                  
fi

GeoJSON Feature Collection per JavaScript-Object erzeugen

Im Rahmen von Michaels Pacific Crest Wanderung habe ich basierend auf OpenLayers, jQuery und den Daten seines SPOT Satellite Messenger eine Webanwendung gebaut, auf der man rund um die Uhr verfolgen kann, wo er sich gerade befindet. Bei der Nutzung von OpenLayers stellt man sich zwangsläufig irgendwann die Frage, in welchem Format man die Location-Daten denn überhaupt abrufen möchte.

In einem Projekt von 2010 für walking-away.de (Beispiel), habe ich beispielsweise GPX-Dateien verwendet. Die Methode bietet sich vor allem immer dann an, wenn man die Daten beispielsweise aus einem Navigationsgerät von Garmin und co. auslesen möchte um sie im Web darzustellen. Diese Geräte bieten meistens die aufgezeichneten Tracks im GPX-Format an.

Im Fall von live.everyday-madness.de habe ich mich dazu entschlossen, auf das GeoJSON-Format zu setzen. JSON als solches ist momentan ohnehin der absolute Shooting-Star was den Datenaustausch im Web angeht. GeoJSON setzt, wie der Name schon sagt, auf die JSON „Sprache“ auf und definiert, wie Daten übermittelt werden müssen. Über GeoJSON lassen sich Punkte (Points), Tracks (LineStrings) oder auch Formen (Polygon) definieren. OpenLayers zum Beispiel unterstützt in der aktuellen Version alle im GeoJSON Standard definierten Typen. Zudem lassen sich mehrer Typen zu so genannten „Feature Collections“ zusammen fassen. Eine Feature Collection besteht beispielsweise aus einem Track (als LineString) und einem Start- bzw. Endpunkt-Marker (als Point). Eine solche Feature Collection kann anschließend als JSON-String von beispielsweise OpenLayers eingelesen werden und stellt sämtliche in ihr enthaltenen Daten dar.

In meinem Fall habe ich die Roh-Daten (Koordinaten, Timestamp, Nachrichten-Inhalt, Typ) per AJAX und JSON (nicht GeoJSON) über den Umweg über PHP aus einer MySQL Datenbank geholt. Natürlich hätte ich die den GeoJSON-String bereits per PHP zusammen setzen können, allerdings benötigte ich ohnehin noch weitere Daten aus der Datenbank, weshalb ich mich dazu entschlossen habe, den GeoJSON-String erst in JavaScript zusammen zu setzen um ihn dann an OpenLayers zu übergeben.

Doch wie erstellt man nun möglichst einfach eine Feature Collection in JavaScript? Der einfachste Weg ist, eine Kombination aus Objekten und Arrays zu verwenden.

weiterlesen

MEGA und die 3rd-Party-Apps

Kim Dotcoms MEGA ist jetzt bereits einige Wochen verfügbar. Manch einer mag sich gewundert haben, wo denn die ganzen 3rd-Party-Apps (Sync-Clients etc.) bleiben, wie man sie von Dropbox, Box und Google Drive kennt. Ich bin selbst vor ein paar Tagen auf eine coole Idee gekommen, die ich mit MEGA als Storage-Provider umsetzen könnte. Die Entwickler-API von MEGA habe ich mir bereits am Start-Tag angesehen um mal zu schauen, was sich so machen lässt. Damals habe ich das Ganze aber nur überflogen. Heute wollte ich dann mal genauer in die Details schauen und zumindest mal ein paar Befehle in Richtung MEGA schicken.

Was mich jedoch irgendwie ziemlich stark abgeschreckt hat war folgender Abschnitt auf der Website der API:

1.1 Security considerations

The cryptographic integrity of MEGA’s user data is important to us. We can therefore not allow you to distribute or make available your client application without going through us. We will perform a code audit of your product and promote/distribute it on our site. You will also receive a share of the revenue that your application generates.

Vielleicht ist mein Englisch einfach zu schlecht, aber ich lese daraus: Bevor ich Software veröffentlichen kann, die die MEGA-API nutzt, muss ich sie an MEGA schicken, damit die die Software auf ihre Sicherheit überprüfen?

Wer wundert sich da noch, dass es bislang noch nicht allzu viele Apps gibt, die eine Anbindung an MEGA bieten?