pretix-mark-as-paid/markaspaid.py

166 lines
5.5 KiB
Python
Executable File

#!/usr/bin/env python3
from getpass import getpass
from os.path import dirname, expanduser
from requests.auth import AuthBase
from requests.exceptions import RequestException
from sys import exit, stderr
import gi
import json
import os
import platform
import requests
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class Api:
class DeviceAuth(AuthBase):
def __init__(self, token):
self.token = token
def __call__(self, r):
r.headers['Authorization'] = f'Device {self.token}'
return r
def __init__(self, config):
self.config = config
def __call__(self, method, endpoint, **kwargs):
data = {'params' if method == 'GET' else 'json': kwargs}
r = requests.request(method, self.config['url'] + endpoint % self.config, auth=Api.DeviceAuth(self.config['api_token']), **data)
try:
r.raise_for_status()
except RequestException as e:
print(e, file=stderr)
print(r.text, file=stderr)
raise e
if r.headers['content-type'] == 'application/json':
return r.json()
return r.text
class Window(Gtk.Window):
def __init__(self, config):
super().__init__(title='Mark as paid')
self.config = config
self.api = Api(config)
self.entry = Gtk.Entry()
self.entry.set_visibility(False)
self.entry.set_width_chars(32)
self.entry.connect('activate', self.on_entry_activate)
self.add(self.entry)
def info(self, primary_text, secondary_text):
dialog = Gtk.MessageDialog(
transient_for=self,
flags=0,
message_type=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.OK,
text=primary_text,
)
dialog.format_secondary_text(secondary_text)
dialog.run()
dialog.destroy()
def on_entry_activate(self, widget):
results = self.api('GET', '/api/v1/organizers/%(organizer)s/checkinrpc/search/', ignore_status='true', list=self.config['list'], search=widget.get_text())
widget.set_text('')
if results['count'] == 0:
self.info('Keine Ergebnisse', 'Bitte präzisiere deine Eingabe.')
return
if results['count'] > 1:
self.info('Mehrere Ergebnisse', 'Bitte präzisiere deine Eingabe.')
return
order = self.api('GET', f"/api/v1/organizers/%(organizer)s/events/%(event)s/orders/{results['results'][0]['order']}/")
if order['status'] == 'p':
self.info('Bereits bezahlt', 'Die Bestellung ist bereits bezahlt.')
return
if order['status'] == 'e':
self.info('Abgelaufen', 'Die Bestellung ist abgelaufen.')
return
if order['status'] == 'c':
self.info('Storniert', 'Die Bestellung wurde storniert.')
return
dialog = Gtk.MessageDialog(
transient_for=self,
flags=0,
message_type=Gtk.MessageType.QUESTION,
buttons=Gtk.ButtonsType.YES_NO,
text=f"Gesamtbetrag: {order['total']}",
)
dialog.format_secondary_text('Wurde der gesamte Betrag in bar entgegengenommen?')
response = dialog.run()
dialog.destroy()
if response == Gtk.ResponseType.YES:
try:
self.api('POST', f"/api/v1/organizers/%(organizer)s/events/%(event)s/orders/{results['results'][0]['order']}/mark_paid/")
self.info('Zahlung bestätigt', 'Die Zahlung wurde bestätigt, die Tickets sind nun gültig.')
except RequestException:
dialog = Gtk.MessageDialog(
transient_for=self,
flags=0,
message_type=Gtk.MessageType.ERROR,
buttons=Gtk.ButtonsType.OK,
text='Fehler',
)
dialog.format_secondary_text('Ein Fehler ist aufgetreten.')
dialog.run()
dialog.destroy()
if __name__ == '__main__':
config_path = expanduser('~/.config/pretix-markaspaid/config.json')
try:
with open(config_path) as f:
config = json.loads(f.read())
except FileNotFoundError:
handshake = json.loads(getpass('Please scan initialization qr code... '))
if handshake['handshake_version'] != 1:
print(f"Unknown handshake version {handshake['handshake_version']}", file=stderr)
exit(1)
r = requests.post(f"{handshake['url']}/api/v1/device/initialize", json={
'hardware_brand': platform.system(),
'hardware_model': platform.release(),
'software_brand': 'mark-as-paid',
'software_version': '0.1.0',
'token': handshake['token'],
})
try:
r.raise_for_status()
except RequestException as e:
print(e, file=stderr)
print(r.text, file=stderr)
exit(1)
r = r.json()
config = {
'api_token': r['api_token'],
'device_id': r['device_id'],
'event': input('Event: '),
'list': int(input('Check-in list: ')),
'organizer': r['organizer'],
'unique_serial': r['unique_serial'],
'url': handshake['url'],
}
os.makedirs(dirname(config_path))
with open(config_path, 'w') as f:
f.write(json.dumps(config))
window = Window(config)
window.connect('destroy', Gtk.main_quit)
window.show_all()
Gtk.main()