Einsteig in das Microsoft Bot Framework – die Enkel der IRC-Bots erwachen

Microsofts Entwickler Konfernz BUILD war dieses Jahr viel von Visionen angetrieben wie Sache X in Y Jahren vielleicht funktionieren könnte. Wenn man den schnelllebigen Technikmarkt ansieht sind (für meiner einer) solche Aussagen oft schneller vergessen als ein Chamäleon die Farbe wechselt. Ich mag eher Dinge die sofort greifbar sind und zum ausprobieren einladen. So war es mit dem Bot Framework von Microsoft (Github Repository). Vorgestellt und am gleichen Tag konnten es sich interessierte Entwickler schon genauer ansehen und damit experimentieren.

Bots sind nicht neues. Mindestens wer zu IRC-Zeiten aufgewachsen ist kann sich an Channel-Bots wie Eggdrop und Co. noch sehr gut erinnern. Die Entwicklung hierfür war allzu nicht so schwer wenn man einmal den Dreh raus hatte. Später folgten Bots für andere Chatplattformen wie Hipchat oder Slack. Nun kommt also auch Microsoft hinzu. Der Ansatz aus Redmond wirkt (natürlich) mehr in Richtung Kommerz und unternehmensgetrieben Kommunikation ausgelegt als auch das der Dienst für eine breitere Masse interessant sein soll. Im Abstrakten gesehen sollen diese Bots in etwa gleich reagieren wie die altbekannten Kompanen aus IRC-Zeiten. Man sagt etwas und der Bot reagiert darauf. Selbstverständlich heute etwas schlauer und kontextsensitiver als die !say Kommandos der damaligen Zeit – im Grunde jedoch das gleiche, irgendwie.

Da ich die offizielle Dokumentation noch etwas verwirrend finde habe ich mir eine einfache Checkliste zusammen gestellt um mit der Entwicklung von Bots beginnen zu können.

  1. Auf docs.botframework.com verstehen was überhaupt Sache ist
  2. Entscheiden ob man in C# oder Node.js entwickeln möchte
  3. Aktuelles Visual Studio (Version egal) installieren
  4. Trotzdem noch mal Visual Studio aktualisieren (Tools – >Extensions and Updates -> Updates)
  5. Manuell (!) dieses Bot Project Template herunterladen
    Template nach „%USERPROFILE%\Documents\Visual Studio 2015\Templates\ProjectTemplates\Visual C#“ (für C#) verschieben
  6. Visual Studio neustarten
  7. Neues Projekt / Solution aus dem Template erstellen, bauen und ausführen lassen
  8. Falls alles korrekt ist, startet Edge mit einer Dummy Seite, falls nicht hat etwas nicht geklappt

Bot VS TemplateIn der offiziellen Dokumentation wird oft vom „Bot Framework Emulator“ gesprochen jedoch nur einmal darauf verlinkt. Ihr könnt den Emulator HIER herunterladen.

Startet ihr nun den Emulator könnt ihr eigentlich sofort mit dem „interaktiven Chatten“ loslegen. Der im Template integrierte Demo-Bot gibt euch die Anzahl der Characters eurer Nachricht zurück.

Bot Emulator

Weiter kann man im Emulator auch gleich die JSON Kommunikation zwischen euch und dem Webservice des Bots sehen.

Alles im allen viel der erste Blick auf das Framework richtig gut aus. Natürlich war es nur ein anschnuppern – welches zu mindestens für mich Lust auf mehr gemacht hat.

Leider gibt es noch keine themenspezfischen Bot Foren im MSDN. Dieser Umstand ist jedoch den verantwortlichen klar und haben zu mindestens versprochen dieses Anliegen weiter zu geben – hoffen wir einfach einmal das Beste.

Auf eine Frage bezüglich der „Selbsthostbarkeit“ von Bots warte ich noch in diesem Thread auf eine Antwort.

Falls es meine Zeit zulässt würde ich mich gern weiter mit dem Framework beschäftigen. Mal sehen was man daraus in der Freizeit so knuffiges Bauen kann.

