Schlagwort-Archive: Code

Editor und Shell mit Powerline

Vor ein paar Wochen bin ich über die von MadMalik erstellte Font “monoOne” gestolpert. Für den täglichen Einsatz in der Shell und im Editor habe ich recht hohe Ansprüche an eine Font. Dazu zählen:

  • Fixed Width, also dass alle Zeichen immer dieselbe Breite haben; hierdurch fallen alle Fonts mit variabler Zeichenbreite und Kerning raus
  • Klare Unterscheidbarkeit der einzelnen Zeichen, was insbesondere für “l”, “I”, “1”, “!” und “|” gilt, aber auch für “O” und “0”
  • Die Font soll nicht all zu groß eingestellt werden müssen, um die Unterscheidbarkeit zu gewährleisten
  • In der kleinen Größe sollen auch fontspezifische Eigenschaften, wie Serifen, erkennbar sein oder allein die Richtung von “ordentlichen” Anführungszeichen
  • Die gängigen Umlaute und diverse Symbole insbesondere für einfache mathematische Darstellungen sollen vorhanden sein

Mit der monoOne hat MadMalik eine Font geschaffen, die mir sehr gut gefällt. Im Büro sprachen mich Kollegen an, welche Font ich nutzen würde, da sie neben der fast schon einheitlichen Menlo positiv heraussticht.

In der Beschreibung zur monoOne las ich, dass diese die powerline Symbole beinhaltet, von denen ich bisher noch nie hörte.

powerline

Die powerline Symbole sind eigentlich nur sieben Zeichen, die im erweiterten Unicode-Bereich abgelegt werden. Zusammen mit anderen bereits vorhandenen Unicode-Zeichen können sie über Plugins in Editors und Shells geladen werden, um visuell erweiterte Informationen zum Zustand der editierten Datei oder dem System zu geben.

Konkret sieht es dann beispielsweise beim Editor vim so aus, dass die Statuszeile um einige Informationen aufgewertet wird (das Bild ist vom powerline Autor übernommen und zeigt die Font Pragmata Pro):

Es zeigt, dass ich mich im Normal-Modus vom vim befinde. Rechts daneben wird mit einem Symbol der aktuell verwendete Branch im git Repository angezeigt, erst dann der Dateiname. Auf der rechten Seite sind Informationen über das Dateiformat (unix), das Encoding (utf-8), das eingestellte Syntax-Highlightning (python), der aktuellen Cursorposition relativ zum Gesamtdokument (2%), Linebreak, Zeilen- und Spaltenposition.

Beim Wechsel des Editor-Modus schaltet sich die Zeile um:



Allein diese massive visuelle Änderung hilft ungemein, schnell Informationen über den Zustand des Editors und der Datei zu erfahren.

Shell

In der Dokumentation zur powerline steht, dass diese auch für andere Applikationen verfügbar ist. Das geht zwar eigentlich auch mit den für vim installierten powerline-Tools, aber irgendwie wollte das auf meinem Mac nicht wirklich zusammenarbeiten. Stattdessen gab es irgendwo den Hinweis, es gebe ein zshTheme für powerline.

Der Entwickler nutzt dazu das Framework oh-my-zsh. Seit Jahren nutze ich die hoch konfigurierbare zsh. Anfangs mit vielen eigenen Snippets, die ich mir im Web zusammensuchte oder mit anderen Bekannten im Hackspace austauschte. Später dann baute ich meine zsh-Config auf die vom Projekt grml auf.

Das Framework oh-my-zsh setzt auf eine schnelle Konfiguration der zsh, was vermutlich viele bisher abschreckte, auf diese Shell zu wechseln. Ohne Konfigurationsdatei ~/.zshrc zeigt die zsh keinen Prompt sondern einen minimalen Konfigurationsdialog. Die danach angezeigte Shell ist extrem Minimal. Die großen Stärken von zsh kommen einfach nicht hervor. Bei oh-my-zsh braucht nur das Repository von github gecloned und eine ~/.zshrc angelegt werden, die im Grunde nicht mehr als zwei Zeilen beinhaltet, um oh-my-zsh zu aktivieren. Weiterhin können in der eigenen ~/.zshrc Plugins oder Themes aktiviert werden.

Bei mir sieht die ~/.zshrc so aus:

