feat(smartlead): Add Smartlead webhook integration [31f88f42]
This commit is contained in:
@@ -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.
|
* **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.
|
* **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
|
## 🏗 Architektur
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from zoneinfo import ZoneInfo
|
|||||||
from threading import Thread, Lock
|
from threading import Thread, Lock
|
||||||
import uvicorn
|
import uvicorn
|
||||||
import logging
|
import logging
|
||||||
from fastapi import FastAPI, Response, BackgroundTasks
|
from fastapi import FastAPI, Request, Response, BackgroundTasks
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
@@ -206,6 +206,50 @@ def create_calendar_invite(lead_email, company, start_time):
|
|||||||
# --- FastAPI Server ---
|
# --- FastAPI Server ---
|
||||||
app = FastAPI()
|
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)
|
@app.get("/test_lead", status_code=202)
|
||||||
def trigger_test_lead(background_tasks: BackgroundTasks):
|
def trigger_test_lead(background_tasks: BackgroundTasks):
|
||||||
req_id = f"test_{int(time.time())}"
|
req_id = f"test_{int(time.time())}"
|
||||||
|
|||||||
@@ -131,6 +131,20 @@ http {
|
|||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
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/ {
|
location /connector/ {
|
||||||
auth_basic off;
|
auth_basic off;
|
||||||
proxy_pass http://connector-superoffice:8000/;
|
proxy_pass http://connector-superoffice:8000/;
|
||||||
|
|||||||
Reference in New Issue
Block a user