Spaces:
Sleeping
Sleeping
| """ | |
| 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 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| 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"} | |