Backup, HiDrive und rsync

Ich habe vor einiger Zeit in diesem Artikel einmal erklärt, wie man Daten mit encfs verschlüsseln kann. Leider ist der darin verlinkte Artikel nicht mehr verfügbar.

Ich selbst betreibe ein ausgelagertes Backup, in das ich Daten lade, die für mich wichtig sind – also eine gewissen Wert haben und nicht einfach wieder beschafft werden können. Ich verwende dafür „HiDrive“ von Strato. Aber Achtung, ich habe noch einen der alten Tarife. Zwar habe ich bisher noch keine Nennenswerten Ausfälle mit Strato gehabt, aber inzwischen muss man sich die hier verwendeten Übertragungsprotokolle extra zu dem Paket dazubuchen. Dafür finde ich die aktuellen Angebote schlicht zu teuer.
Ich habe kein Problem damit für meinen Onlinespeicher zu bezahlen, immerhin zahle ich dann im Gegensatz zu den vielen „kostenlosen“ Anbietern und kann daher auf Leistung bestehen – trotzdem halte ich von dem „jede dazugebuchte Option kostet Geld“ mal gar nichts. Das war am Anfang, als ich HiDrive gebucht habe, nicht so. Da gab es das Rundumpaket. Und ich sehe keinen Sinn darin Übertragungsprotokolle teuer zu verkaufen. Ehrlich, ist ja nicht so, dass die Brot fressen.
Das ist also eine deutliche Negativentwicklung bei Strato.
Dazu kommt aber vielleicht noch einmal ein extra Artikel, weil mir da so einige Sachen eher seltsam vorkommen.
Wenn ihr also nicht schon Strato-Kunde seid und rsync im Paket habt: Ich rate von einem aktuellen Wechsel zu Strato ab.

So, warum aber dieser Artikel?
Ich habe es durch einen dummen Fehler geschafft mir die Config vom HiDrive und die Daten darauf zu zerlegen. Und meine lokale Einrichtung des Backup-Scripts. Das war doof, und ich musste mir die ganzen Infos wieder suchen. Daher das ganze hier für mich auch als Notiz.

Natürlich kann man das mit etwas umfrickeln für jeden Anbieter verwenden, der die Kommunikation via rsync und webDav (Wiederherstellung) erlaubt.

Das Problem

Ich möchte Daten, die für mich einen gewissen Wert haben, gerne extern verschlüsselt sichern. „Wert“ heißt in diesem Fall, dass diese Daten für mich nicht einfach neu beschafft werden können. Also zum Beispiel Fotos.

Das ganze möchte ich möglichst „automatisch“ erledigt wissen, denn ich kenne mich – passiert es nicht automatisch, dann passiert es gar nicht.

Die Lösung

Ich verwende einen Raspberry Pi, der die Daten von meinem lokalen NAS verschlüsselt und anschließend via rsync zu einem Speicherort im Internet synchronisiert.
Der Vorgang soll automatisch gestartet werden, damit ich mich damit garn nicht beschäftigten muss.

Einrichtung

Das Ziel

Ich möchte die zu sichernden Daten in ein zentrales Verzeichnis einhängen und dieses dann mit rsync auf das HiDrive speichern.

Installation der Software

Ich installiere die Software auf einem Raspberry Pi mit „raspbian“. Die Installation und das Vorgehen funktioniert aber auch mit jedem anderen Betriebssystem, das von Debian abstammt (zum Beispiel Ubuntu und seine Derivate).

Für das von mir verwendete Vorgehen benötigen wir encfs und rsync. Falls noch nicht installiert, dann müssen wir das jetzt tun.
Außerdem legen wir das Verzeichnis /tmp/crypted an. Das wird unser Verzeichnis, das wir auf das HiDrive synchronisieren. Darin werden wir Unterverzeichnisse anlegen, die entsprechend die eigentlichen, verschlüsselten Daten enthalten werden.

