Toadoum's picture
Upload 4 files
9c6a172 verified
Raw
History Blame Contribute Delete
6.83 kB
"""
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"}