import os import sqlite3 from flask import Flask, request, redirect, session from datetime import datetime, timezone, timedelta app = Flask(__name__) app.secret_key = "octan-tower-kragle-everything-is-awesome-2026" # für session (Adventure-Fortschritt) # ─── SQLite-Datenbank (U18) ────────────────────────────────────────────────── BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DB_PATH = os.path.join(BASE_DIR, "adventure.db") def get_db(): db = sqlite3.connect(DB_PATH) db.row_factory = sqlite3.Row # Spalten per Name ansprechen return db # ─── Zeitzone: CEST (UTC+2) ────────────────────────────────────────────────── BERLIN = timezone(timedelta(hours=2)) def jetzt(): return datetime.now(BERLIN).strftime("%d.%m.%Y, %H:%M Uhr") # ─── Kommentare im Arbeitsspeicher ─────────────────────────────────────────── # Schema: {"name": str, "text": str, "zeit": str, # "antworten": [{"name": str, "text": str, "zeit": str}]} eintraege = [] # ───────────────────────────────────────────────────────────────────────────── # Hilfsfunktion: gemeinsames HTML-Gerüst # ───────────────────────────────────────────────────────────────────────────── def seite(inhalt): return """ Flask-App – David & Karo
← Zurück zur Website
""" + inhalt + """
""" # ───────────────────────────────────────────────────────────────────────────── # U18 – Klick-Adventure "Einbruch im Octan-Tower" (The-Lego-Movie-Style) # Räume, Ausgänge und Trivia-Fragen kommen aus der SQLite-Datenbank adventure.db # ───────────────────────────────────────────────────────────────────────────── def adventure_seite(inhalt, akzent="#f59e0b"): kopf = """ Einbruch im Octan-Tower – Klick-Adventure
💬 Kommentare ← Website
""".replace("ACCENT", akzent) fuss = """
""" return kopf + inhalt + fuss @app.route("/") def start(): # Fortschritt für einen frischen Durchlauf zurücksetzen for key in [k for k in session.keys() if k.startswith("raum_")]: session.pop(key, None) inhalt = """
LEGO • Geheimmission

Einbruch im Octan-Tower 🧱

Präsident Business hortet ganz oben in seinem Wolkenkratzer das Kragle – und will damit die ganze Welt für immer festkleben. Nur du kannst ihn stoppen! Kämpf dich Etage für Etage nach oben: vorbei an Haien 🦈, Lasern 🔴 und – natürlich – Laserhaien 🦈⚡. Auf jeder Etage prüfen dich die Sicherheits­systeme mit Trivia-Fragen. Nur wer alle knackt, kommt weiter.

Abenteuer starten ▶

Lieber quatschen statt einbrechen? Zu den Kommentaren →

""" return adventure_seite(inhalt) @app.route("/frage") def frage(): return redirect("/app/") @app.route("/raum/", methods=["GET", "POST"]) def raum(id): db = get_db() r = db.execute("SELECT * FROM raeume WHERE id=?", (id,)).fetchone() if r is None: db.close() return adventure_seite( '
Fehler
' '

Diese Etage existiert nicht 🤖

' '

Da hat sich wohl ein Bauplan verheddert.

' 'Zurück zum Start
') fragen = db.execute( "SELECT * FROM fragen WHERE raum_id=? ORDER BY id", (id,) ).fetchall() prog_key = "raum_%d_idx" % id idx = session.get(prog_key, 0) # ── Es sind noch Sicherheitsfragen offen ── if fragen and idx < len(fragen): aktuelle = fragen[idx] feedback = "" if request.method == "POST": gewaehlt = request.form.get("antwort", "").strip() if gewaehlt.lower() == aktuelle["antwort"].strip().lower(): session[prog_key] = idx + 1 session.modified = True db.close() return redirect("/app/raum/%d" % id) feedback = ('
🚨 Alarm! Falsche Antwort – ' 'die Wächter wurden fast auf dich aufmerksam. Versuch es nochmal.
') optionen = aktuelle["optionen"].split("|") db.close() buttons = "" for opt in optionen: buttons += ('' % (esc(opt), esc(opt))) inhalt = ("""
🔐 Sicherheitsprüfung · %s

