fix: [30388f42] Stoppe Webhook-Loop durch Idempotenz und Truncation
- Führt clean_text_for_so Hilfsfunktion ein, die KI-Texte auf 200 Zeichen kürzt. - Vergleicht und sendet nur noch gekürzte Texte an SuperOffice, um Differenzen durch serverseitige Kürzung zu vermeiden. - Fügt detailliertes Logging für erkannte Änderungen hinzu. - Verhindert so die Endlosschleife bei Benutzern mit ID 528 (Bot-ID).
This commit is contained in:
@@ -33,6 +33,12 @@ def safe_get_udfs(entity_data):
|
||||
logger.error(f"Error reading UDFs: {e}")
|
||||
return {}
|
||||
|
||||
def clean_text_for_so(text, limit=200):
|
||||
"""Clean and truncate text for SuperOffice UDF compatibility."""
|
||||
if not text or text == "null": return ""
|
||||
# Strip whitespace and truncate to safe limit
|
||||
return str(text).strip()[:limit]
|
||||
|
||||
def process_job(job, so_client: SuperOfficeClient, queue: JobQueue):
|
||||
"""
|
||||
Core logic for processing a single job.
|
||||
@@ -46,9 +52,10 @@ def process_job(job, so_client: SuperOfficeClient, queue: JobQueue):
|
||||
# --- CIRCUIT BREAKER: DETECT ECHOES ---
|
||||
# We log if the event was triggered by our own API user (Associate 528)
|
||||
# but we NO LONGER SKIP IT, to allow manual changes by the user who shares the same ID.
|
||||
# The idempotency logic below will prevent infinite loops.
|
||||
changed_by = payload.get("ChangedByAssociateId")
|
||||
if changed_by == 528:
|
||||
logger.info(f"ℹ️ Potential Echo: Event triggered by Associate 528. Proceeding to allow manual user updates.")
|
||||
logger.info(f"ℹ️ Potential Echo: Event triggered by Associate 528. Proceeding to check for meaningful changes.")
|
||||
# --------------------------------------------
|
||||
|
||||
# 0. ID Extraction & Early Exit for irrelevant jobs
|
||||
@@ -287,23 +294,25 @@ def process_job(job, so_client: SuperOfficeClient, queue: JobQueue):
|
||||
contact_patch["OrgNr"] = ce_vat
|
||||
|
||||
# --- C. AI Openers & Summary Sync ---
|
||||
ce_opener = provisioning_data.get("opener")
|
||||
ce_opener_secondary = provisioning_data.get("opener_secondary")
|
||||
ce_summary = provisioning_data.get("summary")
|
||||
# TRUNCATION TO 200 CHARS TO PREVENT INFINITE LOOPS (SO UDF LIMITS)
|
||||
ce_opener = clean_text_for_so(provisioning_data.get("opener"), limit=200)
|
||||
ce_opener_secondary = clean_text_for_so(provisioning_data.get("opener_secondary"), limit=200)
|
||||
ce_summary = clean_text_for_so(provisioning_data.get("summary"), limit=132) # Summary is very short in SO
|
||||
|
||||
if ce_opener and ce_opener != "null" and current_udfs.get(settings.UDF_OPENER) != ce_opener:
|
||||
if ce_opener and current_udfs.get(settings.UDF_OPENER) != ce_opener:
|
||||
logger.info(f"Change detected: Opener Primary (Length: {len(ce_opener)})")
|
||||
if "UserDefinedFields" not in contact_patch: contact_patch["UserDefinedFields"] = {}
|
||||
contact_patch["UserDefinedFields"][settings.UDF_OPENER] = ce_opener
|
||||
if ce_opener_secondary and ce_opener_secondary != "null" and current_udfs.get(settings.UDF_OPENER_SECONDARY) != ce_opener_secondary:
|
||||
|
||||
if ce_opener_secondary and current_udfs.get(settings.UDF_OPENER_SECONDARY) != ce_opener_secondary:
|
||||
logger.info(f"Change detected: Opener Secondary (Length: {len(ce_opener_secondary)})")
|
||||
if "UserDefinedFields" not in contact_patch: contact_patch["UserDefinedFields"] = {}
|
||||
contact_patch["UserDefinedFields"][settings.UDF_OPENER_SECONDARY] = ce_opener_secondary
|
||||
|
||||
if ce_summary and ce_summary != "null":
|
||||
short_summary = (ce_summary[:132] + "...") if len(ce_summary) > 135 else ce_summary
|
||||
if current_udfs.get(settings.UDF_SUMMARY) != short_summary:
|
||||
logger.info("Change detected: AI Summary")
|
||||
if "UserDefinedFields" not in contact_patch: contact_patch["UserDefinedFields"] = {}
|
||||
contact_patch["UserDefinedFields"][settings.UDF_SUMMARY] = short_summary
|
||||
if ce_summary and current_udfs.get(settings.UDF_SUMMARY) != ce_summary:
|
||||
logger.info(f"Change detected: AI Summary (Length: {len(ce_summary)})")
|
||||
if "UserDefinedFields" not in contact_patch: contact_patch["UserDefinedFields"] = {}
|
||||
contact_patch["UserDefinedFields"][settings.UDF_SUMMARY] = ce_summary
|
||||
|
||||
# --- D. Timestamps & Website Sync ---
|
||||
# CRITICAL: We only update the timestamp if we actually have OTHER changes to push.
|
||||
@@ -326,6 +335,8 @@ def process_job(job, so_client: SuperOfficeClient, queue: JobQueue):
|
||||
logger.info(f"Pushing combined PATCH for Contact {contact_id}: {list(contact_patch.keys())}")
|
||||
so_client.patch_contact(contact_id, contact_patch)
|
||||
logger.info("✅ Contact Update Successful.")
|
||||
else:
|
||||
logger.info(f"ℹ️ No changes detected for Contact {contact_id}. Skipping PATCH.")
|
||||
|
||||
# 2d. Sync Person Position
|
||||
role_name = provisioning_data.get("role_name")
|
||||
|
||||
Reference in New Issue
Block a user