ZSH=$HOME/.oh-my-zsh
ZSH_THEME="powerline"
COMPLETION_WAITING_DOTS="true"
POWERLINE_RIGHT_A="exit-status"
POWERLINE_NO_BLANK_LINE="true"
plugins=(git brew github osx python screen ssh-agent mosh terminalapp)
source $ZSH/oh-my-zsh.sh

Das Ergebnis sieht dann zusammen mit der monoOne so aus:

Screen Shot 2013-11-03 at 14.56.50

 

In der linken Infobox habe ich meinen Usernamen, den Host, dann den Pfad und noch ähnlich wie beim vim-Plugin die Information über das git-repository und den Status der Dateien. Die rechte Infobox mit der Uhrzeit und dem Return-Code verschwindet, wenn der eingetippte Befehl länger als die Lücke zwischen den Boxen ist.

Community-Configs

Vor allem oh-my-zsh machte mir bewusst, wie stark der Einfluss von github auf den Austausch von Konfigurationsdateien ist. Obwohl es vorher viele andere Projekte gab, bei denen mitgewirkt werden konnte, ist es bei github viel einfacher. Es kann schnell ein Fork erstellt werden, um die eigene Konfiguration anzupassen und zu erweitern. Und wenn alles schön aussieht, kann diese Konfiguration auch wieder hoch geladen werden – oder ein Pull-Request geschickt werden, um die Erweiterung im Hauptprojekt aufzunehmen.

[28C3] Presale Sniper-Scripts

Der Vorverkauf zum 28C3 ist vorbei. Am Dienstagvormittag gab es die dritte Chance, Tickets für alle vier Tage zu erwerben. Vor Ort wird es nur noch Tages- und Nachttickets in sehr begrenzter Zahl geben.

Internetkurort
Statt zum Chaos Communication Congress mal schön Urlaub machen

Der Vorverkauf lief nicht besonders glücklich. Der kleine Server (ich kenne die Netzinfrastruktur nicht, aber vom Gefühl her, kommt das am besten hin) mit seinem nginx wurde während der Ticket-Verkaufszeiten mächtig attackiert und lieferte gerne den Fehler “502 Bad Gateway” aus statt die Presale-Webseite anzuzeigen. Insbesondere auf Twitter konnte unter dem Hashtag #28C3 das Ärgernis mitverfolgt werden. Die Ankündigung zum Vorverkauf im CCC-Events-Blog ist mit knapp 300 Kommentaren gut gefüllt.

Bereits zum ersten Vorverkaufstermin kursierten Scripte im Internet, die die Verpeilung abnehmen sollten und automagisch ein Ticket bestellen. Die Vorgehensweise ist grundsätzlich die gleiche: Der Besucher erstellt zu einem beliebigen Zeitpunkt während der gesamten Vorverkaufsphase einen Account im Presale-System, wählt das gewünschte Ticket aus und startet das Script. Dieses ruft in regelmäßigen Abständen die Webseite ab und überprüft, ob Tickets verfügbar sind. Daraufhin wird die Bestellung getätigt.

Ein paar Scripte habe ich gespeichert, aber nicht immer stand die Quelle oder Lizenz dabei. Zum Zweck des Selbststudiums erlaube ich mir die Freiheit, diese hier zu veröffentlichen.

Der automatisierte Webbrowser

#!/usr/bin/env python
# encoding: utf-8
"""
untitled.py

Created by Thorsten Philipp on 2011-11-06.
Copyright (c) 2011 . All rights reserved.
"""


import sys
import os
import platform
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
import time


USERNAME = 'your_username'
PASSWORD = 'your_password'

