""" CRM / Ticketing Integration ============================ Supports Zendesk out of the box; stub adapters for Freshdesk, HubSpot, Salesforce, and a generic REST endpoint. Environment variables (set in HF Space secrets or .env): CRM_PROVIDER = zendesk | freshdesk | hubspot | generic ZENDESK_SUBDOMAIN = yourcompany ZENDESK_EMAIL = agent@yourcompany.com ZENDESK_API_TOKEN = xxxxx CRM_GENERIC_URL = https://your-crm.example.com/api/tickets CRM_GENERIC_TOKEN = bearer_token """ import os import uuid import logging from datetime import datetime from typing import Optional logger = logging.getLogger(__name__) class CRMClient: def __init__(self): self.provider = os.getenv("CRM_PROVIDER", "demo") self._demo = self.provider == "demo" # ── Public API ──────────────────────────────────────────────────────────── def create_ticket(self, subject: str, description: str, requester_name: str = "Voice User", requester_phone: str = "", priority: str = "normal", tags: list[str] = None) -> dict: """Create a support ticket and return {ticket_id, url, status}.""" tags = tags or ["hausa-voice-agent", "auto-created"] payload = { "subject": subject, "description": description, "requester_name": requester_name, "requester_phone": requester_phone, "priority": priority, "tags": tags, "created_at": datetime.utcnow().isoformat(), "channel": "voice", } if self._demo: return self._demo_ticket(payload) if self.provider == "zendesk": return self._zendesk_create(payload) return self._generic_create(payload) def get_ticket(self, ticket_id: str) -> Optional[dict]: if self._demo: return {"ticket_id": ticket_id, "status": "open", "created_at": datetime.utcnow().isoformat()} if self.provider == "zendesk": return self._zendesk_get(ticket_id) return None def update_ticket(self, ticket_id: str, comment: str, status: str = "pending") -> dict: if self._demo: logger.info(f"[DEMO] Update ticket {ticket_id}: {comment}") return {"ticket_id": ticket_id, "status": status} if self.provider == "zendesk": return self._zendesk_update(ticket_id, comment, status) return {} def assign_to_human(self, ticket_id: str, agent_group: str = "frontline") -> dict: """Transfer ticket to human agent queue.""" comment = f"[Voice AI] Escalated to human agent. Group: {agent_group}" return self.update_ticket(ticket_id, comment, status="open") # ── Demo stub ───────────────────────────────────────────────────────────── @staticmethod def _demo_ticket(payload: dict) -> dict: ticket_id = "TKT-" + str(uuid.uuid4())[:8].upper() logger.info(f"[DEMO] CRM ticket created: {ticket_id} | {payload['subject']}") return { "ticket_id": ticket_id, "url": f"https://demo.zendesk.com/tickets/{ticket_id}", "status": "created", "provider": "demo", } # ── Zendesk ─────────────────────────────────────────────────────────────── def _zendesk_create(self, payload: dict) -> dict: import requests subdomain = os.environ["ZENDESK_SUBDOMAIN"] email = os.environ["ZENDESK_EMAIL"] token = os.environ["ZENDESK_API_TOKEN"] url = f"https://{subdomain}.zendesk.com/api/v2/tickets.json" body = { "ticket": { "subject": payload["subject"], "comment": {"body": payload["description"]}, "requester": {"name": payload["requester_name"]}, "priority": payload["priority"], "tags": payload["tags"], } } r = requests.post(url, json=body, auth=(f"{email}/token", token), timeout=10) r.raise_for_status() data = r.json()["ticket"] return { "ticket_id": str(data["id"]), "url": data["url"], "status": "created", "provider": "zendesk", } def _zendesk_get(self, ticket_id: str) -> dict: import requests subdomain = os.environ["ZENDESK_SUBDOMAIN"] email = os.environ["ZENDESK_EMAIL"] token = os.environ["ZENDESK_API_TOKEN"] url = f"https://{subdomain}.zendesk.com/api/v2/tickets/{ticket_id}.json" r = requests.get(url, auth=(f"{email}/token", token), timeout=10) r.raise_for_status() data = r.json()["ticket"] return {"ticket_id": ticket_id, "status": data["status"], "subject": data["subject"]} def _zendesk_update(self, ticket_id: str, comment: str, status: str) -> dict: import requests subdomain = os.environ["ZENDESK_SUBDOMAIN"] email = os.environ["ZENDESK_EMAIL"] token = os.environ["ZENDESK_API_TOKEN"] url = f"https://{subdomain}.zendesk.com/api/v2/tickets/{ticket_id}.json" body = {"ticket": {"status": status, "comment": {"body": comment, "public": False}}} r = requests.put(url, json=body, auth=(f"{email}/token", token), timeout=10) r.raise_for_status() return {"ticket_id": ticket_id, "status": status} # ── Generic REST ────────────────────────────────────────────────────────── def _generic_create(self, payload: dict) -> dict: import requests url = os.environ["CRM_GENERIC_URL"] token = os.environ.get("CRM_GENERIC_TOKEN", "") headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} r = requests.post(url, json=payload, headers=headers, timeout=10) r.raise_for_status() data = r.json() return {"ticket_id": str(data.get("id", "?")), "status": "created", "provider": "generic"}