← back to Zendesk Chat Analyzer
recent.py
75 lines
#!/usr/bin/env python3
"""Fetch RECENT chats (last N hours) from Zendesk Chat and print JSON to stdout.
Used by the server's /api/recent for the Live feed. Token stays server-side."""
import json, os, re, sys, time, urllib.request, urllib.parse
ENV = os.path.expanduser("~/Projects/secrets-manager/.env")
HOURS = max(1.0, min(168.0, float(os.environ.get("HOURS", "8")))) # clamp 1h..7d (abuse guard)
BASE = "https://www.zopim.com/api/v2/incremental/chats"
def token():
if os.environ.get("ZENDESK_CHAT_ACCESS_TOKEN"):
return os.environ["ZENDESK_CHAT_ACCESS_TOKEN"].strip()
try:
for line in open(ENV):
if line.startswith("ZENDESK_CHAT_ACCESS_TOKEN="):
return line.split("=", 1)[1].strip().strip('"').strip("'")
except Exception:
pass
raise SystemExit("no token")
def clean_title(t):
if not t:
return ""
return re.sub(r"\s+", " ", re.split(r"[–\-|]\s*Designer Wallcoverings", t)[0]).strip()
def main():
tok = token()
start = int(time.time() - HOURS * 3600)
url = BASE + "?" + urllib.parse.urlencode({"fields": "chats(*)", "start_time": start})
rows, pages = [], 0
while url and pages < 20:
req = urllib.request.Request(url, headers={"Authorization": "Bearer " + tok})
try:
d = json.load(urllib.request.urlopen(req, timeout=30))
except Exception as e:
print(json.dumps({"error": str(e), "chats": []})); return
pages += 1
for c in d.get("chats", []):
s = c.get("session") or {}
hist = c.get("history") or []
vmsgs = [h.get("msg", "") for h in hist
if isinstance(h, dict) and str(h.get("sender_type", "")).lower() == "visitor" and h.get("msg")]
if not vmsgs and c.get("comment"):
vmsgs = [c.get("comment")]
rows.append({
"id": c.get("id"), "ts": c.get("timestamp"), "type": c.get("type"),
"country": s.get("country_name") or "Unknown", "country_code": s.get("country_code") or "??",
"city": s.get("city") or "", "region": s.get("region") or "", "platform": s.get("platform") or "",
"agents": c.get("agent_names") or [], "missed": bool(c.get("missed")),
"rating": c.get("rating") or "", "msg": " · ".join(vmsgs)[:200],
"browsed": [clean_title(w.get("title")) for w in (c.get("webpath") or []) if w.get("title")][:1],
})
end = d.get("end_time"); nxt = d.get("next_page")
if not d.get("chats") or not end:
break
url = nxt if nxt else None
# keep only chats that actually STARTED within the window (incremental API also returns recently-updated old chats)
import calendar
cutoff = time.time() - HOURS * 3600
def started(r):
try:
return calendar.timegm(time.strptime(r["ts"], "%Y-%m-%dT%H:%M:%SZ")) >= cutoff
except Exception:
return False
rows = [r for r in rows if started(r)]
rows.sort(key=lambda r: r.get("ts") or "", reverse=True)
print(json.dumps({"now": int(time.time()), "hours": HOURS, "count": len(rows), "chats": rows[:60]}))
if __name__ == "__main__":
main()