sudo apt-get install rsync encfs

So, und für den ganzen Rest habe ich ein Python-Script geschrieben. Klar, das geht bestimmt auch direkt auf der Bash. Das kann ich aber nicht. Nein, das ist nicht ganz richtig. Ich könnte das schon, aber das Terrain auf dem ich mich sicher bewege, das ist Python.

Anlegen der verschlüsselten Verzeichnisse

Bevor wir die Verzeichnisse per Script automatisch sichern können, müssen wir die Grundlagen dafür sorgen, dass die entsprechenden Metadaten angelegt werde.

Ich habe das grundlegende Vorgehen bereits einmal beschrieben.

Wir müssen die entsprechende Verschlüsselungen mit encfs –reverse anlegen. Denn wir wollen ja unsere Klardaten behalten und nur eine verschlüsselte Sicht darauf haben, die wir dann sichern.

Und wir dürfen auf keinen Fall vergessen die .encfs6.xml aus dem Stammverzeichnis zu sichern. Ohne diese Datei ist ein entschlüsseln der Daten nicht mehr möglich!
Also entweder rauskopieren und ab damit auf das HiDrive (muss man dann aber manuell machen) oder an sicherer Stelle verwahren.

Das Backup-Script

Das folgende Python-Script als backup.py (oder ähnlich) speichern. Der Aufruf muss mit sudo erfolgen, da encfs root-Rechte benötigt.

sudo python backup.py

Das Script macht folgendes: Die in SOURCES aufgeführten Verzeichnisse (from) mittels encfs –reverse zu verschlüsseln und unter /tmp/crypted als Unterverzeichnisse (to) zur Verfügung zu stellen. Dass Passwort für die Verschlüsselung wird hier mit angegeben. Das ist natürlich ein Sicherheitsrisiko, wenn die Datei in falsche Hände gerät, dafür ist so aber keine manuelle Eingabe nötig. Und das stände einer automatischen Ausführung im Wege.
Nach dem Mounten der verschlüsselten Verzeichnisse wird das Backup mit rsync durchgeführt. Kommt es zu einem Fehler, zum Beispiel durch einen Disconnect, wird nach SLEEP_AFTER_FAIL Sekunden der nächste Vorgang gestartet.
(Ich nehme übrigens Verbesserungen am Script gerne entgegen, falls jemanden etwas auffällt. Es ist schlicht die Version, die ich verwende. Daher keine großartige Fehlerbehandlung oder Benachrichtigung.)

from __future__ import unicode_literals

# =================================================
# Festlegen einiger Variablen
# Muessen angepasst werden

# Verzeichnisse, die verschluesselt eingehaengt werden sollen
# Je Eintrag: "from"     ist das Quellverzeichnis
#             "to"       ist der Name des Zielverzeichnisses unter "CRYPTED_DIR"
#             "password" ist das Passwort fuer die Verschluesselung
SOURCES = (
{"from": "/home/name/fotos/", "to": "f", "password": 'deinPasswort'},
)

# Rsync-Ziel bei einem Hidrive. "benutzername" muss hier ausgetauscht werden
RSYNC_TARGET = "benutzername@rsync.hidrive.strato.com:/users/benutzername/backup"

# Wenn bei dem rsync etwas schief geht (24 Stunden Disconnect, etc.)
# wird so viele Sekunden vor einem erneuten Versuch gewartet:
SLEEP_AFTER_FAIL = 60

# =================================================


# Variablen die wir nicht anpassen muessen:

CRYPTED_DIR = "/tmp/crypted"
LOCKFILE = "/tmp/backup.lock"

# Ende der Variablen

from subprocess import call, PIPE, Popen
import fcntl
import os, sys, time

def check_lock(lockfile):
    if os.access(lockfile, os.F_OK):
        with open(lockfile, "r") as pidfile:
            old_pid = pidfile.readline()
            if old_pid != "" and os.path.exists("/proc/{0}".format(old_pid)):
                log("Programm is allready running with PID {0}".format(old_pid))
                return False
            else:
                os.remove(lockfile)
    with open(lockfile, "w") as pidfile:
        pidfile.write(str(os.getpid()))
    return True

