2011-08-03 23:58:17 +02:00
|
|
|
#! /usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
|
|
|
# Copyright (C) 2011 Sebastian Pipping <sebastian@pipping.org>
|
|
|
|
# Licensed under GPL v3 or later
|
|
|
|
|
|
|
|
from __future__ import print_function
|
2011-10-05 11:35:56 +02:00
|
|
|
import freitagslib.network as net
|
2011-10-10 07:33:13 +02:00
|
|
|
from freitagslib.commands import BuyCommand, DepositCommand, ToGoCommand
|
2011-08-03 23:58:17 +02:00
|
|
|
|
2011-10-10 04:03:57 +02:00
|
|
|
import colorama
|
|
|
|
from colorama import Fore, Style
|
|
|
|
|
|
|
|
|
2011-08-03 23:58:17 +02:00
|
|
|
import sys
|
2011-10-05 12:43:23 +02:00
|
|
|
from decimal import Decimal
|
2011-08-03 23:58:17 +02:00
|
|
|
import os
|
|
|
|
import time
|
2011-10-05 16:11:45 +02:00
|
|
|
import urllib2
|
2011-08-03 23:58:17 +02:00
|
|
|
|
|
|
|
|
2011-10-10 04:03:57 +02:00
|
|
|
COLOR_WARN = Fore.YELLOW
|
|
|
|
COLOR_ERROR = Fore.RED
|
|
|
|
COLOR_DEPOSIT = Fore.GREEN + Style.BRIGHT
|
2011-10-10 07:33:13 +02:00
|
|
|
COLOR_TO_GO = Fore.MAGENTA + Style.BRIGHT
|
2011-10-10 04:03:57 +02:00
|
|
|
COLOR_WITHDRAW = Fore.RED + Style.BRIGHT
|
|
|
|
|
|
|
|
COLOR_SOME = Fore.WHITE + Style.BRIGHT
|
|
|
|
COLOR_MUCH = Fore.YELLOW + Style.BRIGHT
|
|
|
|
|
|
|
|
COLOR_RESET = Style.RESET_ALL
|
|
|
|
|
2011-10-12 02:28:35 +02:00
|
|
|
display_fifo = None
|
|
|
|
|
|
|
|
|
|
|
|
def print_display(s):
|
|
|
|
global display_fifo
|
|
|
|
if not display_fifo:
|
|
|
|
try:
|
2011-10-12 02:46:36 +02:00
|
|
|
#display_fifo = open('/tmp/display', 'w')
|
|
|
|
fd = os.open( "/tmp/display", os.O_WRONLY)
|
|
|
|
display_fifo = os.fdopen(fd,"w")
|
2011-10-12 02:28:35 +02:00
|
|
|
except IOError:
|
|
|
|
display_fifo = None
|
|
|
|
return
|
|
|
|
display_fifo.write(s)
|
|
|
|
display_fifo.flush()
|
|
|
|
|
2011-10-10 04:03:57 +02:00
|
|
|
|
2011-08-03 23:58:17 +02:00
|
|
|
def clear():
|
|
|
|
os.system('clear')
|
|
|
|
|
|
|
|
|
2011-10-10 21:58:35 +02:00
|
|
|
TO_GO_ONLY = 2
|
2011-10-10 07:33:13 +02:00
|
|
|
TO_GO_ALL = 1
|
|
|
|
TO_GO_NONE = 0
|
|
|
|
TO_GO_PREV = -1
|
|
|
|
|
2011-08-03 23:58:17 +02:00
|
|
|
CODES = {
|
|
|
|
'UNDO':('undo',),
|
|
|
|
'COMMIT':('commit',),
|
|
|
|
|
2011-10-10 07:33:13 +02:00
|
|
|
'TO GO ALL':('to_go', TO_GO_ALL),
|
|
|
|
'TO GO NONE':('to_go', TO_GO_NONE),
|
|
|
|
'TO GO PREV':('to_go', TO_GO_PREV),
|
2011-10-10 21:58:35 +02:00
|
|
|
'TO GO ONLY':('to_go', TO_GO_ONLY),
|
2011-10-10 07:33:13 +02:00
|
|
|
|
2011-10-05 12:43:23 +02:00
|
|
|
'DEPOSIT 0.01':('deposit', Decimal('0.01')),
|
|
|
|
'DEPOSIT 0.05':('deposit', Decimal('0.05')),
|
|
|
|
'DEPOSIT 0.10':('deposit', Decimal('0.10')),
|
|
|
|
'DEPOSIT 0.50':('deposit', Decimal('0.50')),
|
|
|
|
'DEPOSIT 1.00':('deposit', Decimal('1.00')),
|
|
|
|
'DEPOSIT 5.00':('deposit', Decimal('5.00')),
|
|
|
|
'DEPOSIT 10.00':('deposit', Decimal('10.00')),
|
|
|
|
'DEPOSIT 50.00':('deposit', Decimal('50.00')),
|
2011-08-03 23:58:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def delay(what, seconds):
|
|
|
|
for i in xrange(seconds, 0, -1):
|
|
|
|
if i < seconds:
|
|
|
|
sys.stdout.write('\r')
|
|
|
|
sys.stdout.write('%s in %d Sekunden... ' % (what, i))
|
|
|
|
sys.stdout.flush()
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
|
|
|
|
def warn_balance():
|
2011-10-12 01:35:07 +02:00
|
|
|
print('Kontostand im Minus, bitte Geld aufladen.')
|
2011-10-12 19:53:05 +02:00
|
|
|
print_display('\e\x0cKontostand im Minus,bitte Geld aufladen.')
|
2011-08-03 23:58:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
def error_page(message):
|
|
|
|
clear()
|
2011-10-10 04:03:57 +02:00
|
|
|
print(COLOR_ERROR + message + COLOR_RESET)
|
2011-08-03 23:58:17 +02:00
|
|
|
print()
|
|
|
|
delay('Weiter', 3)
|
|
|
|
|
|
|
|
|
|
|
|
class Status:
|
|
|
|
def __init__(self):
|
|
|
|
self._reset()
|
2011-10-05 12:39:22 +02:00
|
|
|
self.item_cache = dict()
|
2011-08-03 23:58:17 +02:00
|
|
|
|
|
|
|
def _reset(self):
|
2011-10-05 11:43:01 +02:00
|
|
|
self.auth_blob = None
|
2011-08-03 23:58:17 +02:00
|
|
|
self.login_name = None
|
|
|
|
self.balance = None
|
|
|
|
self.transfers = None
|
|
|
|
|
|
|
|
def dump(self):
|
2011-10-10 04:03:57 +02:00
|
|
|
def sign(amount, plus='+'):
|
|
|
|
return '-' if amount < 0 else plus
|
|
|
|
|
|
|
|
def color(amount):
|
|
|
|
return COLOR_WITHDRAW if amount < 0 else COLOR_DEPOSIT
|
|
|
|
|
2011-10-10 07:33:13 +02:00
|
|
|
def color_command(command):
|
|
|
|
diff = command.difference()
|
|
|
|
if diff >= 0:
|
|
|
|
return COLOR_DEPOSIT
|
|
|
|
return COLOR_TO_GO if isinstance(command, ToGoCommand) else COLOR_WITHDRAW
|
|
|
|
|
2011-10-10 04:03:57 +02:00
|
|
|
def show_total(balance, plus=' '):
|
|
|
|
print('%3s %-40s %s%c %6.2f Euro%s' \
|
|
|
|
% ('', '', color(balance), sign(balance, plus),
|
|
|
|
abs(balance), COLOR_RESET))
|
|
|
|
|
|
|
|
def show_item(position, command):
|
|
|
|
diff = command.difference()
|
|
|
|
label = command.label()
|
|
|
|
print('%2d) %-40s %s%c %6.2f Euro%s' \
|
2011-10-10 07:33:13 +02:00
|
|
|
% (position, label, color_command(command), sign(diff),
|
2011-10-10 04:03:57 +02:00
|
|
|
abs(diff), COLOR_RESET))
|
|
|
|
|
|
|
|
def show_bar():
|
|
|
|
print('%3s %-40s %13s' % ('', '', '============='))
|
|
|
|
|
2011-08-03 23:58:17 +02:00
|
|
|
if self.logged_in():
|
2011-10-10 04:03:57 +02:00
|
|
|
print('Eingeloggt als: %s%s%s' % (COLOR_SOME, self.login_name, COLOR_RESET))
|
2011-08-03 23:58:17 +02:00
|
|
|
print()
|
2011-10-12 02:28:35 +02:00
|
|
|
print_display('\e\x0cBenutzer: %10s' %self.login_name[:10])
|
2011-08-03 23:58:17 +02:00
|
|
|
if self.transfers:
|
2011-10-05 12:31:48 +02:00
|
|
|
initial_command, initial_balance = self.transfers[0]
|
2011-10-10 04:03:57 +02:00
|
|
|
|
|
|
|
print('Geplante Änderungen:')
|
|
|
|
show_total(initial_balance)
|
2011-10-05 12:31:48 +02:00
|
|
|
for i, (command, balance_backup) in enumerate(self.transfers):
|
2011-10-10 04:03:57 +02:00
|
|
|
show_item(i + 1, command)
|
|
|
|
show_bar()
|
2011-10-10 04:46:34 +02:00
|
|
|
if len(self.transfers) > 1:
|
|
|
|
show_total(self.balance - initial_balance, plus='+')
|
|
|
|
show_bar()
|
2011-10-10 04:03:57 +02:00
|
|
|
show_total(self.balance)
|
2011-08-03 23:58:17 +02:00
|
|
|
|
|
|
|
if self.balance < 0:
|
|
|
|
warn_balance()
|
|
|
|
print()
|
|
|
|
|
2011-10-10 04:03:57 +02:00
|
|
|
print(COLOR_SOME + 'Committen nicht vergessen.' + COLOR_RESET)
|
2011-08-03 23:58:17 +02:00
|
|
|
else:
|
2011-10-10 04:03:57 +02:00
|
|
|
print('Kontostand beträgt: %s%.2f Euro%s' % (COLOR_MUCH, self.balance, COLOR_RESET))
|
2011-10-12 02:28:35 +02:00
|
|
|
print_display('Kontostand: %8.2f' % self.balance)
|
2011-08-03 23:58:17 +02:00
|
|
|
if self.balance < 0:
|
|
|
|
print()
|
|
|
|
warn_balance()
|
|
|
|
else:
|
2011-10-10 04:03:57 +02:00
|
|
|
print(COLOR_MUCH + 'Bitte einloggen.' + COLOR_RESET)
|
2011-08-03 23:58:17 +02:00
|
|
|
print()
|
|
|
|
print('Scanne dazu deine ID-Karte mit dem Barcode-Leser.')
|
2011-10-12 02:28:35 +02:00
|
|
|
print_display("\e\x0cAnmeldung: Bitte ID-Karte scannen...")
|
2011-08-03 23:58:17 +02:00
|
|
|
print()
|
|
|
|
|
|
|
|
def logged_in(self):
|
2011-10-05 11:43:01 +02:00
|
|
|
return self.auth_blob is not None
|
2011-08-03 23:58:17 +02:00
|
|
|
|
2011-10-05 11:43:01 +02:00
|
|
|
def login(self, auth_blob):
|
2011-08-03 23:58:17 +02:00
|
|
|
assert(not self.logged_in())
|
2011-10-05 16:11:45 +02:00
|
|
|
|
2011-10-05 12:31:48 +02:00
|
|
|
user_name = net.get_user_name_from_auth_blob(auth_blob)
|
2011-10-05 16:11:45 +02:00
|
|
|
balance = net.get_balance(user_name)
|
|
|
|
|
2011-10-05 11:43:01 +02:00
|
|
|
self.auth_blob = auth_blob
|
2011-10-05 12:31:48 +02:00
|
|
|
self.login_name = user_name
|
2011-10-05 16:11:45 +02:00
|
|
|
self.balance = balance
|
2011-08-03 23:58:17 +02:00
|
|
|
self.transfers = list()
|
|
|
|
|
|
|
|
def commit(self):
|
|
|
|
assert(self.logged_in())
|
2011-10-05 12:31:48 +02:00
|
|
|
|
|
|
|
# Process command queue
|
2011-10-05 16:11:45 +02:00
|
|
|
for (command, balance_backup) in list(self.transfers):
|
|
|
|
try:
|
|
|
|
command.run(self.login_name)
|
|
|
|
except urllib2.HTTPError as e:
|
2011-10-12 02:28:35 +02:00
|
|
|
print_display('\e\x0cFEHLER: Server Error%20s' % str(e)[:20])
|
2011-10-05 16:11:45 +02:00
|
|
|
error_page('FEHLER bei Kommunikation mit Server "%s"' % str(e))
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
self.transfers.pop(0)
|
2011-08-03 23:58:17 +02:00
|
|
|
|
2011-10-05 16:11:45 +02:00
|
|
|
if not self.transfers:
|
|
|
|
# Show final balance for some time
|
|
|
|
clear()
|
|
|
|
self.dump()
|
|
|
|
delay('Logout', 3)
|
2011-08-03 23:58:17 +02:00
|
|
|
|
2011-10-05 16:11:45 +02:00
|
|
|
# Logout
|
|
|
|
self._reset()
|
2011-08-03 23:58:17 +02:00
|
|
|
|
2011-10-05 21:50:14 +02:00
|
|
|
def find(self, barcode):
|
2011-10-05 12:39:22 +02:00
|
|
|
try:
|
2011-10-05 21:50:14 +02:00
|
|
|
return self.item_cache[barcode]
|
2011-10-05 12:39:22 +02:00
|
|
|
except KeyError:
|
2011-10-05 21:50:14 +02:00
|
|
|
item = net.get_item(barcode)
|
|
|
|
self.item_cache[barcode] = item
|
2011-10-05 12:39:22 +02:00
|
|
|
return item
|
2011-08-03 23:58:17 +02:00
|
|
|
|
2011-10-05 12:31:48 +02:00
|
|
|
def buy(self, item):
|
2011-08-03 23:58:17 +02:00
|
|
|
assert(self.logged_in())
|
2011-10-05 12:31:48 +02:00
|
|
|
log_entry = (BuyCommand(item), self.balance)
|
|
|
|
self.transfers.append(log_entry)
|
|
|
|
self.balance = self.balance - item.price
|
2011-08-03 23:58:17 +02:00
|
|
|
|
|
|
|
def deposit(self, amount):
|
|
|
|
assert(self.logged_in())
|
2011-10-05 12:31:48 +02:00
|
|
|
log_entry = (DepositCommand(amount), self.balance)
|
|
|
|
self.transfers.append(log_entry)
|
|
|
|
self.balance = self.balance + amount
|
2011-08-03 23:58:17 +02:00
|
|
|
|
2011-10-10 07:33:13 +02:00
|
|
|
def to_go(self, scope):
|
|
|
|
_PRODUCT_FIRST = 'FEHLER: Bitte zuerst den betreffenden Artikel scannen.'
|
|
|
|
if not self.transfers:
|
|
|
|
error_page(_PRODUCT_FIRST)
|
|
|
|
return
|
|
|
|
|
|
|
|
if scope == TO_GO_ALL:
|
2011-10-10 21:58:35 +02:00
|
|
|
"""
|
|
|
|
Adds a ToGoCommands for each BuyCommands with deposit > 0 lacking a pair
|
|
|
|
"""
|
2011-10-10 07:33:13 +02:00
|
|
|
after = list()
|
|
|
|
dummy, initial_balance = self.transfers[0]
|
|
|
|
balance_before = initial_balance
|
|
|
|
|
|
|
|
def body(prev, command, balance_before):
|
|
|
|
if isinstance(prev, BuyCommand) \
|
|
|
|
and prev.deposit() > 0 \
|
2011-10-10 21:58:35 +02:00
|
|
|
and (not isinstance(command, ToGoCommand) \
|
|
|
|
or (isinstance(command, ToGoCommand) \
|
|
|
|
and prev.item() != command.item())):
|
|
|
|
to_go = ToGoCommand(prev.item())
|
2011-10-10 07:33:13 +02:00
|
|
|
prev.set_to_go(True)
|
|
|
|
after.append((to_go, balance_before))
|
|
|
|
balance_before += to_go.difference()
|
|
|
|
if command is not None:
|
|
|
|
after.append((command, balance_before))
|
|
|
|
balance_before += command.difference()
|
|
|
|
return balance_before
|
|
|
|
|
|
|
|
prev = None
|
|
|
|
for command, dummy in list(self.transfers):
|
|
|
|
balance_before = body(prev, command, balance_before)
|
|
|
|
prev = command
|
|
|
|
balance_before = body(prev, None, balance_before)
|
|
|
|
|
|
|
|
self.balance = balance_before
|
|
|
|
self.transfers = after
|
|
|
|
elif scope == TO_GO_NONE:
|
2011-10-10 21:58:35 +02:00
|
|
|
"""
|
|
|
|
Deletes all ToGoCommands
|
|
|
|
"""
|
2011-10-10 07:33:13 +02:00
|
|
|
after = list()
|
|
|
|
first_command, initial_balance = self.transfers[0]
|
|
|
|
balance_before = initial_balance
|
|
|
|
|
|
|
|
for command, dummy in list(self.transfers):
|
|
|
|
if isinstance(command, BuyCommand) \
|
|
|
|
and command.is_to_go():
|
|
|
|
command.set_to_go(False)
|
|
|
|
elif isinstance(command, ToGoCommand):
|
|
|
|
continue
|
|
|
|
after.append((command, balance_before))
|
|
|
|
balance_before += command.difference()
|
|
|
|
|
|
|
|
self.balance = balance_before
|
|
|
|
self.transfers = after
|
|
|
|
elif scope == TO_GO_PREV:
|
2011-10-10 21:58:35 +02:00
|
|
|
"""
|
|
|
|
Adds a ToGoCommand after previous BuyCommand
|
|
|
|
"""
|
2011-10-10 07:33:13 +02:00
|
|
|
prev, balance_backup = self.transfers[-1]
|
|
|
|
if not isinstance(prev, BuyCommand):
|
|
|
|
error_page(_PRODUCT_FIRST)
|
|
|
|
return
|
2011-10-10 21:58:35 +02:00
|
|
|
to_go = ToGoCommand(prev.item())
|
2011-10-10 07:33:13 +02:00
|
|
|
prev.set_to_go(True)
|
|
|
|
self.transfers.append((to_go, self.balance))
|
|
|
|
self.balance += to_go.difference()
|
2011-10-10 21:58:35 +02:00
|
|
|
elif scope == TO_GO_ONLY:
|
|
|
|
"""
|
|
|
|
Replace all BuyCommands with deposit > 0 by
|
|
|
|
a ToGoCommand. Resolve ToGoCommand duplicates.
|
|
|
|
"""
|
|
|
|
after = list()
|
|
|
|
first_command, initial_balance = self.transfers[0]
|
|
|
|
balance_before = initial_balance
|
|
|
|
|
|
|
|
for command, dummy in list(self.transfers):
|
|
|
|
if isinstance(command, ToGoCommand) \
|
|
|
|
and prev_item is not None \
|
|
|
|
and command.item() == prev_item:
|
|
|
|
"""
|
|
|
|
Skip this very duplicate
|
|
|
|
"""
|
|
|
|
prev_item = None
|
|
|
|
continue
|
|
|
|
elif isinstance(command, BuyCommand) \
|
|
|
|
and command.deposit() > 0:
|
|
|
|
"""
|
|
|
|
Replace by ToGoCommand
|
|
|
|
"""
|
|
|
|
item = command.item()
|
|
|
|
command = ToGoCommand(item)
|
|
|
|
prev_item = item
|
|
|
|
else:
|
|
|
|
prev_item = None
|
|
|
|
after.append((command, balance_before))
|
|
|
|
balance_before += command.difference()
|
|
|
|
|
|
|
|
self.balance = balance_before
|
|
|
|
self.transfers = after
|
2011-10-10 07:33:13 +02:00
|
|
|
|
2011-08-03 23:58:17 +02:00
|
|
|
def undo(self):
|
|
|
|
assert(self.logged_in())
|
|
|
|
if self.transfers:
|
2011-10-10 07:33:13 +02:00
|
|
|
(last_command, balance_backup) = self.transfers[-1]
|
|
|
|
if isinstance(last_command, ToGoCommand) \
|
|
|
|
and self.transfers:
|
|
|
|
second_last_command = self.transfers[-2]
|
|
|
|
if isinstance(second_last_command, BuyCommand):
|
|
|
|
second_last_command.set_to_go(False)
|
2011-08-03 23:58:17 +02:00
|
|
|
self.transfers.pop()
|
|
|
|
self.balance = balance_backup
|
|
|
|
else:
|
|
|
|
error_page('FEHLER: Nichts da, was ich rückgängig machen könnte.')
|
|
|
|
|
|
|
|
|
|
|
|
def print_prompt():
|
|
|
|
sys.stdout.write(">>> ")
|
|
|
|
|
|
|
|
|
|
|
|
def handle(line, status):
|
|
|
|
if line == 'exit':
|
|
|
|
clear()
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
if status.logged_in():
|
|
|
|
if line in CODES:
|
|
|
|
call = CODES[line]
|
|
|
|
method = call[0]
|
|
|
|
params = call[1:]
|
|
|
|
getattr(status, method)(*params)
|
|
|
|
else:
|
2011-10-05 12:31:48 +02:00
|
|
|
try:
|
2011-10-05 21:50:14 +02:00
|
|
|
item = status.find(line)
|
2011-10-05 16:11:45 +02:00
|
|
|
except urllib2.HTTPError as e:
|
|
|
|
if e.code == 404: # URL not found == item not found with REST
|
2011-10-12 02:28:35 +02:00
|
|
|
print_display('\e\x0cFEHLER:\nAktion/ware unbekannt')
|
2011-10-05 16:11:45 +02:00
|
|
|
error_page('FEHLER: Aktion oder Ware "%s" nicht bekannt' % line)
|
|
|
|
else:
|
2011-10-12 02:28:35 +02:00
|
|
|
print_display('\e\x0cFEHLER: Server Error%20s' % str(e)[:20])
|
2011-10-05 16:11:45 +02:00
|
|
|
error_page('FEHLER bei Kommunikation mit Server "%s"' % str(e))
|
2011-10-05 12:31:48 +02:00
|
|
|
else:
|
|
|
|
status.buy(item)
|
2011-08-03 23:58:17 +02:00
|
|
|
else:
|
2011-10-05 16:11:45 +02:00
|
|
|
try:
|
|
|
|
status.login(line)
|
2011-10-12 02:14:03 +02:00
|
|
|
except urllib2.HTTPError as e:
|
2011-10-12 01:35:07 +02:00
|
|
|
if e.code == 404: # URL not found == user unknown
|
2011-10-12 02:28:35 +02:00
|
|
|
print_display('\e\x0cFEHLER: Nutzer ist unbekannt: "%7s"' % line[:7])
|
2011-10-12 01:35:07 +02:00
|
|
|
error_page('FEHLER: Benutzer "%s" nicht bekannt' % line)
|
|
|
|
else:
|
2011-10-12 02:28:35 +02:00
|
|
|
print_display('\e\x0cFEHLER: Server Error%20s' % str(e)[:20])
|
2011-10-12 01:35:07 +02:00
|
|
|
error_page('FEHLER bei Kommunikation mit Server "%s"' % str(e))
|
2011-10-12 02:14:03 +02:00
|
|
|
except urllib2.URLError as e:
|
|
|
|
error_page('FEHLER bei Kommunikation mit Server "%s"' % str(e))
|
2011-08-03 23:58:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
def main():
|
2011-10-10 04:03:57 +02:00
|
|
|
colorama.init()
|
2011-08-03 23:58:17 +02:00
|
|
|
status = Status()
|
|
|
|
|
2011-10-12 02:28:35 +02:00
|
|
|
print_display("\x1b\x40Kassensystem \n startet...")
|
|
|
|
|
2011-08-03 23:58:17 +02:00
|
|
|
while True:
|
|
|
|
clear()
|
|
|
|
status.dump()
|
|
|
|
print_prompt()
|
|
|
|
l = sys.stdin.readline()
|
|
|
|
if not l:
|
|
|
|
break
|
|
|
|
line = l.rstrip()
|
2011-10-10 07:36:19 +02:00
|
|
|
if line:
|
|
|
|
handle(line, status)
|
2011-08-03 23:58:17 +02:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2011-10-05 17:45:32 +02:00
|
|
|
try:
|
|
|
|
main()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
pass
|
2011-10-12 02:28:35 +02:00
|
|
|
print_display("\x1b\x40Goodbye!")
|