实现完整的Freshdesk和Freshchat集成支持,包含会话守卫、错误定义、消息去重、配置管理、webhook处理、出站消息发送、状态监控、安全校验、配对功能和流式回复支持
680 lines
25 KiB
Python
680 lines
25 KiB
Python
from base64 import b64encode
|
|
|
|
import httpx
|
|
|
|
from yuxi.channel.extensions.freshdesk.constants import DEFAULT_TIMEOUT
|
|
from yuxi.channel.extensions.freshdesk.errors import FreshdeskError, FreshdeskErrorCode
|
|
|
|
FRESHCHAT_API_BASE_TEMPLATE = "https://{domain}.freshchat.com/v2"
|
|
FRESHDESK_API_BASE_TEMPLATE = "https://{domain}.freshdesk.com/api/v2"
|
|
|
|
|
|
class FreshdeskClient:
|
|
def __init__(
|
|
self,
|
|
freshdesk_domain: str,
|
|
freshdesk_api_key: str,
|
|
freshchat_domain: str | None = None,
|
|
freshchat_api_key: str | None = None,
|
|
timeout: float = DEFAULT_TIMEOUT,
|
|
):
|
|
self._freshdesk_domain = freshdesk_domain
|
|
self._freshchat_domain = freshchat_domain or freshdesk_domain
|
|
self._freshchat_api_key = freshchat_api_key or freshdesk_api_key
|
|
|
|
freshdesk_auth = b64encode(f"{freshdesk_api_key}:X".encode()).decode()
|
|
self._fd_headers = {
|
|
"Authorization": f"Basic {freshdesk_auth}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
self._fc_headers = {
|
|
"Authorization": f"Bearer {self._freshchat_api_key}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
self._fd_client = httpx.AsyncClient(
|
|
base_url=FRESHDESK_API_BASE_TEMPLATE.format(domain=freshdesk_domain),
|
|
headers=self._fd_headers,
|
|
timeout=httpx.Timeout(timeout),
|
|
)
|
|
self._fc_client = httpx.AsyncClient(
|
|
base_url=FRESHCHAT_API_BASE_TEMPLATE.format(domain=self._freshchat_domain),
|
|
headers=self._fc_headers,
|
|
timeout=httpx.Timeout(timeout),
|
|
)
|
|
|
|
def _with_retry(self, resp):
|
|
if resp.status_code == 429:
|
|
retry_after = None
|
|
try:
|
|
retry_after = float(resp.headers.get("Retry-After", "5"))
|
|
except (TypeError, ValueError):
|
|
retry_after = 5.0
|
|
raise FreshdeskError(FreshdeskErrorCode.RATE_LIMITED, "Rate limited", retry_after=retry_after)
|
|
if resp.status_code == 401:
|
|
raise FreshdeskError(FreshdeskErrorCode.AUTH_ERROR, f"Auth failed: {resp.text}")
|
|
if resp.status_code == 404:
|
|
raise FreshdeskError(FreshdeskErrorCode.NOT_FOUND, f"Not found: {resp.text}")
|
|
resp.raise_for_status()
|
|
|
|
async def fc_get_me(self) -> dict:
|
|
resp = await self._fc_client.get("/accounts/configuration")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_list_conversations(self, **params) -> dict:
|
|
resp = await self._fc_client.get("/conversations", params=params)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_get_conversation(self, conversation_id: str) -> dict:
|
|
resp = await self._fc_client.get(f"/conversations/{conversation_id}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_get_messages(self, conversation_id: str) -> list[dict]:
|
|
resp = await self._fc_client.get(f"/conversations/{conversation_id}/messages")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_send_message(
|
|
self,
|
|
conversation_id: str,
|
|
message_parts: list[dict],
|
|
*,
|
|
message_type: str = "normal",
|
|
actor_type: str = "agent",
|
|
actor_id: str | None = None,
|
|
user_id: str | None = None,
|
|
) -> dict:
|
|
body: dict = {
|
|
"message_parts": message_parts,
|
|
"message_type": message_type,
|
|
}
|
|
if actor_type:
|
|
body["actor_type"] = actor_type
|
|
if actor_id:
|
|
body["actor_id"] = actor_id
|
|
if user_id:
|
|
body["user_id"] = user_id
|
|
|
|
resp = await self._fc_client.post(
|
|
f"/conversations/{conversation_id}/messages",
|
|
json=body,
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_assign_conversation(
|
|
self,
|
|
conversation_id: str,
|
|
*,
|
|
agent_id: str | None = None,
|
|
group_id: str | None = None,
|
|
) -> dict:
|
|
body = {}
|
|
if agent_id:
|
|
body["assigned_agent_id"] = agent_id
|
|
if group_id:
|
|
body["assigned_group_id"] = group_id
|
|
|
|
resp = await self._fc_client.put(
|
|
f"/conversations/{conversation_id}/assign",
|
|
json=body,
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_resolve_conversation(self, conversation_id: str) -> dict:
|
|
resp = await self._fc_client.put(f"/conversations/{conversation_id}/resolve")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_reopen_conversation(self, conversation_id: str) -> dict:
|
|
resp = await self._fc_client.put(f"/conversations/{conversation_id}/reopen")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_get_user(self, user_id: str) -> dict:
|
|
resp = await self._fc_client.get(f"/users/{user_id}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_list_agents(self) -> list[dict]:
|
|
resp = await self._fc_client.get("/agents")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_list_channels(self) -> list[dict]:
|
|
resp = await self._fc_client.get("/channels")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_me(self) -> dict:
|
|
resp = await self._fd_client.get("/agents/me")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_tickets(self, **params) -> list[dict]:
|
|
resp = await self._fd_client.get("/tickets", params=params)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_ticket(self, ticket_id: str) -> dict:
|
|
resp = await self._fd_client.get(f"/tickets/{ticket_id}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_create_ticket(self, data: dict) -> dict:
|
|
resp = await self._fd_client.post("/tickets", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_update_ticket(self, ticket_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.put(f"/tickets/{ticket_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_reply_ticket(self, ticket_id: str, body_html: str, *, private: bool = False) -> dict:
|
|
if private:
|
|
resp = await self._fd_client.post(
|
|
f"/tickets/{ticket_id}/notes",
|
|
json={"body": body_html, "private": True},
|
|
)
|
|
else:
|
|
resp = await self._fd_client.post(
|
|
f"/tickets/{ticket_id}/reply",
|
|
json={"body": body_html},
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_conversations(self, ticket_id: str) -> list[dict]:
|
|
resp = await self._fd_client.get(f"/tickets/{ticket_id}/conversations")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_search_tickets(self, query: str) -> dict:
|
|
resp = await self._fd_client.get("/search/tickets", params={"query": query})
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_archive_ticket(self, ticket_id: str) -> None:
|
|
resp = await self._fd_client.put(f"/tickets/{ticket_id}/archive")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_restore_ticket(self, ticket_id: str) -> None:
|
|
resp = await self._fd_client.put(f"/tickets/{ticket_id}/restore")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_merge_tickets(self, primary_id: str, ticket_ids: list[str]) -> dict:
|
|
resp = await self._fd_client.post(
|
|
f"/tickets/{primary_id}/merge",
|
|
json={"ticket_ids": ticket_ids},
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_forward_ticket(self, ticket_id: str, to_emails: list[str], note: str = "") -> dict:
|
|
resp = await self._fd_client.post(
|
|
f"/tickets/{ticket_id}/forward",
|
|
json={"to_emails": to_emails, "body": note},
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_bulk_update_tickets(self, ticket_ids: list[str], data: dict) -> dict:
|
|
resp = await self._fd_client.post(
|
|
"/tickets/bulk_update",
|
|
json={"ticket_ids": ticket_ids, **data},
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_filter_tickets(self, query: str) -> list[dict]:
|
|
resp = await self._fd_client.get("/filter/tickets", params={"query": query})
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_contacts(self, **params) -> list[dict]:
|
|
resp = await self._fd_client.get("/contacts", params=params)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_contact(self, contact_id: str) -> dict:
|
|
resp = await self._fd_client.get(f"/contacts/{contact_id}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_search_contacts(self, query: str) -> dict:
|
|
resp = await self._fd_client.get("/search/contacts", params={"query": query})
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_create_contact(self, data: dict) -> dict:
|
|
resp = await self._fd_client.post("/contacts", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_update_contact(self, contact_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.put(f"/contacts/{contact_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_companies(self, **params) -> list[dict]:
|
|
resp = await self._fd_client.get("/companies", params=params)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_company(self, company_id: str) -> dict:
|
|
resp = await self._fd_client.get(f"/companies/{company_id}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_create_company(self, data: dict) -> dict:
|
|
resp = await self._fd_client.post("/companies", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_update_company(self, company_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.put(f"/companies/{company_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_search_companies(self, query: str) -> dict:
|
|
resp = await self._fd_client.get("/search/companies", params={"query": query})
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_groups(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/groups")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_group(self, group_id: str) -> dict:
|
|
resp = await self._fd_client.get(f"/groups/{group_id}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_agents(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/agents")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_agent(self, agent_id: str) -> dict:
|
|
resp = await self._fd_client.get(f"/agents/{agent_id}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_update_agent(self, agent_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.put(f"/agents/{agent_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_ticket_fields(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/ticket_fields")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_contact_fields(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/contact_fields")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_sla_policies(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/sla_policies")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_business_hours(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/business_hours")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_products(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/products")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_surveys(self) -> dict:
|
|
resp = await self._fd_client.get("/surveys")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_categories(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/solutions/categories")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_folders(self, category_id: str) -> list[dict]:
|
|
resp = await self._fd_client.get(f"/solutions/categories/{category_id}/folders")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_articles(self, folder_id: str) -> list[dict]:
|
|
resp = await self._fd_client.get(f"/solutions/folders/{folder_id}/articles")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_article(self, article_id: str) -> dict:
|
|
resp = await self._fd_client.get(f"/solutions/articles/{article_id}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_search_articles(self, query: str) -> dict:
|
|
resp = await self._fd_client.get("/search/solutions", params={"term": query})
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_canned_responses(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/canned_responses")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_canned_response(self, response_id: str) -> dict:
|
|
resp = await self._fd_client.get(f"/canned_responses/{response_id}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_canned_response_folders(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/canned_responses/folders")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_time_entries(self, ticket_id: str | None = None) -> list[dict]:
|
|
url = f"/tickets/{ticket_id}/time_entries" if ticket_id else "/time_entries"
|
|
resp = await self._fd_client.get(url)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_email_configs(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/email_configs")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_automations(self, rule_type: str = "ticket") -> list[dict]:
|
|
resp = await self._fd_client.get(f"/automations/{rule_type}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_settings(self) -> dict:
|
|
resp = await self._fd_client.get("/settings")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_list_users(self, **params) -> list[dict]:
|
|
resp = await self._fc_client.get("/users", params=params)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_create_user(self, data: dict) -> dict:
|
|
resp = await self._fc_client.post("/users", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_update_user(self, user_id: str, data: dict) -> dict:
|
|
resp = await self._fc_client.put(f"/users/{user_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_get_user_conversations(self, user_id: str) -> list[dict]:
|
|
resp = await self._fc_client.get(f"/users/{user_id}/conversations")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_list_groups(self) -> list[dict]:
|
|
resp = await self._fc_client.get("/groups")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_get_agent_status(self) -> dict:
|
|
resp = await self._fc_client.get("/agents/status")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_delete_ticket(self, ticket_id: str) -> None:
|
|
resp = await self._fd_client.delete(f"/tickets/{ticket_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_assign_ticket(self, ticket_id: str, *, agent_id: int | None = None, group_id: int | None = None) -> dict:
|
|
body = {}
|
|
if agent_id is not None:
|
|
body["responder_id"] = agent_id
|
|
if group_id is not None:
|
|
body["group_id"] = group_id
|
|
resp = await self._fd_client.put(f"/tickets/{ticket_id}", json=body)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_bulk_delete_tickets(self, ticket_ids: list[str]) -> dict:
|
|
resp = await self._fd_client.post(
|
|
"/tickets/bulk_delete",
|
|
json={"ticket_ids": ticket_ids},
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_reply_to_forward(self, ticket_id: str, body_html: str, from_email_id: str | None = None) -> dict:
|
|
payload: dict = {"body": body_html}
|
|
if from_email_id:
|
|
payload["from_email_id"] = from_email_id
|
|
resp = await self._fd_client.post(
|
|
f"/tickets/{ticket_id}/reply_to_forward",
|
|
json=payload,
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_add_watcher(self, ticket_id: str, watcher_id: int) -> dict:
|
|
resp = await self._fd_client.post(
|
|
f"/tickets/{ticket_id}/watchers",
|
|
json={"user_id": watcher_id},
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_remove_watcher(self, ticket_id: str, watcher_id: int) -> None:
|
|
resp = await self._fd_client.delete(f"/tickets/{ticket_id}/watchers/{watcher_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_associate_tickets(self, primary_id: str, ticket_ids: list[str]) -> dict:
|
|
resp = await self._fd_client.post(
|
|
f"/tickets/{primary_id}/associations",
|
|
json={"association_list": [{"id": tid} for tid in ticket_ids]},
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_get_ticket_summary(self, ticket_id: str) -> dict:
|
|
resp = await self._fd_client.get(f"/tickets/{ticket_id}/summary")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_update_conversation(self, conversation_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.put(f"/conversations/{conversation_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_delete_conversation(self, conversation_id: str) -> None:
|
|
resp = await self._fd_client.delete(f"/conversations/{conversation_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_delete_contact(self, contact_id: str) -> None:
|
|
resp = await self._fd_client.delete(f"/contacts/{contact_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_restore_contact(self, contact_id: str) -> dict:
|
|
resp = await self._fd_client.put(f"/contacts/{contact_id}/restore")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_make_agent(self, contact_id: str) -> dict:
|
|
resp = await self._fd_client.put(f"/contacts/{contact_id}/make_agent")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_merge_contacts(self, primary_id: str, contact_ids: list[str]) -> dict:
|
|
resp = await self._fd_client.post(
|
|
"/contacts/merge",
|
|
json={"primary_contact_id": primary_id, "secondary_contact_ids": contact_ids},
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_delete_company(self, company_id: str) -> None:
|
|
resp = await self._fd_client.delete(f"/companies/{company_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_filter_companies(self, query: str) -> list[dict]:
|
|
resp = await self._fd_client.get("/filter/companies", params={"query": query})
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_list_company_fields(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/company_fields")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_create_group(self, data: dict) -> dict:
|
|
resp = await self._fd_client.post("/groups", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_update_group(self, group_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.put(f"/groups/{group_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_delete_group(self, group_id: str) -> None:
|
|
resp = await self._fd_client.delete(f"/groups/{group_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_create_category(self, data: dict) -> dict:
|
|
resp = await self._fd_client.post("/solutions/categories", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_update_category(self, category_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.put(f"/solutions/categories/{category_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_delete_category(self, category_id: str) -> None:
|
|
resp = await self._fd_client.delete(f"/solutions/categories/{category_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_create_folder(self, category_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.post(
|
|
f"/solutions/categories/{category_id}/folders",
|
|
json=data,
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_update_folder(self, folder_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.put(f"/solutions/folders/{folder_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_delete_folder(self, folder_id: str) -> None:
|
|
resp = await self._fd_client.delete(f"/solutions/folders/{folder_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_create_article(self, folder_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.post(
|
|
f"/solutions/folders/{folder_id}/articles",
|
|
json=data,
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_update_article(self, article_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.put(f"/solutions/articles/{article_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_delete_article(self, article_id: str) -> None:
|
|
resp = await self._fd_client.delete(f"/solutions/articles/{article_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_create_canned_response(self, data: dict) -> dict:
|
|
resp = await self._fd_client.post("/canned_responses", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_update_canned_response(self, response_id: str, data: dict) -> dict:
|
|
resp = await self._fd_client.put(f"/canned_responses/{response_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_delete_canned_response(self, response_id: str) -> None:
|
|
resp = await self._fd_client.delete(f"/canned_responses/{response_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fd_list_roles(self) -> list[dict]:
|
|
resp = await self._fd_client.get("/roles")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_delete_agent(self, agent_id: str) -> None:
|
|
resp = await self._fd_client.delete(f"/agents/{agent_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fc_create_conversation(self, user_id: str, message_parts: list[dict], *, channel_id: str | None = None) -> dict:
|
|
body: dict = {"users": [{"id": user_id}], "message_parts": message_parts}
|
|
if channel_id:
|
|
body["channel_id"] = channel_id
|
|
resp = await self._fc_client.post("/conversations", json=body)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_update_conversation(self, conversation_id: str, data: dict) -> dict:
|
|
resp = await self._fc_client.put(f"/conversations/{conversation_id}", json=data)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_delete_conversation(self, conversation_id: str) -> None:
|
|
resp = await self._fc_client.delete(f"/conversations/{conversation_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fc_delete_user(self, user_id: str) -> None:
|
|
resp = await self._fc_client.delete(f"/users/{user_id}")
|
|
self._with_retry(resp)
|
|
|
|
async def fc_fetch_users(self, user_ids: list[str]) -> dict:
|
|
resp = await self._fc_client.post("/users/fetch", json={"ids": user_ids})
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_list_outbound_messages(self, **params) -> dict:
|
|
resp = await self._fc_client.get("/outbound-messages", params=params)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_get_report(self, report_id: str) -> dict:
|
|
resp = await self._fc_client.get(f"/reports/raw/{report_id}")
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fd_upload_attachment(self, file_content: bytes, file_name: str, content_type: str) -> dict:
|
|
resp = await self._fd_client.post(
|
|
"/attachments",
|
|
files={"file": (file_name, file_content, content_type)},
|
|
headers={**self._fd_headers, "Content-Type": None},
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def fc_upload_file(self, file_content: bytes, file_name: str, content_type: str) -> dict:
|
|
resp = await self._fc_client.post(
|
|
"/attachments",
|
|
files={"file": (file_name, file_content, content_type)},
|
|
headers={**self._fc_headers, "Content-Type": None},
|
|
)
|
|
self._with_retry(resp)
|
|
return resp.json()
|
|
|
|
async def close(self) -> None:
|
|
await self._fd_client.aclose()
|
|
await self._fc_client.aclose()
|