feat(smartlead): Add Smartlead webhook integration [31f88f42]

This commit is contained in:
2026-03-10 07:38:47 +00:00
parent 6f059b9ea5
commit d8eab55aac
3 changed files with 79 additions and 1 deletions

View File

@@ -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

View File

@@ -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())}"

View File

@@ -131,6 +131,20 @@ http {
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;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /connector/ {
auth_basic off;
proxy_pass http://connector-superoffice:8000/;