def create_crypted_dir(d):
    log("Create {0} (if not exists)...".format(d))
    if not os.path.exists(d):
        os.makedirs(d)

def do_rsync(source_dir, target):
    while True:
        log("Starting sync...")
        command = ["rsync", "-rltDvzre", '"ssh"', source_dir, target]
        log("  '{0}'".format(" ".join(command)))
        ret = call(command)
        if ret == 0:
            break
        else:
            time.sleep(SLEEP_AFTER_FAIL)

def log(text, newline=True):
    sys.stdout.write(text)
    if newline:
        sys.stdout.write("\n")

def mount_encfs_dirs(sources, crypted_dir):
    for source in sources:
        source_dir = source["from"]
        target_dir = os.path.join(crypted_dir, source["to"])
        if not os.path.exists(target_dir):
            log("Creating '{0}'...".format(target_dir))
            os.makedirs(target_dir)
        log("Mount '{0}' to '{1}'...".format(source_dir, target_dir))
        if not test_allready_mounted(target_dir):
            command = ("encfs", "--reverse", "--stdinpass", source_dir, target_dir)
            log("  command: {0}".format(" ".join(command)))
            p = Popen(command, stdin=PIPE, stdout=PIPE)
            c = p.communicate(source["password"])
            r = p.wait()
            if r != 0:
                raise Exception("Failed to mount {0} to {1}".format(source_dir, target_dir))
        else:
            log("  seems allready mounted...")

def test_allready_mounted(target_dir):
    p = Popen(("mount"), stdin=PIPE, stdout=PIPE)
    for line in p.stdout.readlines():
        parts = line.split()
        if parts[2] == target_dir:
            return True
    return False


def main():
    if not check_lock(LOCKFILE):
        sys.exit(1)
    create_crypted_dir(CRYPTED_DIR)
    mount_encfs_dirs(SOURCES, CRYPTED_DIR)
    do_rsync(CRYPTED_DIR, RSYNC_TARGET)


if __name__ == "__main__":
    main()

Wiederherstellung

Das beste Backup nützt nichts, wenn man die Daten nicht wiederherstellen kann.

Es ist wichtig, dass man die Möglichkeit der Wiederherstellung in regelmäßigen Abständen prüft. Es ist einfach die Daten in die Wolke zu schieben. Der Schrecken ist groß, wenn man bemerkt, dass die Verschlüsselung nicht wiederherstellbar ist.
Gut, wenn das passiert, wenn man es ausprobiert. Schlecht, wenn man das Backup tatsächlich braucht.

Wir hängen das HiDrive per WebDav ein und sorgen für eine entsprechende Entschlüsselung.

Laut ubuntuuser-Wiki braucht man folgende Pakete um WebDav als Dateisystem mounten zu können:

sudo apt-get install ca-certificates davfs2

Wir legen ein Verzeichnis unter /tmp/ an und mounten dann mittels davfs2 dort das HiDrive:

sudo mkdir /tmp/hidrive_crypted/
sudo mount -t davfs https://webdav.hidrive.strato.com /tmp/hidrive_crypted

Benutzername und Passwort werden anschließend abgefragt. Wenn alles klappt steht der Inhalt vom HiDrive anschließend unter /tmp/hidrive_crypted zur Verfügung.

Jetzt müssen wir den Inhalt natürlich noch entschlüsseln. Wenn wir bei unserem Beispiel aus dem Script bleiben, müssen wir wie folgt vorgehen, wobei der Ort für die encfs6.xml angegeben werden muss – je nachdem wo sie liegt. Ich habe ja bereits weiter oben geschrieben, dass ohne Datei keine Entschlüsselung möglich ist.
‚benutzername‘ müssen wir wieder durch unseren Benutzernamen auf dem HiDrive ersetzen.

