#!/usr/bin/env python3 """Fehler-Melde-Server fuer brettspiel-news.de Mini-HTTP-Server auf Port 8888. Nur das Formular + API. Isoliert von allen anderen Diensten. Kein Flask, nur stdlib.""" import http.server import json import sqlite3 import os import urllib.parse import urllib.request from datetime import datetime PORT = 8888 HOST = "127.0.0.1" DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fehler.db") TOKEN = os.environ.get("FEHLER_TOKEN", "br3ttsp1el-f3hl3r-2026") TELEGRAM_BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", "") TELEGRAM_CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", "") # ═══════════════════════════════════════════════════ # SQLite # ═══════════════════════════════════════════════════ def init_db(): conn = sqlite3.connect(DB_PATH) conn.execute(""" CREATE TABLE IF NOT EXISTS fehler ( id INTEGER PRIMARY KEY AUTOINCREMENT, article_id TEXT NOT NULL, melder_name TEXT NOT NULL, melder_email TEXT DEFAULT '', fehler_text TEXT NOT NULL, status TEXT DEFAULT 'offen', loesung TEXT DEFAULT '', created_at TEXT DEFAULT (datetime('now','localtime')), updated_at TEXT DEFAULT (datetime('now','localtime')) ) """) conn.commit() conn.close() def save_report(article_id, name, email, text): conn = sqlite3.connect(DB_PATH) conn.execute( "INSERT INTO fehler (article_id, melder_name, melder_email, fehler_text) VALUES (?,?,?,?)", (str(article_id), name, email, text) ) rid = conn.execute("SELECT last_insert_rowid()").fetchone()[0] conn.commit() conn.close() return rid def get_stats(): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row total = conn.execute("SELECT COUNT(*) as c FROM fehler").fetchone()["c"] offen = conn.execute("SELECT COUNT(*) as c FROM fehler WHERE status='offen'").fetchone()["c"] behoben = conn.execute("SELECT COUNT(*) as c FROM fehler WHERE status='behoben'").fetchone()["c"] verworfen = conn.execute("SELECT COUNT(*) as c FROM fehler WHERE status='verworfen'").fetchone()["c"] conn.close() return {"total": total, "offen": offen, "behoben": behoben, "verworfen": verworfen} def get_meldungen(status_filter=None, limit=100): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row if status_filter: rows = conn.execute( "SELECT * FROM fehler WHERE status=? ORDER BY created_at DESC LIMIT ?", (status_filter, limit) ).fetchall() else: rows = conn.execute( "SELECT * FROM fehler ORDER BY created_at DESC LIMIT ?", (limit,) ).fetchall() conn.close() return [dict(r) for r in rows] def update_status(rid, status, loesung): conn = sqlite3.connect(DB_PATH) conn.execute( "UPDATE fehler SET status=?, loesung=?, updated_at=datetime('now','localtime') WHERE id=?", (status, loesung, rid) ) conn.commit() conn.close() # ═══════════════════════════════════════════════════ # Telegram # ═══════════════════════════════════════════════════ def notify_basti(report_id, article_id, name, text): if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHAT_ID: return preview = text[:300] + ("…" if len(text) > 300 else "") msg = ( f"\U0001f41b *Neue Fehlermeldung* \\#{report_id}\n\n" f"\U0001f4f0 *Artikel:* \\#{article_id}\n" f"\U0001f464 *Von:* {name}\n\n" f"\U0001f4dd *Fehler:*\n{preview}" ) try: payload = json.dumps({ "chat_id": TELEGRAM_CHAT_ID, "text": msg, "parse_mode": "Markdown" }).encode() req = urllib.request.Request( f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage", data=payload, headers={"Content-Type": "application/json"} ) urllib.request.urlopen(req, timeout=5) except Exception as e: print(f"[WARN] Telegram failed: {e}") # ═══════════════════════════════════════════════════ # HTML-Formular (inline, self-contained) # ═══════════════════════════════════════════════════ FORM_HTML = r"""