def main():

    driver = webdriver.Firefox()
    driver.get("https://presale.events.ccc.de/order")
   
    NO_TICKET = True
    while NO_TICKET:
        if driver.current_url == 'https://presale.events.ccc.de/accounts/sign_in':
            username = driver.find_element_by_xpath("//input[@id='account_username']")
            password = driver.find_element_by_xpath("//input[@id='account_password']")
            username.send_keys(USERNAME)
            password.send_keys(PASSWORD)
            password.submit()
       
        # Now wait for the order page
        WebDriverWait(driver, 10).until(lambda x : x.find_element_by_xpath("//input[@value='Confirm Order']"))
        confirmOrder = driver.find_element_by_xpath("//input[@value='Confirm Order']")
        confirmOrder.submit()
        time.sleep(5)
        errorField = driver.find_element_by_xpath("//div[@id='flash_error']")
        if errorField.text == 'There are currently not enough tickets available.':
            print "%s. Trying again" % errorField.text
            driver.get('https://presale.events.ccc.de/order')
        else:
            print "Something else happend! Maybe a successful order? I'm not touching anything any more. Please take over Commander"
            while True:
                if platform.system() == 'Darwin':
                    os.system("say Help")
                else:
                    print "\a"
                time.sleep(10)


if __name__ == '__main__':
    main()

Das erste mir bekannte Sniper-Script nutzt die Python Bindings des Selenium/Webdriver Projekts. Es öffnet einen Browser (in diesem Fall Firefox), ruft die Presale-Webseite auf, logt sich mit dem gegebenen Usernamen und Passwort ein und wartet in einer Schleife darauf, dass Tickets verfügbar sind. Dem Autor dieses ersten Scripts war nicht bekannt, wie die Seite aussehen würde, wenn Tickets vorhanden sind. Das Script gibt nur einen Hinweis aus (unter Mac OS X wird auch eine Sprachausgabe gestartet) und lässt den Benutzer weiter machen.

Screen-Scraping

import requests
from BeautifulSoup import BeautifulSoup
from time import sleep

s = requests.session()

username = ''
password = ''

print "CCC-Presale 28C3 Script by @sctan [https://github.com/szechuen/CCC-Presale]"

# Sign In

print "Accessing Sign In..."

while True:
    sign_in = s.get("https://presale.events.ccc.de/accounts/sign_in")
    if sign_in.ok: break
    print "Request Error - Retrying..."

sign_in_soup = BeautifulSoup(sign_in.content)

account_post = {}
for item in sign_in_soup.findAll("input", type="hidden"): account_post[item['name']] = item['value']
for item in sign_in_soup.findAll("input", type="submit"): account_post[item['name']] = item['value']
account_post['account[username]'] = username
account_post['account[password]'] = password

account_link = "https://presale.events.ccc.de" + sign_in_soup.find("form")['action']

# Account

print "Accessing Account..."

while True:
    account = s.post(account_link, data=account_post)
    if account.ok: break
    print "Request Error - Retrying..."

account_soup = BeautifulSoup(account.content)

ordered = False
page = account
soup = account_soup

while not ordered:

    post = {}
    for item in soup.findAll("input", type="hidden"): post[item['name']] = item['value']
    for item in soup.findAll("input", type="submit"): post[item['name']] = item['value']

    link = "https://presale.events.ccc.de" + soup.find("form")['action']

    while True:
        page = s.post(link, data=post)
        if page.ok: break
        print "Request Error - Retrying..."
   
    soup = BeautifulSoup(page.content)

    last = open("last.html", "w")
    last.write(soup.prettify())
    last.close()

    if soup.find(text=lambda(x): x.find("There are currently not enough tickets available") != -1):
        print "Not Open - Sleeping before retrying..."
        sleep(1)
    else:
        print "Ordered :D"
        ordered = True

Das zweite Script, auch in Python, benutzt den Screen-Scraper Beautiful Soup. Die Selbstbeschreibung lautet “an HTML parser optimized for screen-scraping”. Durch die erste Runde des Vorverkaufs war bereits bekannt, dass nur noch der ‘Confirm’-Button getätigt werden muss, wenn Tickets vorhanden sind. Das macht sich dieses Script zunutze und läuft komplett autark ohne Webbrowser ab. Es geht einfach nur die HTML-Sourcen durch, füllt die Eingabefelder zum Login aus und schickt HTTP-Kommandos ab.

Bash mit curl

#! /bin/bash


#---------------------login and password----------------------------------------

user=$1 && shift
pass=$1


echo "getting for user = $user, pass = $pass"


#-------------------------------------------------------------------------------


cookie="./coockie$user"
agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.928.0 Safari/535.8"



function callit() {

    cmd=$1
    echo $cmd
    res=$($1 -w "%{http_code}")
    echo $res


}

