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.

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
# 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
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
#---------------------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=✓&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=✓&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
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…
Moin Pylon,
vielen Dank für Deine Erklärungen – ehrlichgesagt sind die Postings, die ich hier auf deinem Blog zum Congress lese, etwas, was sich so auch auf dem Congress-Blog selbst finden sollte. Frustvermeidung und so. Schön, dass Du Dir die Mühe machst 🙂
Allerdings fehlt mir hier aber die Ursachenbetrachtung, warum überhaupt solche Scripts geschrieben werden. Das Presale-System ist legendär schlecht – ich habe bei allen drei VVK-Terminen versucht, ein Ticket zu bekommen (ganz ohne Scripts), und hatte erst beim letzten Termin nach gefühlt 200 Reload-Klicks Erfolg. Eine Performance, die auch letztes Jahr nicht anders lief.
Dass das natürlich für massiven Frust sorgt, und gerade bei einem technisch versiertem Publikum wie dem durchschnittlichem Congress-Besucher solche Scriptlösungen erfordert ist doch klar. Aber wäre es hier nicht sinnvoller, statt hier nun noch ein Eskalationslayer in Form eines Captcha drüber zu legen, auf das dann beim 29C3 die Scripte sicher entsprechend vorbereitet sind, lieber mal die Ursache zu beheben? Entweder technisch, in Form einer Applikation die nicht sofort komplett stirbt, oder in dem man den logischen Ablauf des Verkaufs von FCFS in irgendeine ausgewogenere Methode ändert, die auch denjenigen die Chance lässt, die was anderes zu tun haben, als 24/7 vor dem Rechner zu sitzen. Meine persönliche Präferenz wäre da übrigens klar die zweite Variante.
Ich habe von mehreren Personen gehört, dass die Postings durchaus für das events-Blog interessant sind. Aber ich habe auch (laut)starke Gegenstimmen gehört. Mein Blog soll nicht die “Hofberichtserstattung” für den Congress werden. Mein Anliegen ist es, Offensichtliches oder Gesehenes zusammenzufassen. Viele Themen sind älter und ich wärme sie nur etwas auf, um sie wieder ins Bewusstsein zu holen.
Was den Presale angeht, so finde ich, gibt es nur eine vernünftige Lösung: eine Location finden, die Platz genug für alle hat und wo wieder eine Kasse mit Bezahlung vor Ort angeboten werden kann. Da lässt es sich überlegen, ob eine Presale-Plattform zusätzlich angeboten werden soll, um – wie schon vor Jahren diskutiert – dem Nerd das Weihnachtsgeschenk in Form eines Tickets unter den Baum zu legen. Wenn es genug Platz für alle potentiellen Besucher gibt, kann der Presale wie beim Camp über einen längeren Zeitraum ohne eine Begrenzung des Kontingents laufen. Dann aber tatsächlich auf einer besseren Plattform, die zumindest die erste Last bei Freischaltung des Vorverkaufs abfangen zu können. Technisch ist das machbar. Es muss halt nur jemand machen…
Den ersten Vorverkauf hatte ich verpennt, beim zweiten war ich noch so naiv es manuell zu versuchen, beim dritten hatte ich mir endlich einen Bot mit der nötigen Durchschlagskraft programmiert. Hatte mich schon gefreut, doch was seh ich da in der Konsole? Dieses dumme CAPTCHA! Hatte eilig meinen Bot umprogrammiert, dass er die Lösung senden sollte, dabei aber einen Fehler gemacht, und das wars dann. Ein schwieriger zu knackendes CAPTCHA hättet ihr gleich am Anfang einführen sollen, als Abschreckung, damit es gar nicht erst zu diesem Hochrüsten gekommen wäre. Jetzt am Ende bringt es nichts mehr, und hat mir mein Ticket gekostet.
Woher kriege ich jetzt eines? Vom Schwarzmarkt? :/
Oder einen der Orte besuchen, an dem Streams des Congresses angeboten werden. In den letzten Jahren haben viele lokale CCC-Clubräume und andere Interessierte diese Möglichkeit angeboten. Der Grund nicht zum Congress zu fahren ist da unterschiedlich. Keine Karte erhalten, nicht das nötige Kleingeld für Anreise und Unterkunft oder weil die Personen schlicht arbeiten müssen, aber am Abend gerne gemeinsam mit anderen ein wenig Congress-Feeling im Kleinen mitnehmen wollen.
Von den Organisatoren von den vergangenen “Dragons everywhere” oder “Peace Missions” hörte ich, dass dadurch neue, interessiert Leute kennen gelernt wurden. Dadurch, dass weniger Hast als auf dem Congress besteht, hat man mehr Zeit füreinander und kommt tiefer in Gespräche. Außerdem ist das Internet in den Hackerspaces meist besser als auf dem Congress 😉
Hey, ich lebe in Berlin! Den Alexanderplatz kann ich zu Fuß erreichen. Da will ich nicht ausgesperrt sein. Außerdem wollte ich mich auf dem Congress mit einer alten Freundin treffen, was jetzt auch flach fällt. 🙁
Laut der im Artikel verlinkten Seite zu den Tickets wird es Tageskarten geben.
[…] [28C3] Presale Sniper-Scripts » Konvergenzfehler [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 Nachtticke… […]
Ich finde die jetzige Lösung mit dem Presale-System etwas „halbgar“ (sorry): das einzige was man zu sehen kriegt ist eine Fehlermeldung weil der Server überlastet ist => das ist einfach nur frustrierend. Da stelle ich mich dann doch lieber 1 Stunde in die Schlange.
Alternativen:
* ein System das den Ansturm halbwegs verkraftet
* eine ECHTE VERLOSUNG (mit Registrierung vorher, das ist dann auch wieder fair)
BTW: das Aufrüsten auf beiden Seiten mit Scripten und Captcha kann m.E. nicht die Lösung sein => führt nur zu einer unendlichen Spirale.
Michael (glücklos 2010 und 2011, der 2009 in der Schlange gestanden ist).
PS: die Tageskarten sind keine Alternative, da an den ersten 2 Tagen nicht erhältlich. Und in einem der „Außenstellen“ hat nicht das Flair der eigentlichen Veranstaltung.
Schlange stehen hat das Problem, dass ab einem bestimmten Zeitpunkt Besucher abgewiesen werden müssen. Was reichlich dämlich ist, wenn man eine längere Anreise hinter sich hat oder Zimmer gebucht. Dann lieber ein paar Wochen im Voraus Klarheit haben, ob man rein kommt oder nicht.