ENCFS6_CONFIG=/tmp/encfs6.xml encfs /tmp/hidrive_crypted/users/benutzername/backup/crypted/f/ /tmp/fotos

Wenn alles geklappt hat, können wir uns nun unter /tmp/fotos unser Fotos anschauen. Entschlüsselt auf unserem Rechner mit Livedaten aus dem HiDrive.

— der Würschlmann

Neue App: Apfeltalk Live für das tvOS (Top 10 Platzierung!)

Man freut sich ja immer wie Hulle wenn das kleine grüne Lichtlein im Apple App Store anspringt. Dieses signalisiert, dass die eingerichtet App den Review-Prozess bestanden hat und nun für den Apple Store freigeschaltet werden kann.

Dieses Gefühl hatte ich einmal wieder vor wenigen Wochen also ich die Apfeltalk Live Apple TV App in die große weite Welt entlassen habe. Binnen weniger Stunden was Programm in den Top 10 der App Store Charts gelistet – ein episches Gefühl.

Apfeltalk Live für tvOS ist eine Video-on-Demand App für den Apple TV der vierten Generation um die Aufzeichnungen der Live Sendung von apfeltalk.de im Nachhinein anzusehen.

Aus Entwickler wäre soviel mehr möglich gewesen. Die Einbindung des Live Streams, Benutzerinteraktion via Twitter oder Chat, etc. pp. Jedoch fehlen (noch) hierfür benötigte Komponenten wie eine WebView im tvOS. Ein Blick geht deswegen sehnsüchtig zur WWDC im Herbst. Hoffen darf man ja.

Da der Store noch keine Links unterstützt würde ich jeden bitten der diese App ausprobieren möchte im App Store einfach nach „Apfeltalk“ zu suchen.

Apfeltalk Apple TV Listenansicht

Apfeltalk Apple TV Apple TV Homescreen

Technisch gesehen handelt es sich um ein Swift 2.x Programm für das tvOS 9.2 welches ohne CocoaPods auskommt. Es parsed den Video-Podcast Feed und interpoliert hieraus weitere Annahmen wie zum Beispiel den Link zum Artikelbild, etc.

Den offiziellen Post von Apfeltalk zu der App findet ihr hier.

[Linktipp] Swift Applikationen auf dem Raspberry Pi ausführen

TL;DR: Link

Apple verfolgt im Moment einen ähnlichen Weg wie Microsoft, Amazon und Co.: „Open Source it!“.

Natürlich steht Apple als BDF-basiertes Unternehmen schon immer mit einem Fuß in der FOSS-Schiene, jedoch waren sie nie öffentlich dafür bekannt zum Beispiel den allseits geliebten Drucker-Damon CUPS entwickelt zu haben oder maßgeblich WebKit weiterentwickelt zu haben.

In den Mainstream gelang dieser neue Weg von Apple wohl durch die Offenlegung von Swift – welche im Moment meine Lieblingssprache ist. Nicht nur dass interessierte und fähige Menschen ausserhalb von Apple sich an der Entwicklung beteiligen können, nein, auch ganz andere Betriebssysteme werden nun offiziell unterstützt – so lang diese eine Linuxbasis haben.

Swift auf RP

Wer sich bisschen tiefer mit Swift auf Linuxmaschinen auseinander gesetzt hat der wird sicherlich wissen, dass der Funktionsumfang noch sehr begrenzt ist und noch viel Wasser die Isar herunterfließen muss bevor es wirklich 1:1 API konform mit der OS X beziehungsweise iOS Variante ist. Dennoch, der Anfang ist gemacht.

Damit man auch einmal fernab des Apple Ökosystems mit Swift auf richtiger Hardware experimentieren kann eigenen sich natürlich kleine Bastelrechner wie der Raspberry Pi super.

Hierzu hat Andrew Madsen einen recht einfachen Leitfaden geschrieben. Dieser funktioniert auch noch mit späteren Versionen von Swift als im Text beschrieben.

