175 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import time
 | 
						|
import smtplib
 | 
						|
import yaml
 | 
						|
import requests
 | 
						|
import csv
 | 
						|
import qrcode
 | 
						|
import io
 | 
						|
import os
 | 
						|
from email.message import EmailMessage
 | 
						|
from flask import Flask, request, jsonify, abort
 | 
						|
from jinja2 import Template
 | 
						|
from functools import wraps
 | 
						|
from datetime import datetime
 | 
						|
 | 
						|
app = Flask(__name__)
 | 
						|
 | 
						|
with open("config.yaml", "r") as f:
 | 
						|
    config = yaml.safe_load(f)
 | 
						|
 | 
						|
MAIL_CONFIG = config["mails"]
 | 
						|
MEALS = config["meals"]
 | 
						|
TYPES = config["types"]
 | 
						|
PRETIX = config["pretix"]
 | 
						|
AUTH_TOKEN = config.get("auth", {}).get("token")
 | 
						|
DEBUG = config.get("debug", False)
 | 
						|
 | 
						|
with open("template.txt", "r") as f:
 | 
						|
    MAIL_TEMPLATE = Template(f.read())
 | 
						|
 | 
						|
def debug_print(msg):
 | 
						|
    if DEBUG:
 | 
						|
        print(msg)
 | 
						|
 | 
						|
def require_auth(f):
 | 
						|
    @wraps(f)
 | 
						|
    def decorated(*args, **kwargs):
 | 
						|
        token = request.headers.get("Authorization")
 | 
						|
        if not token or token != f"Bearer {AUTH_TOKEN}":
 | 
						|
            abort(401, description="Unauthorized: Invalid or missing token")
 | 
						|
        return f(*args, **kwargs)
 | 
						|
    return decorated
 | 
						|
 | 
						|
def send_email(to_email, subject, body, attachment_bytes, filename):
 | 
						|
    debug_print(f"[DEBUG] Sending email to {to_email} with attachment {filename}")
 | 
						|
    msg = EmailMessage()
 | 
						|
    msg["From"] = MAIL_CONFIG["sender"]
 | 
						|
    msg["To"] = to_email
 | 
						|
    msg["Subject"] = subject
 | 
						|
    msg.set_content(body)
 | 
						|
    msg.add_attachment(attachment_bytes, maintype="application", subtype="pdf", filename=filename)
 | 
						|
 | 
						|
    server = smtplib.SMTP(MAIL_CONFIG["server"], MAIL_CONFIG["port"])
 | 
						|
    if MAIL_CONFIG.get("starttls"):
 | 
						|
        server.starttls()
 | 
						|
    server.login(MAIL_CONFIG["user"], MAIL_CONFIG["password"])
 | 
						|
    server.send_message(msg)
 | 
						|
    server.quit()
 | 
						|
    debug_print("[DEBUG] Email sent successfully")
 | 
						|
 | 
						|
def get_meal_times(meal_key):
 | 
						|
    if meal_key.endswith("mittag"):
 | 
						|
        return "11:30 - 13:30"
 | 
						|
    elif meal_key.endswith("abend"):
 | 
						|
        return "17:30 - 19:30"
 | 
						|
    return ""
 | 
						|
 | 
						|
def log_printed(email, meal_key, typ):
 | 
						|
    location = email.split("@")[0]
 | 
						|
    now = datetime.now()
 | 
						|
    date_str = now.strftime("%Y-%m-%d")
 | 
						|
    time_str = now.strftime("%H:%M:%S")
 | 
						|
 | 
						|
    file_exists = os.path.isfile("printed.csv")
 | 
						|
 | 
						|
    with open("printed.csv", "a", newline="") as csvfile:
 | 
						|
        writer = csv.writer(csvfile, delimiter=";")
 | 
						|
 | 
						|
        if not file_exists:
 | 
						|
            writer.writerow(["Datum", "Zeit", "Mahlzeit", "Vouchertyp", "Location"])
 | 
						|
        writer.writerow([date_str, time_str, meal_key, typ, location])
 | 
						|
 | 
						|
    debug_print(f"[DEBUG] Logged printed PDF: {location} - {meal_key} - {typ}")
 | 
						|
 | 
						|
def generate_qr(secret):
 | 
						|
    qr = qrcode.QRCode(box_size=10, border=4)
 | 
						|
    qr.add_data(secret)
 | 
						|
    qr.make(fit=True)
 | 
						|
    img = qr.make_image(fill_color="black", back_color="white")
 | 
						|
    buf = io.BytesIO()
 | 
						|
    img.save(buf, format="PNG")
 | 
						|
    buf.seek(0)
 | 
						|
    return buf.getvalue()
 | 
						|
 | 
						|
