"""Booking confirmation email (#18): sender-address resolution, the customer-facing manage-booking link, and the render/send call. Booking creation must succeed even if this fails -- see mailer.py's fire-and-forget send, which this relies on rather than talking to smtplib directly. """ import os import re from zoneinfo import ZoneInfo from flask import render_template import mailer PUBLIC_BASE_URL = os.environ.get("PUBLIC_BASE_URL", "https://onboard.mivanchenko.de").rstrip("/") # customer_contact is a single free-text "E-Mail oder Telefon" field (#16/#17) # -- not guaranteed to be an email address. Only attempt to send when it # looks like one. _EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$") def looks_like_email(contact): return bool(_EMAIL_RE.match((contact or "").strip())) def manage_url(token): return f"{PUBLIC_BASE_URL}/manage/{token}" # clients.domain is free text from the onboarding form (e.g. "cafe-lichtblick.de", # but nothing stops "https://cafe-lichtblick.de/" being entered) -- strip any # scheme/path/whitespace so a malformed value can't end up in a From header. _DOMAIN_RE = re.compile(r"^(?:[a-z][a-z0-9+.-]*://)?([^/\s]+)", re.IGNORECASE) def _clean_domain(domain): m = _DOMAIN_RE.match((domain or "").strip()) return m.group(1).lower() if m else None def _sender_for(client): domain = _clean_domain((client or {}).get("domain")) return f"noreply@{domain}" if domain else mailer.MAIL_FALLBACK_FROM def send_booking_confirmation(client, booking, token): """No-op if customer_contact doesn't look like an email address -- it's a free-text "E-Mail oder Telefon" field (#16/#17), so this is expected for phone-only customers, not an error.""" if not looks_like_email(booking.get("customer_contact")): print(f"[booking_mail] customer_contact for booking " f"{booking.get('booking_id')} doesn't look like an email, " f"skipping confirmation send", flush=True) return tz = ZoneInfo((client or {}).get("timezone") or "Europe/Berlin") business_name = (client or {}).get("business_name") or "Ihr Termin" html = render_template( "emails/booking_confirmation.html", business_name=business_name, customer_name=booking["customer_name"], service=booking["service"], start_local=booking["start_time"].astimezone(tz), status=booking["status"], manage_url=manage_url(token), ) mailer.send_email( booking["customer_contact"], f"Terminbestätigung – {business_name}", html, from_addr=_sender_for(client), )