From 6940d64a0100148e2ae761f95620d87ab0b9acad Mon Sep 17 00:00:00 2001 From: Kevkus Date: Sun, 1 Mar 2026 06:41:26 +0100 Subject: [PATCH] Repo erstellen! --- push-updates.ps1 | 28 ++++++ speiseplan.py | 218 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 push-updates.ps1 create mode 100644 speiseplan.py diff --git a/push-updates.ps1 b/push-updates.ps1 new file mode 100644 index 0000000..f17a41b --- /dev/null +++ b/push-updates.ps1 @@ -0,0 +1,28 @@ +param( + [string]$RepoPath = "C:\Users\Kevkus\Desktop\Obsidian\Allgemeine D&D Notizen\DND Zeug" +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +# Datum/Time für Commit-Message (lokal, ISO-ish) +$stamp = Get-Date -Format "yyyy-MM-dd HH:mm" + +Set-Location $RepoPath + +# Add alles +git add -A | Out-Null + +# Prüfen ob überhaupt Änderungen da sind +$changes = git status --porcelain +if ([string]::IsNullOrWhiteSpace($changes)) { + Write-Host "Keine Änderungen – nichts zu committen/pushen." + exit 0 +} + +# Commit + Push +$msg = "Automatischer Push von $stamp" +git commit -m $msg | Out-Null +git push | Out-Null + +Write-Host "OK: $msg" \ No newline at end of file diff --git a/speiseplan.py b/speiseplan.py new file mode 100644 index 0000000..d2afe89 --- /dev/null +++ b/speiseplan.py @@ -0,0 +1,218 @@ +import os +import json +import requests +import hashlib +import datetime +import random + +# ------------------------- +# KONFIG +# ------------------------- + +WEBHOOK_URL = "https://discord.com/api/webhooks/1440463711645990912/g__qrs1k7gpeHO9xMY0TahIYjVoA0NNTl0mGcn2us80NeTb9hly_lDI41trpuc4oumH3" +BASE_URL = "https://das-schmeckt-mir.ruhr/images/pdf/speise_knapp/{kw:02d}_KW_Speiseplan{year}_Knapp.jpg" + +SAVE_DIR = "/opt/speiseplan-bot/plans" +HASH_FILE = "/opt/speiseplan-bot/last_hashes.json" +DELAY_FILE = "/opt/speiseplan-bot/delay.json" + +MESSAGES = [ + "🍽️ Herr B., ein weiterer Speiseplan für die Woche {found_kw} ist in das Bominhaus eingeschlagen.", + "🥘 Speiseplan für Woche {found_kw}. Hoffentlich ist da kein Döner dabei.", + "🍛 Boah, guck mal hier! Speiseplan für {found_kw}!", + "😋 Der {found_kw}. Speiseplan existiert und wir haben ihn.", + "🍲 Isch hab' gar nichts gemacht! Ich habe nur den {found_kw}. Speiseplan hochgeladen...", + "💀 Wer diese Nachricht über den {found_kw}. Speiseplan liest, der schuldet Kevin einen Döner.", + "💕 Speiseplan gefunden und hochgeladen. Woche {found_kw}.", + "🤓 Wusstet ihr schon, dass das Essen in der KW {found_kw} schlechter ist als dieses Script?" +] + + +# ------------------------- +# LOGGING +# ------------------------- + +def log_info(msg): + print(f"\033[94m[{datetime.datetime.now().strftime('%H:%M:%S')}] {msg}\033[0m") + + +def log_success(msg): + print(f"\033[92m[{datetime.datetime.now().strftime('%H:%M:%S')}] {msg}\033[0m") + + +def log_warn(msg): + print(f"\033[93m[{datetime.datetime.now().strftime('%H:%M:%S')}] {msg}\033[0m") + + +def log_error(msg): + print(f"\033[91m[{datetime.datetime.now().strftime('%H:%M:%S')}] {msg}\033[0m") + + +# ------------------------- +# HILFSFUNKTIONEN +# ------------------------- + +def load_json(path, default): + if not os.path.exists(path): + return default + try: + with open(path, "r") as f: + return json.load(f) + except: + log_warn(f"JSON-Datei beschädigt: {path}. Benutze Default.") + return default + + +def save_json(path, data): + with open(path, "w") as f: + json.dump(data, f, indent=2) + + +def hash_bytes(data): + return hashlib.sha256(data).hexdigest() + + +def cleanup_old_plans(current_kw, current_year): + log_info("Aufräumen alter Pläne beginnt…") + + files = os.listdir(SAVE_DIR) + cutoff = current_kw - 2 + + for file in files: + if not file.startswith("Speiseplan_KW"): + continue + + parts = file.replace("Speiseplan_KW", "").replace(".jpg", "").split("_") + kw_old = int(parts[0]) + year_old = int(parts[1]) + + if year_old < current_year or (year_old == current_year and kw_old <= cutoff): + log_warn(f"Lösche alten Plan: {file}") + os.remove(os.path.join(SAVE_DIR, file)) + + log_success("Aufräumen abgeschlossen.") + + +def fetch_plan(kw, year): + url = BASE_URL.format(kw=kw, year=year) + log_info(f"Prüfe URL: {url}") + + try: + r = requests.get(url) + + # Wenn kein Bild → direkt ignorieren + content_type = r.headers.get("Content-Type", "") + + if r.status_code == 200 and content_type.startswith("image"): + log_success(f"Echter Speiseplan gefunden → KW {kw}") + return r.content + + # DEBUG: ausgeben, was der Server wirklich liefert + log_warn(f"Kein gültiges Bild für KW {kw} (Status={r.status_code}, Type={content_type})") + return None + + except Exception as e: + log_error(f"Fehler beim Abruf von KW {kw}: {e}") + return None + + + +def record_delay(today, found_kw): + delay = load_json(DELAY_FILE, {"active": False, "start": None, "days": 0}) + current_kw = today.isocalendar().week + + if found_kw == current_kw: + if delay["active"]: + log_warn(f"Verzögerung beendet nach {delay['days']} Tagen.") + delay = {"active": False, "start": None, "days": 0} + save_json(DELAY_FILE, delay) + return + + if found_kw < current_kw: + if not delay["active"]: + delay["active"] = True + delay["start"] = today.isoformat() + delay["days"] = 0 + log_warn(f"Verzögerung gestartet! KW {found_kw} statt {current_kw}") + else: + start = datetime.date.fromisoformat(delay["start"]) + delay["days"] = (today - start).days + log_warn(f"Verzögerung läuft: {delay['days']} Tage") + save_json(DELAY_FILE, delay) + + +# ------------------------- +# MAIN +# ------------------------- + +def main(): + os.makedirs(SAVE_DIR, exist_ok=True) + + today = datetime.date.today() + year = today.year + kw_current = today.isocalendar().week + kw_next = kw_current + 1 + year_next = year + 1 if kw_next > 52 else year + if kw_next > 52: + kw_next = 1 + + log_info(f"Starte Speiseplan-Check für KW {kw_current} + KW {kw_next}…") + + hashes = load_json(HASH_FILE, {}) + + found_data = None + found_kw = None + found_year = None + + for kw_try, year_try in [(kw_current, year), (kw_next, year_next)]: + data = fetch_plan(kw_try, year_try) + if data: + key = f"{kw_try}-{year_try}" + new_hash = hash_bytes(data) + + if hashes.get(key) == new_hash: + log_warn(f"Plan KW {kw_try} bereits bekannt – überspringe.") + continue + + found_data = data + found_kw = kw_try + found_year = year_try + hashes[key] = new_hash + break + + if not found_data: + log_warn("Kein neuer Plan gefunden.") + record_delay(today, -1) + return + + record_delay(today, found_kw) + + filename = f"Speiseplan_KW{found_kw}_{found_year}.jpg" + file_path = os.path.join(SAVE_DIR, filename) + + with open(file_path, "wb") as f: + f.write(found_data) + + log_success(f"Plan gespeichert: {file_path}") + + # Nachricht formatieren + message = random.choice(MESSAGES).format(found_kw=found_kw) + + with open(file_path, "rb") as f: + requests.post( + WEBHOOK_URL, + data={"content": message}, + files={"file": (filename, f, "image/jpeg")} + ) + + log_success("Webhook erfolgreich gesendet!") + + save_json(HASH_FILE, hashes) + + cleanup_old_plans(kw_current, year) + + log_success("Speiseplan-Bot abgeschlossen.") + + +if __name__ == "__main__": + main()