function prepare() {

    login=''


    page=$(curl --retry 100 --retry-delay 1 --connect-timeout 75  -s -A $agent -c $cookie "https://presale.events.ccc.de/accounts/sign_in")
   

    token=$(echo $page  | grep -o '<input name="authenticity_token" type="hidden" value=".*"' | sed s/'.*value="\(.*\)"'/'\1'/g)


    login="utf8=&#x2713;&authenticity_token=$token&account[username]=$user&account[password]=$pass&commit=Sign+in"

   
   
    page=$(curl --retry 100 --retry-delay 1 --connect-timeout 5 -A "$agent" -s -c $cookie  -X POST -d "$login" "https://presale.events.ccc.de/accounts/sign_in")

    page=$(curl --retry 100 --retry-delay 1 --connect-timeout 75 -s -A "$agent" -b $cookie  -c $cookie "https://presale.events.ccc.de/")

    echo $page | grep -o "<td>confirmed</td>" && echo "GOT ITTTTTTTTTTTTTT !!!" && exit 0


    token=$(echo $page| grep -o '<input name="authenticity_token" type="hidden" value="[^"]*"' | sed s/'.*value="\([^"]*\)"'/'\1'/g)


    order="utf8=&#x2713;&authenticity_token=$token&commit=Confirm+Order"

   
}



function getIt() {


    confirm=$(curl --retry 100 --retry-delay 1 -L  -A "$agent" -s -b $cookie  -c $cookie -X POST -d "$order" "https://presale.events.ccc.de/order/confirm/")

    res=$(curl --retry 100 --retry-delay 1 -s -A "$agent" -b $cookie  -c $cookie "https://presale.events.ccc.de/order")

    echo $res
}



prepare








while [ true ]
do

it=$(getIt)


echo $it | grep '<html><body>You are being <a href="https://presale.events.ccc.de/accounts/sign_in">redirected</a>.</body></html>' > /dev/null && echo "error in site " && prepare && continue

ret=$(echo $it| grep -o "<div id='flash[^>]*' class=visible>[^<]*</div>" | sed s/"<div id='flash_\(.*\)'.*visible>\(.*\)<\/div>"/"\1 : \2"/g)

status=$(echo $it | grep status)

echo $ret | grep "^error" && continue

echo $it | grep -o "<td>confirmed</td>" && echo "GOT ITTTTTTTTTTTTTT !!!" && break



done

Auch dieses Script konnte erst nach der ersten Runde des Vorverkaufs entstanden sein, da es überprüft, ob der Status nach der Order auf ‘confirmed’ gesetzt wurde. Der Ansatz, die Webseite mit curl abzurufen und mit grep die wichtigen Daten auszulesen, ist passabel, aber nicht wirklich effizient. Ich habe das Script nicht selbst getestet, aber ich kann mir vorstellen, dass es sehr gut an der CPU knabberte.

KISS-Prinzip

#!/bin/bash


while [ 1 ]; do
wget -q -T 10 -O out --load-cookies=cookie -U "`cat ua`" --post-file=postdata --no-check-certificate https://presale.events.ccc.de/order/confirm
if [ -s out ]; then
grep -q "Confirm Order" out || exit
fi
rm -f out
sleep 1
echo Retrying...
done

In einer Schleife ruft wget mit dem vorher im Browser erstellten Cookie die Routine zum Bestätigen der Order auf – drückt also quasi dauerhaft auf den Button. Das Script gibt erst dann auf, wenn auf der Seite nicht mehr der Text ‘Confirm Order’ erscheint. Eine sehr elegante Lösung, bei der nicht bekannt sein muss, wie die Bestätigungsmeldung aussieht.

Anti-Sniper

In der dritten Runde des Vorverkaufs wurde ein simples Captcha eingeführt. Mit eigenen Augen habe ich es nicht gesehen, aber es war wohl nur die Eingabe einer ausgeschriebenen Zahl notwendig (“six million, one thousand, eight hundred and 5”). Das reichte aus, um Menschen eine Chance zu lassen, denn in der Kürze bis zu dem Zeitpunkt, zu dem die Scripte umgeschrieben sind, dürften alle Tickets bereits vergeben sein.

Hier nicht gelistet, aber den Dämlichkeits-Preis erhält der Autor des Scripts, der es mit vollen Login-Daten auf eine Paste Server Webseite kopierte…