freescout-api 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- freescout/__init__.py +55 -0
- freescout/_transport.py +344 -0
- freescout/_version.py +3 -0
- freescout/client.py +168 -0
- freescout/enums.py +165 -0
- freescout/exceptions.py +100 -0
- freescout/models.py +451 -0
- freescout/resources/__init__.py +19 -0
- freescout/resources/base.py +18 -0
- freescout/resources/conversations.py +304 -0
- freescout/resources/customers.py +235 -0
- freescout/resources/mailboxes.py +74 -0
- freescout/resources/tags.py +40 -0
- freescout/resources/threads.py +84 -0
- freescout/resources/users.py +114 -0
- freescout/resources/webhooks.py +72 -0
- freescout_api-0.1.0.dist-info/METADATA +393 -0
- freescout_api-0.1.0.dist-info/RECORD +21 -0
- freescout_api-0.1.0.dist-info/WHEEL +5 -0
- freescout_api-0.1.0.dist-info/licenses/LICENSE +21 -0
- freescout_api-0.1.0.dist-info/top_level.txt +1 -0
freescout/__init__.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""FreeScout API Python Client Library."""
|
|
2
|
+
|
|
3
|
+
from freescout._version import __version__
|
|
4
|
+
from freescout.client import FreeScoutClient
|
|
5
|
+
from freescout.exceptions import (
|
|
6
|
+
FreeScoutError,
|
|
7
|
+
AuthenticationError,
|
|
8
|
+
NotFoundError,
|
|
9
|
+
ValidationError,
|
|
10
|
+
RateLimitError,
|
|
11
|
+
ServerError,
|
|
12
|
+
)
|
|
13
|
+
from freescout.enums import (
|
|
14
|
+
ConversationType,
|
|
15
|
+
ConversationStatus,
|
|
16
|
+
ConversationState,
|
|
17
|
+
ThreadType,
|
|
18
|
+
ThreadState,
|
|
19
|
+
SortOrder,
|
|
20
|
+
SortField,
|
|
21
|
+
CustomerSortField,
|
|
22
|
+
EmailType,
|
|
23
|
+
PhoneType,
|
|
24
|
+
SocialProfileType,
|
|
25
|
+
PhotoType,
|
|
26
|
+
FolderType,
|
|
27
|
+
SourceType,
|
|
28
|
+
SourceVia,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"__version__",
|
|
33
|
+
"FreeScoutClient",
|
|
34
|
+
"FreeScoutError",
|
|
35
|
+
"AuthenticationError",
|
|
36
|
+
"NotFoundError",
|
|
37
|
+
"ValidationError",
|
|
38
|
+
"RateLimitError",
|
|
39
|
+
"ServerError",
|
|
40
|
+
"ConversationType",
|
|
41
|
+
"ConversationStatus",
|
|
42
|
+
"ConversationState",
|
|
43
|
+
"ThreadType",
|
|
44
|
+
"ThreadState",
|
|
45
|
+
"SortOrder",
|
|
46
|
+
"SortField",
|
|
47
|
+
"CustomerSortField",
|
|
48
|
+
"EmailType",
|
|
49
|
+
"PhoneType",
|
|
50
|
+
"SocialProfileType",
|
|
51
|
+
"PhotoType",
|
|
52
|
+
"FolderType",
|
|
53
|
+
"SourceType",
|
|
54
|
+
"SourceVia",
|
|
55
|
+
]
|
freescout/_transport.py
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"""Internal HTTP transport helpers for FreeScout API client."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from collections.abc import Iterator
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
from requests.adapters import HTTPAdapter
|
|
9
|
+
from urllib3.util.retry import Retry
|
|
10
|
+
|
|
11
|
+
from freescout._version import __version__
|
|
12
|
+
from freescout.exceptions import (
|
|
13
|
+
AuthenticationError,
|
|
14
|
+
ConflictError,
|
|
15
|
+
ForbiddenError,
|
|
16
|
+
FreeScoutError,
|
|
17
|
+
NotFoundError,
|
|
18
|
+
RateLimitError,
|
|
19
|
+
ServerError,
|
|
20
|
+
ValidationError,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
DEFAULT_TIMEOUT = 30
|
|
25
|
+
DEFAULT_MAX_RETRIES = 3
|
|
26
|
+
DEFAULT_BACKOFF_FACTOR = 0.5
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def create_session(
|
|
30
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
31
|
+
backoff_factor: float = DEFAULT_BACKOFF_FACTOR,
|
|
32
|
+
) -> requests.Session:
|
|
33
|
+
"""Create a requests Session with retry configuration.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
max_retries: Maximum number of retries for failed requests.
|
|
37
|
+
backoff_factor: Backoff factor for retries.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Configured requests Session.
|
|
41
|
+
"""
|
|
42
|
+
session = requests.Session()
|
|
43
|
+
|
|
44
|
+
if max_retries > 0:
|
|
45
|
+
retry_strategy = Retry(
|
|
46
|
+
total=max_retries,
|
|
47
|
+
backoff_factor=backoff_factor,
|
|
48
|
+
status_forcelist=[502, 503, 504],
|
|
49
|
+
allowed_methods=["GET", "POST", "PUT", "DELETE"],
|
|
50
|
+
raise_on_status=False,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
adapter = HTTPAdapter(max_retries=retry_strategy)
|
|
54
|
+
session.mount("http://", adapter)
|
|
55
|
+
session.mount("https://", adapter)
|
|
56
|
+
|
|
57
|
+
return session
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def handle_response(response: requests.Response) -> dict[str, Any] | None:
|
|
61
|
+
"""Handle API response and raise appropriate exceptions.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
response: The requests Response object.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Parsed JSON response data, or None for 204 responses.
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
AuthenticationError: For 401 responses.
|
|
71
|
+
ForbiddenError: For 403 responses.
|
|
72
|
+
NotFoundError: For 404 responses.
|
|
73
|
+
ValidationError: For 400 responses.
|
|
74
|
+
ConflictError: For 409 responses.
|
|
75
|
+
RateLimitError: For 429 responses.
|
|
76
|
+
ServerError: For 5xx responses.
|
|
77
|
+
FreeScoutError: For other error responses.
|
|
78
|
+
"""
|
|
79
|
+
status_code = response.status_code
|
|
80
|
+
|
|
81
|
+
if status_code == 204:
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
if status_code in (200, 201):
|
|
85
|
+
if response.headers.get("Resource-ID"):
|
|
86
|
+
return {"id": int(response.headers["Resource-ID"])}
|
|
87
|
+
try:
|
|
88
|
+
return response.json()
|
|
89
|
+
except ValueError:
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
error_data = response.json()
|
|
94
|
+
except ValueError:
|
|
95
|
+
error_data = {"message": response.text or "Unknown error"}
|
|
96
|
+
|
|
97
|
+
message = error_data.get("message", "API error")
|
|
98
|
+
errors = error_data.get("_embedded", {}).get("errors", [])
|
|
99
|
+
|
|
100
|
+
# Common kwargs for all exceptions
|
|
101
|
+
common_kwargs = {
|
|
102
|
+
"message": message,
|
|
103
|
+
"status_code": status_code,
|
|
104
|
+
"response_data": error_data,
|
|
105
|
+
"request_url": response.request.url,
|
|
106
|
+
"request_method": response.request.method,
|
|
107
|
+
"response_text": response.text,
|
|
108
|
+
"response_headers": dict(response.headers),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if status_code == 400:
|
|
112
|
+
raise ValidationError(
|
|
113
|
+
**common_kwargs,
|
|
114
|
+
errors=errors,
|
|
115
|
+
)
|
|
116
|
+
elif status_code == 401:
|
|
117
|
+
raise AuthenticationError(**common_kwargs)
|
|
118
|
+
elif status_code == 403:
|
|
119
|
+
raise ForbiddenError(**common_kwargs)
|
|
120
|
+
elif status_code == 404:
|
|
121
|
+
raise NotFoundError(**common_kwargs)
|
|
122
|
+
elif status_code == 409:
|
|
123
|
+
raise ConflictError(**common_kwargs)
|
|
124
|
+
elif status_code == 429:
|
|
125
|
+
raise RateLimitError(**common_kwargs)
|
|
126
|
+
elif status_code >= 500:
|
|
127
|
+
raise ServerError(**common_kwargs)
|
|
128
|
+
else:
|
|
129
|
+
raise FreeScoutError(**common_kwargs)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class Paginator:
|
|
133
|
+
"""Iterator for paginated API responses.
|
|
134
|
+
|
|
135
|
+
Handles automatic pagination through list endpoints.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def __init__(
|
|
139
|
+
self,
|
|
140
|
+
transport: "Transport",
|
|
141
|
+
endpoint: str,
|
|
142
|
+
params: dict[str, Any] | None = None,
|
|
143
|
+
page_size: int = 50,
|
|
144
|
+
max_pages: int | None = None,
|
|
145
|
+
) -> None:
|
|
146
|
+
"""Initialize Paginator.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
transport: Transport instance for making requests.
|
|
150
|
+
endpoint: API endpoint path.
|
|
151
|
+
params: Query parameters for the request.
|
|
152
|
+
page_size: Number of items per page.
|
|
153
|
+
max_pages: Maximum number of pages to fetch (None for all).
|
|
154
|
+
"""
|
|
155
|
+
self.transport = transport
|
|
156
|
+
self.endpoint = endpoint
|
|
157
|
+
self.params = params or {}
|
|
158
|
+
self.page_size = page_size
|
|
159
|
+
self.max_pages = max_pages
|
|
160
|
+
self.current_page = 0
|
|
161
|
+
self.total_pages: int | None = None
|
|
162
|
+
|
|
163
|
+
def __iter__(self) -> Iterator[dict[str, Any]]:
|
|
164
|
+
"""Iterate through all pages."""
|
|
165
|
+
return self
|
|
166
|
+
|
|
167
|
+
def __next__(self) -> dict[str, Any]:
|
|
168
|
+
"""Get the next page of results."""
|
|
169
|
+
if self.total_pages is not None and self.current_page >= self.total_pages:
|
|
170
|
+
raise StopIteration
|
|
171
|
+
|
|
172
|
+
if self.max_pages is not None and self.current_page >= self.max_pages:
|
|
173
|
+
raise StopIteration
|
|
174
|
+
|
|
175
|
+
self.current_page += 1
|
|
176
|
+
params = {
|
|
177
|
+
**self.params,
|
|
178
|
+
"page": self.current_page,
|
|
179
|
+
"pageSize": self.page_size,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
result = self.transport.get(self.endpoint, params=params)
|
|
183
|
+
if result is None:
|
|
184
|
+
raise StopIteration
|
|
185
|
+
|
|
186
|
+
page_info = result.get("page", {})
|
|
187
|
+
self.total_pages = page_info.get("totalPages", 1)
|
|
188
|
+
|
|
189
|
+
return result
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class Transport:
|
|
193
|
+
"""HTTP transport layer for making API requests."""
|
|
194
|
+
|
|
195
|
+
def __init__(
|
|
196
|
+
self,
|
|
197
|
+
base_url: str,
|
|
198
|
+
api_key: str,
|
|
199
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
200
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
201
|
+
session: requests.Session | None = None,
|
|
202
|
+
) -> None:
|
|
203
|
+
"""Initialize Transport.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
base_url: Base URL for the FreeScout API.
|
|
207
|
+
api_key: API key for authentication.
|
|
208
|
+
timeout: Request timeout in seconds.
|
|
209
|
+
max_retries: Maximum number of retries for failed requests.
|
|
210
|
+
session: Optional pre-configured requests Session.
|
|
211
|
+
"""
|
|
212
|
+
self.base_url = base_url.rstrip("/")
|
|
213
|
+
self.api_key = api_key
|
|
214
|
+
self.timeout = timeout
|
|
215
|
+
self.session = session or create_session(max_retries=max_retries)
|
|
216
|
+
|
|
217
|
+
def _get_headers(self) -> dict[str, str]:
|
|
218
|
+
"""Get headers for API requests."""
|
|
219
|
+
return {
|
|
220
|
+
"X-FreeScout-API-Key": self.api_key,
|
|
221
|
+
"Content-Type": "application/json",
|
|
222
|
+
"Accept": "application/json",
|
|
223
|
+
"User-Agent": f"freescout-api-python/{__version__}",
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
def _build_url(self, endpoint: str) -> str:
|
|
227
|
+
"""Build full URL from endpoint."""
|
|
228
|
+
endpoint = endpoint.lstrip("/")
|
|
229
|
+
return f"{self.base_url}/api/{endpoint}"
|
|
230
|
+
|
|
231
|
+
def get(
|
|
232
|
+
self,
|
|
233
|
+
endpoint: str,
|
|
234
|
+
params: dict[str, Any] | None = None,
|
|
235
|
+
) -> dict[str, Any] | None:
|
|
236
|
+
"""Make a GET request.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
endpoint: API endpoint path.
|
|
240
|
+
params: Query parameters.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
Response data.
|
|
244
|
+
"""
|
|
245
|
+
url = self._build_url(endpoint)
|
|
246
|
+
response = self.session.get(
|
|
247
|
+
url,
|
|
248
|
+
headers=self._get_headers(),
|
|
249
|
+
params=params,
|
|
250
|
+
timeout=self.timeout,
|
|
251
|
+
)
|
|
252
|
+
return handle_response(response)
|
|
253
|
+
|
|
254
|
+
def post(
|
|
255
|
+
self,
|
|
256
|
+
endpoint: str,
|
|
257
|
+
data: dict[str, Any] | None = None,
|
|
258
|
+
) -> dict[str, Any] | None:
|
|
259
|
+
"""Make a POST request.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
endpoint: API endpoint path.
|
|
263
|
+
data: Request body data.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
Response data.
|
|
267
|
+
"""
|
|
268
|
+
url = self._build_url(endpoint)
|
|
269
|
+
response = self.session.post(
|
|
270
|
+
url,
|
|
271
|
+
headers=self._get_headers(),
|
|
272
|
+
json=data,
|
|
273
|
+
timeout=self.timeout,
|
|
274
|
+
)
|
|
275
|
+
return handle_response(response)
|
|
276
|
+
|
|
277
|
+
def put(
|
|
278
|
+
self,
|
|
279
|
+
endpoint: str,
|
|
280
|
+
data: dict[str, Any] | None = None,
|
|
281
|
+
) -> dict[str, Any] | None:
|
|
282
|
+
"""Make a PUT request.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
endpoint: API endpoint path.
|
|
286
|
+
data: Request body data.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Response data.
|
|
290
|
+
"""
|
|
291
|
+
url = self._build_url(endpoint)
|
|
292
|
+
response = self.session.put(
|
|
293
|
+
url,
|
|
294
|
+
headers=self._get_headers(),
|
|
295
|
+
json=data,
|
|
296
|
+
timeout=self.timeout,
|
|
297
|
+
)
|
|
298
|
+
return handle_response(response)
|
|
299
|
+
|
|
300
|
+
def delete(
|
|
301
|
+
self,
|
|
302
|
+
endpoint: str,
|
|
303
|
+
) -> dict[str, Any] | None:
|
|
304
|
+
"""Make a DELETE request.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
endpoint: API endpoint path.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
Response data.
|
|
311
|
+
"""
|
|
312
|
+
url = self._build_url(endpoint)
|
|
313
|
+
response = self.session.delete(
|
|
314
|
+
url,
|
|
315
|
+
headers=self._get_headers(),
|
|
316
|
+
timeout=self.timeout,
|
|
317
|
+
)
|
|
318
|
+
return handle_response(response)
|
|
319
|
+
|
|
320
|
+
def paginate(
|
|
321
|
+
self,
|
|
322
|
+
endpoint: str,
|
|
323
|
+
params: dict[str, Any] | None = None,
|
|
324
|
+
page_size: int = 50,
|
|
325
|
+
max_pages: int | None = None,
|
|
326
|
+
) -> Paginator:
|
|
327
|
+
"""Create a paginator for the given endpoint.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
endpoint: API endpoint path.
|
|
331
|
+
params: Query parameters.
|
|
332
|
+
page_size: Number of items per page.
|
|
333
|
+
max_pages: Maximum number of pages to fetch.
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
Paginator instance.
|
|
337
|
+
"""
|
|
338
|
+
return Paginator(
|
|
339
|
+
transport=self,
|
|
340
|
+
endpoint=endpoint,
|
|
341
|
+
params=params,
|
|
342
|
+
page_size=page_size,
|
|
343
|
+
max_pages=max_pages,
|
|
344
|
+
)
|
freescout/_version.py
ADDED
freescout/client.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""FreeScout API Client."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from freescout._transport import Transport
|
|
9
|
+
from freescout.exceptions import FreeScoutError
|
|
10
|
+
from freescout.resources.conversations import ConversationsResource
|
|
11
|
+
from freescout.resources.customers import CustomersResource
|
|
12
|
+
from freescout.resources.mailboxes import MailboxesResource
|
|
13
|
+
from freescout.resources.tags import TagsResource
|
|
14
|
+
from freescout.resources.threads import ThreadsResource
|
|
15
|
+
from freescout.resources.users import UsersResource
|
|
16
|
+
from freescout.resources.webhooks import WebhooksResource
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FreeScoutClient:
|
|
20
|
+
"""Client for interacting with the FreeScout API.
|
|
21
|
+
|
|
22
|
+
This client provides access to all FreeScout API resources through
|
|
23
|
+
resource-specific attributes.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
```python
|
|
27
|
+
from freescout import FreeScoutClient
|
|
28
|
+
|
|
29
|
+
# Using environment variables
|
|
30
|
+
client = FreeScoutClient()
|
|
31
|
+
|
|
32
|
+
# Or with explicit credentials
|
|
33
|
+
client = FreeScoutClient(
|
|
34
|
+
base_url="https://support.example.com",
|
|
35
|
+
api_key="your-api-key"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# List conversations
|
|
39
|
+
conversations = client.conversations.list(mailbox_id=1)
|
|
40
|
+
for conv in conversations.conversations:
|
|
41
|
+
print(conv.subject)
|
|
42
|
+
|
|
43
|
+
# Get a specific customer
|
|
44
|
+
customer = client.customers.get(123)
|
|
45
|
+
print(customer.first_name)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Attributes:
|
|
49
|
+
conversations: Resource for managing conversations.
|
|
50
|
+
customers: Resource for managing customers.
|
|
51
|
+
mailboxes: Resource for managing mailboxes.
|
|
52
|
+
tags: Resource for managing tags.
|
|
53
|
+
threads: Resource for managing conversation threads.
|
|
54
|
+
users: Resource for managing users.
|
|
55
|
+
webhooks: Resource for managing webhooks.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
base_url: str | None = None,
|
|
61
|
+
api_key: str | None = None,
|
|
62
|
+
timeout: int = 30,
|
|
63
|
+
max_retries: int = 3,
|
|
64
|
+
session: requests.Session | None = None,
|
|
65
|
+
) -> None:
|
|
66
|
+
"""Initialize the FreeScout API client.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
base_url: Base URL for the FreeScout instance. If not provided,
|
|
70
|
+
uses the FREESCOUT_URL environment variable.
|
|
71
|
+
api_key: API key for authentication. If not provided, uses the
|
|
72
|
+
FREESCOUT_API_KEY environment variable.
|
|
73
|
+
timeout: Request timeout in seconds. Defaults to 30.
|
|
74
|
+
max_retries: Maximum number of retries for failed requests.
|
|
75
|
+
Defaults to 3.
|
|
76
|
+
session: Optional pre-configured requests Session.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
FreeScoutError: If base_url or api_key is not provided and
|
|
80
|
+
corresponding environment variable is not set.
|
|
81
|
+
"""
|
|
82
|
+
self._base_url = base_url or os.environ.get("FREESCOUT_URL")
|
|
83
|
+
self._api_key = api_key or os.environ.get("FREESCOUT_API_KEY")
|
|
84
|
+
|
|
85
|
+
if not self._base_url:
|
|
86
|
+
raise FreeScoutError(
|
|
87
|
+
"base_url is required. Provide it directly or set FREESCOUT_URL "
|
|
88
|
+
"environment variable."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if not self._api_key:
|
|
92
|
+
raise FreeScoutError(
|
|
93
|
+
"api_key is required. Provide it directly or set FREESCOUT_API_KEY "
|
|
94
|
+
"environment variable."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
self._transport = Transport(
|
|
98
|
+
base_url=self._base_url,
|
|
99
|
+
api_key=self._api_key,
|
|
100
|
+
timeout=timeout,
|
|
101
|
+
max_retries=max_retries,
|
|
102
|
+
session=session,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
self._conversations: ConversationsResource | None = None
|
|
106
|
+
self._customers: CustomersResource | None = None
|
|
107
|
+
self._mailboxes: MailboxesResource | None = None
|
|
108
|
+
self._tags: TagsResource | None = None
|
|
109
|
+
self._threads: ThreadsResource | None = None
|
|
110
|
+
self._users: UsersResource | None = None
|
|
111
|
+
self._webhooks: WebhooksResource | None = None
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def conversations(self) -> ConversationsResource:
|
|
115
|
+
"""Access the conversations resource."""
|
|
116
|
+
if self._conversations is None:
|
|
117
|
+
self._conversations = ConversationsResource(self._transport)
|
|
118
|
+
return self._conversations
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def customers(self) -> CustomersResource:
|
|
122
|
+
"""Access the customers resource."""
|
|
123
|
+
if self._customers is None:
|
|
124
|
+
self._customers = CustomersResource(self._transport)
|
|
125
|
+
return self._customers
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def mailboxes(self) -> MailboxesResource:
|
|
129
|
+
"""Access the mailboxes resource."""
|
|
130
|
+
if self._mailboxes is None:
|
|
131
|
+
self._mailboxes = MailboxesResource(self._transport)
|
|
132
|
+
return self._mailboxes
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def tags(self) -> TagsResource:
|
|
136
|
+
"""Access the tags resource."""
|
|
137
|
+
if self._tags is None:
|
|
138
|
+
self._tags = TagsResource(self._transport)
|
|
139
|
+
return self._tags
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def threads(self) -> ThreadsResource:
|
|
143
|
+
"""Access the threads resource."""
|
|
144
|
+
if self._threads is None:
|
|
145
|
+
self._threads = ThreadsResource(self._transport)
|
|
146
|
+
return self._threads
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def users(self) -> UsersResource:
|
|
150
|
+
"""Access the users resource."""
|
|
151
|
+
if self._users is None:
|
|
152
|
+
self._users = UsersResource(self._transport)
|
|
153
|
+
return self._users
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def webhooks(self) -> WebhooksResource:
|
|
157
|
+
"""Access the webhooks resource."""
|
|
158
|
+
if self._webhooks is None:
|
|
159
|
+
self._webhooks = WebhooksResource(self._transport)
|
|
160
|
+
return self._webhooks
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def base_url(self) -> str:
|
|
164
|
+
"""Get the base URL for the FreeScout instance."""
|
|
165
|
+
return self._base_url
|
|
166
|
+
|
|
167
|
+
def __repr__(self) -> str:
|
|
168
|
+
return f"FreeScoutClient(base_url='{self._base_url}')"
|