%s

Frage %d von %d

%s

%s

%s
""" % (esc(r["name"]), esc(r["name"]), idx + 1, len(fragen), feedback, esc(aktuelle["frage"]), buttons)) return adventure_seite(inhalt) # ── Alle Fragen gelöst (oder keine) → Beschreibung + Ausgänge / Sieg ── ausgaenge = db.execute( "SELECT richtung, nach_raum FROM ausgaenge WHERE von_raum=? ORDER BY rowid", (id,) ).fetchall() db.close() if r["ist_ziel"]: inhalt = ("""
🎉🧱🎉
Mission erfüllt

%s

%s

„Everything is awesome!“ 🎵

Nochmal spielen 🔁 Sieg in den Kommentaren feiern 💬
""" % (esc(r["name"]), esc(r["beschreibung"]))) return adventure_seite(inhalt) exits_html = "" for a in ausgaenge: exits_html += ('%s' % (a["nach_raum"], esc(a["richtung"]))) if not ausgaenge: exits_html = 'Zurück zum Start' geschafft = "" if fragen: # Etage hatte Fragen und alle wurden gelöst geschafft = ('

✅ Alle Sicherheitsfragen geknackt – ' 'der Weg ist frei!

') inhalt = ("""
🏢 %s

%s

%s

%s
%s
""" % (esc(r["name"]), esc(r["name"]), esc(r["beschreibung"]), geschafft, exits_html)) return adventure_seite(inhalt) # ───────────────────────────────────────────────────────────────────────────── # Route 3: Pinnwand – YouTube-Kommentar-Style mit Antwort-Funktion # ───────────────────────────────────────────────────────────────────────────── PINNWAND_CSS = """ """ def esc(s): return (s.replace("&", "&") .replace("<", "<") .replace(">", ">") .replace('"', """)) def avatar_farbe(name): farben = ["#e53935", "#8e24aa", "#1e88e5", "#00897b", "#43a047", "#fb8c00", "#6d4c41", "#546e7a"] idx = sum(ord(c) for c in name) % len(farben) return farben[idx] def render_antworten(antworten): html = "" for a in antworten: farbe = avatar_farbe(a["name"]) initial = a["name"][0].upper() html += f"""
{initial}
{esc(a['name'])} {esc(a['zeit'])}
{esc(a['text'])}
""" return html @app.route("/pinnwand", methods=["GET", "POST"]) def pinnwand(): if request.method == "POST": name = request.form.get("name", "").strip() or "Anonym" text = request.form.get("nachricht", "").strip() parent_raw = request.form.get("parent_id", "").strip() if text: if parent_raw.isdigit(): idx = int(parent_raw) if 0 <= idx < len(eintraege): eintraege[idx]["antworten"].append( {"name": name, "text": text, "zeit": jetzt()} ) else: eintraege.append( {"name": name, "text": text, "zeit": jetzt(), "antworten": []} ) return redirect("/app/pinnwand") # ── Kommentare rendern (neueste zuerst, echter Index für parent_id) ── komm_html = "" for real_idx, e in reversed(list(enumerate(eintraege))): farbe = avatar_farbe(e["name"]) initial = e["name"][0].upper() n_ant = len(e["antworten"]) # Block: Antworten anzeigen/verbergen antworten_block = "" if n_ant > 0: label = f"{n_ant} Antwort{'en' if n_ant != 1 else ''}" antworten_block = f""" """ komm_html += f"""
{initial}
{esc(e['name'])} {esc(e['zeit'])}
{esc(e['text'])}
{antworten_block}
""" if not eintraege: komm_html = '

Noch keine Kommentare. Sei der Erste! 💬

' n = len(eintraege) anzahl_text = f"{n} Kommentar{'e' if n != 1 else ''}" return seite(PINNWAND_CSS + f"""
Kommentare
👤

{anzahl_text}

{komm_html}
""") # ───────────────────────────────────────────────────────────────────────────── if __name__ == "__main__": app.run(host="0.0.0.0", port=9002) # isa2 → Port 9002