Compare commits
2 Commits
6f059b9ea5
...
d7299df277
| Author | SHA1 | Date | |
|---|---|---|---|
| d7299df277 | |||
| d8eab55aac |
@@ -34,6 +34,26 @@ Der vollautomatische "Zero Touch" Workflow für Trading Twins Anfragen.
|
||||
* **Abstand:** Bietet zwei Termine an, mit ca. **3 Stunden Pause** dazwischen.
|
||||
* **Buchung:** Klick auf Link -> Server erstellt Outlook-Termin von `info@` mit `e.melcer` als Teilnehmer.
|
||||
|
||||
### 6. Smartlead Webhook Integration (NEU)
|
||||
* **Zweck:** Empfang von Echtzeit-Lead-Benachrichtigungen direkt aus der Smartlead-Plattform.
|
||||
* **Event-basiert:** Das System empfängt nur Daten für *neue* Ereignisse (z.B. "Lead als heiß markiert"), keine historischen Daten.
|
||||
|
||||
#### Endpunkte
|
||||
Die folgenden Endpunkte werden für Smartlead bereitgestellt. Die Basis-URL ist flexibel und kann bei einem Serverumzug einfach angepasst werden. Der Platzhalter `{IHRE_AKTUELLE_DOMAIN}` sollte durch die aktuell aktive URL (z.B. `floke-ai.duckdns.org` oder die zukünftige neue Domain) ersetzt werden.
|
||||
|
||||
* **Hot Leads:** `https://{IHRE_AKTUELLE_DOMAIN}/public/smartlead/hot-lead`
|
||||
* **Follow-up Leads:** `https://{IHRE_AKTUELLE_DOMAIN}/public/smartlead/follow-up-lead`
|
||||
|
||||
#### Inbetriebnahme & Test-Prozess
|
||||
Die Integration wird in einem kontrollierten Prozess ausgerollt, um die Datenstruktur sicher zu analysieren:
|
||||
|
||||
1. **URLs an Smartlead übergeben:** Die oben genannten URLs werden an den Smartlead-Support oder in deren Admin-Oberfläche eingetragen.
|
||||
2. **Test-Auslösung anfordern:** Smartlead wird gebeten, für jeden der beiden Endpunkte einen einzelnen Test-Lead manuell auszulösen.
|
||||
3. **Datenanalyse:** Alle eingehenden Anfragen werden ungefiltert in die Log-Datei `lead-engine/Log/smartlead_webhooks.log` geschrieben. Dies ermöglicht eine genaue Analyse der von Smartlead gesendeten JSON-Struktur.
|
||||
4. **Implementierung der Logik:** Basierend auf den analysierten Test-Daten wird die eigentliche Geschäftslogik (z.B. Eintrag in die Datenbank, Teams-Benachrichtigung) implementiert.
|
||||
|
||||
Dieser Prozess stellt sicher, dass wir nicht "blind" entwickeln, sondern auf Basis der realen Datenstruktur von Smartlead aufbauen.
|
||||
|
||||
## 🏗 Architektur
|
||||
|
||||
```text
|
||||
@@ -71,6 +91,68 @@ Der vollautomatische "Zero Touch" Workflow für Trading Twins Anfragen.
|
||||
* **Problem:** Die API-Endpoints gaben nackten Text zurück, was für Kunden unprofessionell wirkte.
|
||||
* **Lösung:** Der Server liefert nun saubere HTML-Snippets zurück. Durch Setzen der `WORDPRESS_BOOKING_URL` in der `.env` führen die Links in der Kunden-E-Mail direkt auf die Kunden-Website. Dort wird das HTML-Ergebnis (Erfolg inkl. Teams-Link ODER Fallback zum Microsoft Bookings Kalender bei Doppelbuchung) unsichtbar in einem iFrame geladen.
|
||||
|
||||
#### 5.1 Troubleshooting der iFrame-Integration
|
||||
|
||||
Um sicherzustellen, dass die Buchungs-Landingpage auf WordPress immer korrekt funktioniert – sowohl mit spezifischen Terminvorschlägen als auch als Fallback zu einer allgemeinen Buchungsseite – sind mehrere Konfigurationsschritte und Fehlerbehebungen notwendig:
|
||||
|
||||
**1. Robuster iFrame-Code für Ihre WordPress-Seite**
|
||||
Der folgende JavaScript-Code sollte in Ihre WordPress-Seite integriert werden, die als Landingpage für Terminbuchungen dient (z.B. `robo-planet.de/terminbestaetigung`). Er prüft, ob spezifische Termindaten (Job-UUID und Timestamp) über die URL übergeben werden, und lädt entsprechend entweder den spezifischen Termin-Slot oder die allgemeine Microsoft Bookings URL als Fallback.
|
||||
|
||||
```html
|
||||
<iframe id="booking-iframe" width="100%" height="800px" style="border:none;" scrolling="no"></iframe>
|
||||
<script>
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const jobUuid = urlParams.get('job_uuid');
|
||||
const timestamp = urlParams.get('ts');
|
||||
|
||||
let iframeSrc = "";
|
||||
|
||||
if (jobUuid && timestamp) {
|
||||
// Wenn beide Parameter vorhanden sind, den spezifischen Buchungslink verwenden
|
||||
iframeSrc = "https://floke-ai.duckdns.org/feedback/book_slot/" + jobUuid + "/" + timestamp;
|
||||
} else {
|
||||
// Andernfalls auf die allgemeine MS Bookings URL verweisen
|
||||
iframeSrc = "https://outlook.office.com/book/KennenlernenmitRoboplanet@wackler-group.de/?ismsaljsauthenabled";
|
||||
}
|
||||
|
||||
document.getElementById('booking-iframe').src = iframeSrc;
|
||||
</script>
|
||||
```
|
||||
|
||||
**2. Nginx-Konfiguration für korrekte Pfad-Weiterleitung**
|
||||
Das Laden der spezifischen `/feedback/book_slot/...`-URL im iFrame scheiterte initial, da der Nginx-Proxy die verschachtelten Pfade nicht korrekt an den `lead-engine`-Container weiterleitete. Dies führte zur "Synology-Fehlerseite".
|
||||
|
||||
* **Problem:** Die `location`-Regel in `nginx-proxy-clean.conf` für `/feedback/` war zu starr.
|
||||
* **Lösung:** Die Konfiguration wurde mit einer `rewrite`-Regel angepasst, um sicherzustellen, dass der gesamte Pfad korrekt an den `lead-engine`-Dienst weitergegeben wird und die notwendigen Proxy-Header für moderne Webanwendungen gesetzt sind.
|
||||
|
||||
```nginx
|
||||
location /feedback/ {
|
||||
auth_basic off;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
rewrite ^/feedback/(.*)$ /$1 break;
|
||||
proxy_pass http://lead-engine:8004;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
```
|
||||
* **Aktion nach Änderung:** Nach dem Anpassen von `nginx-proxy-clean.conf` muss der Nginx-Container neu gestartet werden:
|
||||
`docker-compose restart nginx`
|
||||
|
||||
**3. Behebung des `lead-engine` Service-Absturzes (`SyntaxError`)**
|
||||
Der `lead-engine`-Dienst selbst stürzte aufgrund eines Python `SyntaxError` ab, der durch einen ungültigen Import-Pfad verursacht wurde, der Bindestriche enthielt. Dies verhinderte, dass der Dienst überhaupt eine Antwort senden konnte, was ebenfalls zu einer leeren iFrame-Anzeige beitrug.
|
||||
|
||||
* **Problem:** Eine Zeile `from connector-superoffice.superoffice_client import SuperOfficeClient` in `manager.py` verursachte einen `SyntaxError`, da Bindestriche in Python-Modulnamen nicht erlaubt sind.
|
||||
* **Lösung:** Die fehlerhafte `sys.path.append`-Anweisung und die ungültige `import`-Zeile wurden aus `lead-engine/trading_twins/manager.py` entfernt, da sie Teil eines verschobenen Tasks waren.
|
||||
|
||||
* **Aktion nach Änderung:** Nach der Korrektur in `manager.py` muss der `lead-engine`-Container neu gebaut und neu gestartet werden:
|
||||
`docker-compose up -d --build --force-recreate lead-engine`
|
||||
|
||||
Durch diese Maßnahmen wird die iFrame-Integration nun robust funktionieren und immer eine sinnvolle Ansicht auf der WordPress-Landingpage bieten.
|
||||
|
||||
## 🚀 Inbetriebnahme & Test
|
||||
|
||||
### Inbetriebnahme
|
||||
|
||||
@@ -10,7 +10,7 @@ from zoneinfo import ZoneInfo
|
||||
from threading import Thread, Lock
|
||||
import uvicorn
|
||||
import logging
|
||||
from fastapi import FastAPI, Response, BackgroundTasks
|
||||
from fastapi import FastAPI, Request, Response, BackgroundTasks
|
||||
from fastapi.responses import HTMLResponse
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
@@ -206,6 +206,50 @@ def create_calendar_invite(lead_email, company, start_time):
|
||||
# --- FastAPI Server ---
|
||||
app = FastAPI()
|
||||
|
||||
# --- Webhook Endpoints for Smartlead ---
|
||||
SMARTLEAD_LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "Log")
|
||||
SMARTLEAD_LOG_FILE = os.path.join(SMARTLEAD_LOG_DIR, "smartlead_webhooks.log")
|
||||
|
||||
# Ensure log directory exists
|
||||
os.makedirs(SMARTLEAD_LOG_DIR, exist_ok=True)
|
||||
|
||||
async def log_webhook_data(webhook_type: str, request: Request):
|
||||
"""Helper function to log incoming webhook data."""
|
||||
try:
|
||||
data = await request.json()
|
||||
timestamp = datetime.now(TZ_BERLIN).isoformat()
|
||||
|
||||
log_entry = {
|
||||
"timestamp": timestamp,
|
||||
"webhook_type": webhook_type,
|
||||
"source_ip": request.client.host,
|
||||
"payload": data
|
||||
}
|
||||
|
||||
with open(SMARTLEAD_LOG_FILE, "a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(log_entry) + "\n")
|
||||
|
||||
logging.info(f"Successfully processed and logged '{webhook_type}' webhook.")
|
||||
return {"status": "success", "message": f"{webhook_type} webhook received"}
|
||||
|
||||
except json.JSONDecodeError:
|
||||
logging.error("Webhook received with invalid JSON format.")
|
||||
return Response(content='{"status": "error", "message": "Invalid JSON format"}', status_code=400, media_type="application/json")
|
||||
except Exception as e:
|
||||
logging.error(f"Error processing '{webhook_type}' webhook: {e}")
|
||||
return Response(content='{"status": "error", "message": "Internal server error"}', status_code=500, media_type="application/json")
|
||||
|
||||
@app.post("/webhook/hot-lead", status_code=202)
|
||||
async def webhook_hot_lead(request: Request):
|
||||
"""Webhook endpoint for 'hot leads' from Smartlead."""
|
||||
return await log_webhook_data("hot-lead", request)
|
||||
|
||||
@app.post("/webhook/follow-up-lead", status_code=202)
|
||||
async def webhook_follow_up_lead(request: Request):
|
||||
"""Webhook endpoint for 'follow-up leads' from Smartlead."""
|
||||
return await log_webhook_data("follow-up-lead", request)
|
||||
# --- END Webhook Endpoints ---
|
||||
|
||||
@app.get("/test_lead", status_code=202)
|
||||
def trigger_test_lead(background_tasks: BackgroundTasks):
|
||||
req_id = f"test_{int(time.time())}"
|
||||
|
||||
@@ -121,10 +121,27 @@ http {
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
# Feedback API (public)
|
||||
location /feedback/ {
|
||||
auth_basic off;
|
||||
proxy_pass http://lead-engine:8004/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
rewrite ^/feedback/(.*)$ /$1 break;
|
||||
proxy_pass http://lead-engine:8004;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Smartlead Webhooks (public)
|
||||
location /public/smartlead/ {
|
||||
auth_basic off;
|
||||
# Rewrite the URL to remove the public prefix and pass the rest to the webhook handler
|
||||
# e.g., /public/smartlead/hot-lead -> /webhook/hot-lead
|
||||
rewrite ^/public/smartlead/(.*)$ /webhook/$1 break;
|
||||
|
||||
proxy_pass http://lead-engine:8004;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
Reference in New Issue
Block a user