@app.route("/order", methods=["POST"])
 | 
						|
@require_auth
 | 
						|
def order():
 | 
						|
    data = request.get_json()
 | 
						|
    debug_print(f"[DEBUG] Incoming request JSON: {data}")
 | 
						|
 | 
						|
    email = data.get("email")
 | 
						|
    typ = data.get("type")
 | 
						|
    meal_key = data.get("meal")
 | 
						|
 | 
						|
    if not email or not typ or not meal_key:
 | 
						|
        return jsonify({"error": "Missing field"}), 400
 | 
						|
 | 
						|
    meal_info = MEALS.get(meal_key)
 | 
						|
    if not meal_info:
 | 
						|
        return jsonify({"error": "Meal not found in config"}), 400
 | 
						|
 | 
						|
    position = {
 | 
						|
        "positionid": 1,
 | 
						|
        "item": TYPES.get(typ),
 | 
						|
        "variation": None,
 | 
						|
        "price": "0",
 | 
						|
        "attendee_email": None,
 | 
						|
        "addon_to": None,
 | 
						|
        "subevent": meal_info["subevent"]
 | 
						|
    }
 | 
						|
 | 
						|
    pretix_body = {
 | 
						|
        "email": "",
 | 
						|
        "locale": "en",
 | 
						|
        "sales_channel": "web",
 | 
						|
        "payment_provider": "manual",
 | 
						|
        "positions": [position]
 | 
						|
    }
 | 
						|
 | 
						|
    url = f"{PRETIX['host']}/api/v1/organizers/{PRETIX['organizer']}/events/{PRETIX['event']}/orders/"
 | 
						|
    headers = {"Authorization": f"Token {PRETIX['token']}"}
 | 
						|
 | 
						|
    debug_print(f"[DEBUG] Sending POST to Pretix: {url} with body {pretix_body}")
 | 
						|
    resp = requests.post(url, json=pretix_body, headers=headers)
 | 
						|
    debug_print(f"[DEBUG] Pretix response status: {resp.status_code}")
 | 
						|
    debug_print(f"[DEBUG] Pretix response text: {resp.text}")
 | 
						|
 | 
						|
    try:
 | 
						|
        resp_json = resp.json()
 | 
						|
        debug_print(f"[DEBUG] Pretix response JSON: {resp_json}")
 | 
						|
    except ValueError:
 | 
						|
        debug_print("[DEBUG] Pretix returned no JSON")
 | 
						|
        return jsonify({"error": "Pretix returned no JSON", "status_code": resp.status_code, "text": resp.text}), 502
 | 
						|
 | 
						|
    if "positions" in resp_json and "item" in resp_json["positions"][0] and isinstance(resp_json["positions"][0]["item"], list):
 | 
						|
        if isinstance(resp_json["positions"][0]["item"][0], str):
 | 
						|
            debug_print("[DEBUG] Not enough quota available")
 | 
						|
            return "Essen ist bereits alle", 418
 | 
						|
 | 
						|
    try:
 | 
						|
        secret = resp_json["positions"][0]["secret"]
 | 
						|
        debug_print(f"[DEBUG] Secret: {secret}")
 | 
						|
    except (KeyError, IndexError):
 | 
						|
        return jsonify({"error": "Secret not found in Pretix response", "resp_json": resp_json}), 500
 | 
						|
 | 
						|
    meal_times = get_meal_times(meal_key)
 | 
						|
 | 
						|
    mail_body = MAIL_TEMPLATE.render(
 | 
						|
        meal_name=meal_info["name"],
 | 
						|
        meal_name_en=meal_info["name_en"],
 | 
						|
        meal_date=meal_info["date"],
 | 
						|
        meal_times=meal_times
 | 
						|
    )
 | 
						|
 | 
						|
    if email.endswith("@printme.local"):
 | 
						|
        log_printed(email, meal_key, typ)
 | 
						|
        return secret, 200, {"Content-Type": "text/plain; charset=utf-8"}
 | 
						|
 | 
						|
    qr_bytes = generate_qr(secret)
 | 
						|
    send_email(email, "Dein Engelessen / Your Angel Meal", mail_body, qr_bytes, "ticket.png")
 | 
						|
 | 
						|
    return "Token gesendet", 201
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    app.run(host="0.0.0.0", port=8000, debug=DEBUG)
 |