diff --git a/lead-engine/README.md b/lead-engine/README.md index fc894713..0a4f2dfd 100644 --- a/lead-engine/README.md +++ b/lead-engine/README.md @@ -76,7 +76,27 @@ docker exec lead-engine python /app/trading_twins/test_calendar_logic.py **Zugriff:** `https://floke-ai.duckdns.org/lead/` (Passwortgeschützt) -## 📝 Credentials (.env) +## 📝 Zukünftige Erweiterungen & Todos + +### Task: Race-Condition-Schutz bei Überbuchung +* **Problem:** Wenn mehrere Leads E-Mails mit denselben Terminvorschlägen erhalten, kann es zu "Race Conditions" kommen, bei denen mehrere Personen denselben Slot fast zeitgleich buchen. +* **Lösung:** Implementierung eines "Live-Checks" im Feedback-Server. + 1. **Trigger:** Ein Nutzer klickt auf einen Buchungslink. + 2. **Aktion:** Bevor der Termin im Kalender erstellt wird, sendet der Server eine *erneute* `getSchedule`-Anfrage an die Graph API für exakt diesen Zeit-Slot. + 3. **Logik:** + * **Slot frei:** Der Termin wird wie geplant gebucht und der Job-Status auf `booked` gesetzt. + * **Slot belegt:** Der Nutzer erhält eine freundliche Nachricht ("Dieser Termin wurde gerade vergeben."). Idealerweise werden ihm dynamisch zwei neue, freie Termine vorgeschlagen, die er direkt auf der Seite buchen kann. +* **Ziel:** Sicherstellen, dass der Kalender die "Single Source of Truth" ist und doppelte Buchungen zuverlässig verhindert werden. + +### Task: Integration der Buchungs-Seiten in WordPress +* **Ziel:** Eine nahtlose User Experience schaffen, bei der Termin-Bestätigungen auf der Haupt-Website (`robo-planet.de`) statt auf der direkten API-URL angezeigt werden. +* **Phase 1 (Kurzfristig): Einbettung via iFrame** + * **Umsetzung:** Eine Seite in WordPress anlegen und die URL des Feedback-Servers (z.B. `https://floke-ai.duckdns.org/feedback/book_slot/...`) in einem iFrame laden. + * **Vorteil:** Kein Programmieraufwand auf unserer Seite nötig, sofort umsetzbar. +* **Phase 2 (Langfristig): Native API-Integration** + * **Umsetzung:** Die Links in der E-Mail führen direkt zu einer WordPress-Seite (z.B. `robo-planet.de/termin-bestaetigen`). Ein Skript auf dieser Seite ruft im Hintergrund unsere `/book_slot` API auf. + * **Vorteil:** Perfekte Integration ins Corporate Design, volle Kontrolle über die Erfolgs- und Fehlermeldungen. Die API ist dafür bereits ausgelegt. + ```env # Info-Postfach (App 1 - Schreiben) diff --git a/lead-engine/trading_twins/RoboPlanetBannerWebinarEinladung.png b/lead-engine/trading_twins/RoboPlanetBannerWebinarEinladung.png new file mode 100644 index 00000000..e69de29b diff --git a/lead-engine/trading_twins/manager.py b/lead-engine/trading_twins/manager.py index f2273dcb..608ec520 100644 --- a/lead-engine/trading_twins/manager.py +++ b/lead-engine/trading_twins/manager.py @@ -28,6 +28,7 @@ DEFAULT_WAIT_MINUTES = 5 SENDER_EMAIL = os.getenv("SENDER_EMAIL", "info@robo-planet.de") TEST_RECEIVER_EMAIL = "floke.com@gmail.com" SIGNATURE_FILE_PATH = os.path.join(os.path.dirname(__file__), "signature.html") +BANNER_FILE_PATH = os.path.join(os.path.dirname(__file__), "RoboPlanetBannerWebinarEinladung.png") # Credentials AZURE_CLIENT_ID = os.getenv("INFO_Application_ID") @@ -156,12 +157,25 @@ def book_slot(job_uuid: str, ts: int): db.close(); return Response("Fehler bei Kalender.", 500) # --- Workflow Logic --- -def send_email(subject, body, to_email, signature): +def send_email(subject, body, to_email, signature, banner_path=None): + attachments = [] + if banner_path and os.path.exists(banner_path): + with open(banner_path, "rb") as f: + content_bytes = f.read() + content_b64 = base64.b64encode(content_bytes).decode("utf-8") + attachments.append({ + "@odata.type": "#microsoft.graph.fileAttachment", + "name": "RoboPlanetBannerWebinarEinladung.png", + "contentBytes": content_b64, + "isInline": True, + "contentId": "banner_image" + }) catchall = os.getenv("EMAIL_CATCHALL"); to_email = catchall if catchall else to_email token = get_access_token(AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID) if not token: return headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} payload = {"message": {"subject": subject, "body": {"contentType": "HTML", "content": body + signature}, "toRecipients": [{"emailAddress": {"address": to_email}}]}, "saveToSentItems": "true"} + if attachments: payload["message"]["attachments"] = attachments requests.post(f"{GRAPH_API_ENDPOINT}/users/{SENDER_EMAIL}/sendMail", headers=headers, json=payload) def process_lead(request_id, company, opener, receiver, name): @@ -186,8 +200,10 @@ def process_lead(request_id, company, opener, receiver, name): for s in suggestions: db.add(ProposedSlot(job_id=job.id, start_time=s, end_time=s+timedelta(minutes=15))) db.commit() - card = {"type": "message", "attachments": [{"contentType": "application/vnd.microsoft.card.adaptive", "content": {"type": "AdaptiveCard", "version": "1.4", "body": [{"type": "TextBlock", "text": f"🤖 E-Mail an {company}?"}], "actions": [{"type": "Action.OpenUrl", "title": "STOP", "url": f"{FEEDBACK_SERVER_BASE_URL}/stop/{request_id}"},{"type": "Action.OpenUrl", "title": "JETZT", "url": f"{FEEDBACK_SERVER_BASE_URL}/send_now/{request_id}"}]}}]} - requests.post(TEAMS_WEBHOOK_URL, json=card) + send_time = datetime.now(TZ_BERLIN) + timedelta(minutes=DEFAULT_WAIT_MINUTES) + # Using the more detailed card from teams_notification.py + from .teams_notification import send_approval_card + send_approval_card(job_uuid=request_id, customer_name=company, time_string=send_time.strftime("%H:%M"), webhook_url=TEAMS_WEBHOOK_URL, api_base_url=FEEDBACK_SERVER_BASE_URL) send_time = datetime.now(TZ_BERLIN) + timedelta(minutes=DEFAULT_WAIT_MINUTES) while datetime.now(TZ_BERLIN) < send_time: @@ -213,7 +229,7 @@ def process_lead(request_id, company, opener, receiver, name): {booking_html} """ - send_email(f"Ihr Kontakt mit RoboPlanet - {company}", email_body, receiver, sig) + send_email(f"Ihr Kontakt mit RoboPlanet - {company}", email_body, receiver, sig, BANNER_FILE_PATH) job.status = "sent"; db.commit(); db.close() if __name__ == "__main__": diff --git a/lead-engine/trading_twins/signature.html b/lead-engine/trading_twins/signature.html index 41a8cb2b..9bc9a3f1 100644 --- a/lead-engine/trading_twins/signature.html +++ b/lead-engine/trading_twins/signature.html @@ -1,40 +1,13 @@ - - -
- - -Freundliche Grüße
-
- Elizabeta Melcer
- Inside Sales Managerin
-
-
- RoboPlanet GmbH
- Schatzbogen 39, 81829 München
- T: +49 89 420490-402 | M: +49 175 8334071
- e.melcer@robo-planet.de | www.robo-planet.de
-
- LinkedIn | Instagram | Newsletteranmeldung -
-
- Sitz der Gesellschaft München | Geschäftsführung: Axel Banoth
- Registergericht AG München, HRB 296113 | USt.-IdNr. DE400464410
- Hinweispflichten zum Datenschutz
-
-
-
