#!/usr/bin/env python3 """ fredagsbar_ics_generator_v3.py Genererer .ics invitationer baseret på JSON stories. Understøtter nu 'hints' i filnavne og headers. """ import json import argparse import sys from ics import Calendar, Event from datetime import datetime, date, time, timedelta from zoneinfo import ZoneInfo import uuid import os import re # ====== STANDARD INDSTILLINGER ====== START_DATE = date(2026, 1, 29) START_TIME = time(15, 00) DURATION_MINUTES = 60 TIMEZONE = ZoneInfo("Europe/Copenhagen") OUTPUT_BASE_DIR = "fredagsbar_output" # ==================================== def load_story(filepath): """Indlæser JSON fil og validerer strukturen.""" try: with open(filepath, 'r', encoding='utf-8') as f: data = json.load(f) return data except Exception as e: print(f"Fejl ved indlæsning af {filepath}: {e}") sys.exit(1) def next_or_same_friday(d: date) -> date: return d + timedelta(days=(4 - d.weekday()) % 7) def sanitize_filename(s: str) -> str: s = re.sub(r"[^\w\s-]", "", s, flags=re.UNICODE) s = re.sub(r"\s+", "_", s.strip()) return s[:120] def strip_html_tags(text: str) -> str: clean = re.compile('<.*?>') return re.sub(clean, '', text) def inject_outlook_lines(ical_text: str, html_description: str) -> str: """Indsætter Outlook-specifikke linjer og HTML beskrivelse.""" injection_status = "TRANSP:OPAQUE\r\nX-MICROSOFT-CDO-BUSYSTATUS:BUSY\r\n" # Outlook kræver often at HTML er på én linje eller foldet korrekt html_oneline = html_description.replace("\n", "") injection_html = f"X-ALT-DESC;FMTTYPE=text/html:
{html_oneline}\r\n" def replacer(match): vevent = match.group(0) if "X-MICROSOFT-CDO-BUSYSTATUS" not in vevent: vevent = vevent.replace("\r\nEND:VEVENT", "\r\n" + injection_status + "END:VEVENT") # Overskriv eller indsæt X-ALT-DESC if "X-ALT-DESC" in vevent: # Simpel håndtering: Hvis vi allerede har det, ignorer (eller implementer regex replace af X-ALT-DESC) pass else: vevent = vevent.replace("\r\nEND:VEVENT", "\r\n" + injection_html + "END:VEVENT") return vevent text_crlf = ical_text.replace("\n", "\r\n") vevent_pattern = re.compile(r"BEGIN:VEVENT[\s\S]*?END:VEVENT", flags=re.IGNORECASE) return vevent_pattern.sub(replacer, text_crlf) def create_event_ics_file(event_data, meta, log_index, total_logs, start_dt, end_dt, out_path): title = event_data["title"] story_text = event_data["story"] hint = event_data.get("hint", "") # Hent hint hvis det findes cal = Calendar() event = Event() event.uid = str(uuid.uuid4()) # Hvis der er et hint, sæt det evt. ind i titlen eller behold titlen ren corporate event.name = title event.begin = start_dt event.end = end_dt event.status = "CONFIRMED" event.classification = "PUBLIC" # Meta styling font = meta.get("font", "Arial, sans-serif") bg_color = meta.get("bg_color", "#f0f0f0") text_color = meta.get("text_color", "#000000") theme_color = meta.get("theme_color", "#000000") log_prefix = meta.get("log_prefix", "LOG") # Byg Header strengen header_text = f"// {log_prefix} {log_index:02d}/{total_logs} // SUBJECT: {title.upper()}" if hint: header_text += f" // CODE: {hint}" # --- HTML Version (Outlook) --- html_desc = ( f"