Wer einen kleinen Editor für Linux mit Swift-Highlighting sucht, der kann sich ja einmal mein Post zu „Visual Studio Code auf Linux in 5 Minuten installieren“ ansehen. Netter, feiner Editor.

Artikelbild: Quelle

[Videotipp] Ein Blick hinter die Kulissen von Visual Studio Code

Es war nun länger etwas ruhig auf diesem Blog. Dies soll sich in gewohnter Weiße wieder einmal ändern bis es eben wieder etwas ruhiger wird.

Dennoch habe ich bis dahin etliches an Material in den letzten Monaten zusammengetragen welches ich gerne hier mit euch teilen will – auch um es selber nicht zu vergessen.

VSC Folie Tech Days 2016
Klicken um Video abzuspielen

Anfangen möchte ich mit einem wirklich guten und spannenden Video von den diesjährigen Tech Days aus Baden, dass einen Blick in die Entstehung meines derzeitigen Lieblings-Editors (Visual Studio Code) bringt.

Drei Dinge aus dem Video haben mich wirklich überrascht:

  1. Visual Studio Code stammt Großteil aus der Schweiz. Salü!
  2. Der Man-Ihn-Chief ist Erich Gamma – der Mann hinter Eclipse und dem J-Unit Framework.
  3. Wie ein Großunternehmen Open-Source angeht / angehen muss (Legal Department, etc)

Auch für potentielle Product-Owner hat Erich Gamma gewisse Tipps unterschwellig mit in seinen Talk integriert. Lohnt also wirklich!

Quelle: giza-blog.de

Nerd of the Day: Robert Jackson – 913 Tage durchgehend auf Github aktiv

Stellt euch vor, 913 Tage am Stück, ohne eine einzelne Pause mindestens eine Aktion auf Github auszuführen. Egal ob Weihnachten, Silvester oder Valentinstag.

Github Activity

Dies können Commits, Merge Requests oder auch die anfallenden Verwaltungsaufgaben im Hinblick auf das Ticket-Management sein.

Dies hat Robert Jackson aus dem Ember.js Core Team geschafft. Für diese absolut reife Leistung ist er mein „Nerd of the Day“.

NSTableView mittels NSSortDescriptor aber ohne NSMutableArray sortieren

Wie schon im letzen Post gesagt versuche ich mich gerade in einer kleinen Mac App. Ein Desktop-Programm bringt ganz andere UI-Konzepte mit als eine mobile App auf dem Smartphone. Beispielsweise können natürlich auf dem Desktop Tabellen mehrere Spalten haben – dies ist auf iOS und co. ja eher unüblich.

Irgendwie war es für mich ein bisschen irritierend eine Lösung zu finden wie ich den Inhalt einer NSTableView ohne die Benutzung eines NSMutableArrays aber mit NSSortDescriptor sortieren kann. Ich will so gut wie möglich den NS-Namespace in modernen Swift Anwendungen vermeiden. Sprich: Array statt NSArray, String statt NSString, etc.

Dank dem NSHipster habe ich eine fast schöne Lösung gefunden. Fast schön da diese etliche Forced-Casts enthält die man ja eigentlich immer versucht zu vermeiden. Falls also jemand eine bessere Idee hat, nur her damit!

Schlussendlich musste ich nur folgende paar dinge tun und schon sortiert sich mache NSTableView als hätte sie noch nie was anderes getan.

  1. Jeder sortierbaren Spalte einen SortDescriptor Key geben. Entweder über Quelltext oder (wie ich) über den Attributes Inspector in Xcode
    Attribute Inspector
  2. Im NSTableViewController die folgende NSTableViewDelegate-Methode hinzufügen:
    // MARK: - NSTableViewDelegate -
    extension ParserViewController: NSTableViewDelegate
    {
        func tableView(tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor])
        {
            myList = (myList as NSArray).sortedArrayUsingDescriptors(tableView.sortDescriptors) as! Array
            tableView.reloadData()
        }
    }

Et viola, schon klappt es auch mit der Sortierung.