intra_max_chatbot/external/intradesk_api.py

149 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Intradesk REST API: создание заявок и чтение истории/комментариев.
Эндпоинты:
POST /changes/v3/tasks — создать заявку
GET /taskhistory/api/v2.0/lifetime/{id}/full — история (комментарии, смена статуса...)
"""
from __future__ import annotations
import json
import logging
import httpx
import config
logger = logging.getLogger(__name__)
async def get_task_last_comment(task_id: int) -> dict | None:
"""
Получить последний комментарий к заявке из Intradesk.
Возвращает dict: {text, author, date} или None если нет комментариев.
"""
key = config.INTRADESK_TASKS_API_KEY
if not key:
return None
url = (
f"{config.INTRADESK_BASE_URL.rstrip('/')}"
f"/taskhistory/api/v2.0/lifetime/{task_id}/full"
f"?ApiKey={key}&sortDirection=Desc&top=10&events=50"
)
try:
async with httpx.AsyncClient() as client:
resp = await client.get(url, timeout=15.0)
if resp.status_code != 200:
logger.warning(
"get_task_last_comment task=%s → HTTP %s", task_id, resp.status_code
)
return None
data = resp.json()
except Exception as exc:
logger.warning("get_task_last_comment error: %s", exc)
return None
# Ищем последнее событие с blockname="comment" (type=50)
for entry in data.get("data") or []:
for ev in (entry.get("events") or {}).get("data") or []:
if ev.get("blockname") == "comment" and ev.get("type") == 50:
raw_date = entry.get("eventat") or ""
# "2021-03-15T11:54:36.92Z" → "2021-03-15 11:54"
short_date = raw_date[:16].replace("T", " ")
return {
"text": ev.get("stringvalue") or "",
"author": entry.get("username") or "",
"date": short_date,
}
return None
async def create_intradesk_task(
name: str,
description: str,
client_intradesk_id: int | None = None,
contact_person_id: int | None = None,
attachment_urls: list[str] | None = None,
) -> dict | None:
"""
Создать заявку в Intradesk.
Возвращает ответ API (dict с полями id, tasknumber и др.) или None при ошибке.
Вложения — пока добавляются как ссылки в конец описания (до появления upload API).
В dry-run режиме запрос печатается в консоль и возвращается None.
"""
key = config.INTRADESK_CHANGES_API_KEY
if not key:
logger.warning("create_intradesk_task: INTRADESK_CHANGES_API_KEY не задан")
return None
# Вложения — ссылки в конце описания
full_desc = description
if attachment_urls:
full_desc += "\n\nВложения:\n" + "\n".join(attachment_urls)
blocks: dict[str, str] = {
"name": json.dumps({"value": name}, ensure_ascii=False),
"description": json.dumps({"value": full_desc}, ensure_ascii=False),
}
# initiator: вариант 3 из документации — клиент + пользователь клиента.
# clientid заполняется автоматически из initiatorclient, отдельно не нужен.
if client_intradesk_id is not None and contact_person_id is not None:
blocks["initiator"] = json.dumps(
{"value": {"groupid": client_intradesk_id, "userid": contact_person_id}},
ensure_ascii=False,
)
elif client_intradesk_id is not None:
# только клиент (нет конкретного контактного лица)
blocks["initiator"] = json.dumps(
{"value": {"groupid": client_intradesk_id}}, ensure_ascii=False
)
if config.INTRADESK_DEFAULT_SERVICE:
blocks["service"] = json.dumps(
{"value": config.INTRADESK_DEFAULT_SERVICE}, ensure_ascii=False
)
if config.INTRADESK_DEFAULT_TASK_TYPE:
blocks["tasktype"] = json.dumps(
{"value": int(config.INTRADESK_DEFAULT_TASK_TYPE)}, ensure_ascii=False
)
body = {"blocks": blocks, "Channel": "web"}
url = f"{config.INTRADESK_BASE_URL.rstrip('/')}/changes/v3/tasks?ApiKey={key}"
if contact_person_id is not None:
url += f"&userid={contact_person_id}"
if config.DRY_RUN:
print(
"\n[DRY-RUN] create_intradesk_task — запрос НЕ отправлен\n"
f" POST {url}\n"
f" Body: {json.dumps(body, ensure_ascii=False, indent=2)}\n"
)
return None
try:
async with httpx.AsyncClient() as client:
resp = await client.post(
url,
json=body,
headers={"Content-Type": "application/json"},
timeout=20.0,
)
if resp.status_code not in (200, 201):
logger.error(
"create_intradesk_task → HTTP %s: %s",
resp.status_code,
resp.text[:500],
)
return None
result = resp.json()
logger.info(
"create_intradesk_task ok: id=%s number=%s",
result.get("id"),
result.get("tasknumber") or result.get("number"),
)
return result
except Exception as exc:
logger.exception("create_intradesk_task error: %s", exc)
return None