Files
realtime_voice_service/sales_sync_client.py
T
2026-05-09 09:25:29 +05:00

127 lines
3.6 KiB
Python

from __future__ import annotations
import logging
import os
from typing import Any
import httpx
from realtime_voice_service.crm_client import _issue_service_token
LOGGER = logging.getLogger("uvicorn.error")
def _enabled() -> bool:
return os.getenv("SALES_SYNC_ENABLED", "0").strip().lower() in {"1", "true", "yes", "on"}
def _base_url() -> str:
return str(os.getenv("SALES_SERVICE_URL", "http://sales-service:8000")).rstrip("/")
def _timeout_seconds() -> float:
raw = str(os.getenv("SALES_SYNC_TIMEOUT_SECONDS", "8")).strip()
try:
return max(float(raw), 1.0)
except ValueError:
return 8.0
def _secret() -> str:
return str(os.getenv("CRM_APP_TOKEN_SECRET", "")).strip()
def _service_headers() -> dict[str, str] | None:
secret = _secret()
if not secret:
LOGGER.warning("sales sync: CRM_APP_TOKEN_SECRET not configured")
return None
token = _issue_service_token(secret)
return {"Authorization": f"Bearer {token}"}
def _queue_id() -> str | None:
raw = str(
os.getenv("REALTIME_VOICE_QUEUE_ID")
or os.getenv("CRM_QUEUE_ID")
or ""
).strip()
return raw or None
def _queue_code() -> str | None:
raw = str(os.getenv("REALTIME_VOICE_QUEUE_CODE", "")).strip()
return raw or None
async def sync_voice_session(
*,
call_id: str,
interaction_id: str | None = None,
caller_number: str | None = None,
caller_name: str | None = None,
voice_session_id: str | None = None,
ai_session_id: str | None = None,
ai_state: str | None = None,
handoff_reason: str | None = None,
telephony_status: str | None = None,
call_status: str | None = None,
started_at: str | None = None,
ended_at: str | None = None,
summary: str | None = None,
transcript_text: str | None = None,
metadata: dict[str, Any] | None = None,
) -> dict[str, Any] | None:
if not _enabled():
return None
headers = _service_headers()
if headers is None:
return None
payload: dict[str, Any] = {
"call_id": call_id,
"interaction_id": interaction_id,
"queue_id": _queue_id(),
"queue_code": _queue_code(),
"caller_number": caller_number,
"caller_name": caller_name,
"voice_session_id": voice_session_id or call_id,
"ai_session_id": ai_session_id,
"ai_state": ai_state,
"handoff_reason": handoff_reason,
"telephony_status": telephony_status,
"call_status": call_status,
"started_at": started_at,
"ended_at": ended_at,
"summary": summary,
"transcript_text": transcript_text,
"metadata": {
"source_service": "realtime_voice_service",
**(metadata or {}),
},
}
try:
async with httpx.AsyncClient(timeout=_timeout_seconds()) as client:
response = await client.post(
f"{_base_url()}/internal/sales-sync/voice",
json=payload,
headers=headers,
)
response.raise_for_status()
data = response.json()
LOGGER.info(
"sales sync: voice session synced call_id=%s voice_session_id=%s status=%s",
call_id,
voice_session_id or call_id,
call_status,
)
return data if isinstance(data, dict) else None
except Exception:
LOGGER.exception(
"sales sync: failed to sync voice session call_id=%s voice_session_id=%s",
call_id,
voice_session_id or call_id,
)
return None