synquic-slide 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.
- synquic_slide/__init__.py +62 -0
- synquic_slide/client.py +146 -0
- synquic_slide/errors.py +28 -0
- synquic_slide/resources/__init__.py +15 -0
- synquic_slide/resources/contacts.py +39 -0
- synquic_slide/resources/email.py +158 -0
- synquic_slide/resources/instagram.py +76 -0
- synquic_slide/resources/shopify.py +70 -0
- synquic_slide/resources/voice.py +103 -0
- synquic_slide/resources/whatsapp.py +132 -0
- synquic_slide/types.py +233 -0
- synquic_slide-0.1.0.dist-info/METADATA +313 -0
- synquic_slide-0.1.0.dist-info/RECORD +14 -0
- synquic_slide-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from .client import AsyncSlideClient, SlideClient
|
|
2
|
+
from .errors import (
|
|
3
|
+
SlideAuthError,
|
|
4
|
+
SlideError,
|
|
5
|
+
SlideNotFoundError,
|
|
6
|
+
SlideScopeError,
|
|
7
|
+
SlideValidationError,
|
|
8
|
+
)
|
|
9
|
+
from .types import (
|
|
10
|
+
Contact,
|
|
11
|
+
DiscountValidation,
|
|
12
|
+
EmailContact,
|
|
13
|
+
EmailTemplate,
|
|
14
|
+
InitiateOutboundCallResponse,
|
|
15
|
+
InstagramConversation,
|
|
16
|
+
InstagramProfile,
|
|
17
|
+
PaginatedResponse,
|
|
18
|
+
PaginationMeta,
|
|
19
|
+
SendEmailResponse,
|
|
20
|
+
SendInstagramMessageResponse,
|
|
21
|
+
SendWhatsAppTemplateResponse,
|
|
22
|
+
ShopifyCollection,
|
|
23
|
+
ShopifyOrder,
|
|
24
|
+
ShopifyProduct,
|
|
25
|
+
VoiceAgent,
|
|
26
|
+
VoiceAnalytics,
|
|
27
|
+
VoiceCall,
|
|
28
|
+
WhatsAppLog,
|
|
29
|
+
WhatsAppTemplate,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"SlideClient",
|
|
34
|
+
"AsyncSlideClient",
|
|
35
|
+
"SlideError",
|
|
36
|
+
"SlideAuthError",
|
|
37
|
+
"SlideScopeError",
|
|
38
|
+
"SlideNotFoundError",
|
|
39
|
+
"SlideValidationError",
|
|
40
|
+
"PaginationMeta",
|
|
41
|
+
"PaginatedResponse",
|
|
42
|
+
"Contact",
|
|
43
|
+
"SendEmailResponse",
|
|
44
|
+
"EmailTemplate",
|
|
45
|
+
"EmailContact",
|
|
46
|
+
"SendWhatsAppTemplateResponse",
|
|
47
|
+
"WhatsAppLog",
|
|
48
|
+
"WhatsAppTemplate",
|
|
49
|
+
"InstagramProfile",
|
|
50
|
+
"InstagramConversation",
|
|
51
|
+
"SendInstagramMessageResponse",
|
|
52
|
+
"ShopifyProduct",
|
|
53
|
+
"ShopifyCollection",
|
|
54
|
+
"ShopifyOrder",
|
|
55
|
+
"DiscountValidation",
|
|
56
|
+
"VoiceAgent",
|
|
57
|
+
"VoiceCall",
|
|
58
|
+
"InitiateOutboundCallResponse",
|
|
59
|
+
"VoiceAnalytics",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
__version__ = "0.1.0"
|
synquic_slide/client.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from .errors import SlideAuthError, SlideError, SlideNotFoundError, SlideScopeError, SlideValidationError
|
|
8
|
+
from .resources import (
|
|
9
|
+
AsyncContactsResource,
|
|
10
|
+
AsyncEmailResource,
|
|
11
|
+
AsyncInstagramResource,
|
|
12
|
+
AsyncShopifyResource,
|
|
13
|
+
AsyncVoiceResource,
|
|
14
|
+
AsyncWhatsAppResource,
|
|
15
|
+
ContactsResource,
|
|
16
|
+
EmailResource,
|
|
17
|
+
InstagramResource,
|
|
18
|
+
ShopifyResource,
|
|
19
|
+
VoiceResource,
|
|
20
|
+
WhatsAppResource,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
_DEFAULT_BASE_URL = "https://slide.synquic.com/api"
|
|
24
|
+
_DEFAULT_TIMEOUT = 30.0
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _clean_params(params: Optional[Dict]) -> Optional[Dict]:
|
|
28
|
+
if params is None:
|
|
29
|
+
return None
|
|
30
|
+
return {k: v for k, v in params.items() if v is not None}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _raise_for_status(response: httpx.Response) -> None:
|
|
34
|
+
if response.is_success:
|
|
35
|
+
return
|
|
36
|
+
try:
|
|
37
|
+
body = response.json()
|
|
38
|
+
except Exception:
|
|
39
|
+
body = {"message": response.text}
|
|
40
|
+
message: str = body.get("message", response.reason_phrase or "Request failed")
|
|
41
|
+
|
|
42
|
+
if response.status_code == 400:
|
|
43
|
+
raise SlideValidationError(message, body)
|
|
44
|
+
if response.status_code == 401:
|
|
45
|
+
raise SlideAuthError(message)
|
|
46
|
+
if response.status_code == 403:
|
|
47
|
+
raise SlideScopeError(message)
|
|
48
|
+
if response.status_code == 404:
|
|
49
|
+
raise SlideNotFoundError(message)
|
|
50
|
+
raise SlideError(response.status_code, message, body)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class SlideClient:
|
|
54
|
+
"""Synchronous Slide API client."""
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
*,
|
|
59
|
+
api_key: str,
|
|
60
|
+
base_url: str = _DEFAULT_BASE_URL,
|
|
61
|
+
timeout: float = _DEFAULT_TIMEOUT,
|
|
62
|
+
) -> None:
|
|
63
|
+
if not api_key:
|
|
64
|
+
raise ValueError("api_key is required")
|
|
65
|
+
self._http = httpx.Client(
|
|
66
|
+
base_url=base_url.rstrip("/") + "/",
|
|
67
|
+
headers={"Authorization": f"Bearer {api_key}"},
|
|
68
|
+
timeout=timeout,
|
|
69
|
+
)
|
|
70
|
+
self.contacts = ContactsResource(self)
|
|
71
|
+
self.email = EmailResource(self)
|
|
72
|
+
self.whatsapp = WhatsAppResource(self)
|
|
73
|
+
self.instagram = InstagramResource(self)
|
|
74
|
+
self.shopify = ShopifyResource(self)
|
|
75
|
+
self.voice = VoiceResource(self)
|
|
76
|
+
|
|
77
|
+
def _request(self, method: str, path: str, **kwargs: Any) -> Any:
|
|
78
|
+
if "params" in kwargs:
|
|
79
|
+
kwargs["params"] = _clean_params(kwargs["params"])
|
|
80
|
+
response = self._http.request(method, path, **kwargs)
|
|
81
|
+
_raise_for_status(response)
|
|
82
|
+
if response.status_code == 204:
|
|
83
|
+
return None
|
|
84
|
+
return response.json()
|
|
85
|
+
|
|
86
|
+
def _request_multipart(self, method: str, path: str, *, files: Dict, data: Dict) -> Any:
|
|
87
|
+
response = self._http.request(method, path, files=files, data=data)
|
|
88
|
+
_raise_for_status(response)
|
|
89
|
+
return response.json()
|
|
90
|
+
|
|
91
|
+
def close(self) -> None:
|
|
92
|
+
self._http.close()
|
|
93
|
+
|
|
94
|
+
def __enter__(self) -> SlideClient:
|
|
95
|
+
return self
|
|
96
|
+
|
|
97
|
+
def __exit__(self, *_: Any) -> None:
|
|
98
|
+
self.close()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class AsyncSlideClient:
|
|
102
|
+
"""Async Slide API client — use with `async with` or `await client.close()`."""
|
|
103
|
+
|
|
104
|
+
def __init__(
|
|
105
|
+
self,
|
|
106
|
+
*,
|
|
107
|
+
api_key: str,
|
|
108
|
+
base_url: str = _DEFAULT_BASE_URL,
|
|
109
|
+
timeout: float = _DEFAULT_TIMEOUT,
|
|
110
|
+
) -> None:
|
|
111
|
+
if not api_key:
|
|
112
|
+
raise ValueError("api_key is required")
|
|
113
|
+
self._http = httpx.AsyncClient(
|
|
114
|
+
base_url=base_url.rstrip("/") + "/",
|
|
115
|
+
headers={"Authorization": f"Bearer {api_key}"},
|
|
116
|
+
timeout=timeout,
|
|
117
|
+
)
|
|
118
|
+
self.contacts = AsyncContactsResource(self)
|
|
119
|
+
self.email = AsyncEmailResource(self)
|
|
120
|
+
self.whatsapp = AsyncWhatsAppResource(self)
|
|
121
|
+
self.instagram = AsyncInstagramResource(self)
|
|
122
|
+
self.shopify = AsyncShopifyResource(self)
|
|
123
|
+
self.voice = AsyncVoiceResource(self)
|
|
124
|
+
|
|
125
|
+
async def _request(self, method: str, path: str, **kwargs: Any) -> Any:
|
|
126
|
+
if "params" in kwargs:
|
|
127
|
+
kwargs["params"] = _clean_params(kwargs["params"])
|
|
128
|
+
response = await self._http.request(method, path, **kwargs)
|
|
129
|
+
_raise_for_status(response)
|
|
130
|
+
if response.status_code == 204:
|
|
131
|
+
return None
|
|
132
|
+
return response.json()
|
|
133
|
+
|
|
134
|
+
async def _request_multipart(self, method: str, path: str, *, files: Dict, data: Dict) -> Any:
|
|
135
|
+
response = await self._http.request(method, path, files=files, data=data)
|
|
136
|
+
_raise_for_status(response)
|
|
137
|
+
return response.json()
|
|
138
|
+
|
|
139
|
+
async def close(self) -> None:
|
|
140
|
+
await self._http.aclose()
|
|
141
|
+
|
|
142
|
+
async def __aenter__(self) -> AsyncSlideClient:
|
|
143
|
+
return self
|
|
144
|
+
|
|
145
|
+
async def __aexit__(self, *_: Any) -> None:
|
|
146
|
+
await self.close()
|
synquic_slide/errors.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SlideError(Exception):
|
|
5
|
+
def __init__(self, status_code: int, message: str, body: Optional[Any] = None) -> None:
|
|
6
|
+
super().__init__(message)
|
|
7
|
+
self.status_code = status_code
|
|
8
|
+
self.body = body
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SlideAuthError(SlideError):
|
|
12
|
+
def __init__(self, message: str = "Invalid or missing API key") -> None:
|
|
13
|
+
super().__init__(401, message)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SlideScopeError(SlideError):
|
|
17
|
+
def __init__(self, message: str) -> None:
|
|
18
|
+
super().__init__(403, message)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SlideNotFoundError(SlideError):
|
|
22
|
+
def __init__(self, message: str) -> None:
|
|
23
|
+
super().__init__(404, message)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SlideValidationError(SlideError):
|
|
27
|
+
def __init__(self, message: str, body: Optional[Any] = None) -> None:
|
|
28
|
+
super().__init__(400, message, body)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .contacts import ContactsResource, AsyncContactsResource
|
|
2
|
+
from .email import EmailResource, AsyncEmailResource
|
|
3
|
+
from .whatsapp import WhatsAppResource, AsyncWhatsAppResource
|
|
4
|
+
from .instagram import InstagramResource, AsyncInstagramResource
|
|
5
|
+
from .shopify import ShopifyResource, AsyncShopifyResource
|
|
6
|
+
from .voice import VoiceResource, AsyncVoiceResource
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"ContactsResource", "AsyncContactsResource",
|
|
10
|
+
"EmailResource", "AsyncEmailResource",
|
|
11
|
+
"WhatsAppResource", "AsyncWhatsAppResource",
|
|
12
|
+
"InstagramResource", "AsyncInstagramResource",
|
|
13
|
+
"ShopifyResource", "AsyncShopifyResource",
|
|
14
|
+
"VoiceResource", "AsyncVoiceResource",
|
|
15
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING, Optional
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from ..client import SlideClient, AsyncSlideClient
|
|
6
|
+
|
|
7
|
+
from ..types import Contact, PaginatedResponse
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ContactsResource:
|
|
11
|
+
def __init__(self, client: SlideClient) -> None:
|
|
12
|
+
self._c = client
|
|
13
|
+
|
|
14
|
+
def list(
|
|
15
|
+
self,
|
|
16
|
+
*,
|
|
17
|
+
page: int = 1,
|
|
18
|
+
limit: int = 50,
|
|
19
|
+
search: Optional[str] = None,
|
|
20
|
+
) -> PaginatedResponse[Contact]:
|
|
21
|
+
return self._c._request("GET", "v1/contacts", params={
|
|
22
|
+
"page": page, "limit": limit, "search": search,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AsyncContactsResource:
|
|
27
|
+
def __init__(self, client: AsyncSlideClient) -> None:
|
|
28
|
+
self._c = client
|
|
29
|
+
|
|
30
|
+
async def list(
|
|
31
|
+
self,
|
|
32
|
+
*,
|
|
33
|
+
page: int = 1,
|
|
34
|
+
limit: int = 50,
|
|
35
|
+
search: Optional[str] = None,
|
|
36
|
+
) -> PaginatedResponse[Contact]:
|
|
37
|
+
return await self._c._request("GET", "v1/contacts", params={
|
|
38
|
+
"page": page, "limit": limit, "search": search,
|
|
39
|
+
})
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from ..client import SlideClient, AsyncSlideClient
|
|
6
|
+
|
|
7
|
+
from ..types import (
|
|
8
|
+
EmailContact,
|
|
9
|
+
EmailTemplate,
|
|
10
|
+
PaginatedResponse,
|
|
11
|
+
SendEmailResponse,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EmailResource:
|
|
16
|
+
def __init__(self, client: SlideClient) -> None:
|
|
17
|
+
self._c = client
|
|
18
|
+
|
|
19
|
+
def send(
|
|
20
|
+
self,
|
|
21
|
+
*,
|
|
22
|
+
recipient: Dict,
|
|
23
|
+
template_id: str,
|
|
24
|
+
from_name: str,
|
|
25
|
+
from_email: str,
|
|
26
|
+
subject: Optional[str] = None,
|
|
27
|
+
reply_to: Optional[str] = None,
|
|
28
|
+
variables: Optional[Dict[str, str]] = None,
|
|
29
|
+
) -> SendEmailResponse:
|
|
30
|
+
body = {
|
|
31
|
+
"recipient": recipient,
|
|
32
|
+
"templateId": template_id,
|
|
33
|
+
"fromName": from_name,
|
|
34
|
+
"fromEmail": from_email,
|
|
35
|
+
}
|
|
36
|
+
if subject is not None:
|
|
37
|
+
body["subject"] = subject
|
|
38
|
+
if reply_to is not None:
|
|
39
|
+
body["replyTo"] = reply_to
|
|
40
|
+
if variables is not None:
|
|
41
|
+
body["variables"] = variables
|
|
42
|
+
return self._c._request("POST", "v1/email/send", json=body)
|
|
43
|
+
|
|
44
|
+
def list_templates(self) -> Dict:
|
|
45
|
+
return self._c._request("GET", "v1/email/templates")
|
|
46
|
+
|
|
47
|
+
def get_template(self, template_id: str) -> EmailTemplate:
|
|
48
|
+
return self._c._request("GET", f"v1/email/templates/{template_id}")
|
|
49
|
+
|
|
50
|
+
def list_contacts(
|
|
51
|
+
self,
|
|
52
|
+
*,
|
|
53
|
+
page: int = 1,
|
|
54
|
+
limit: int = 20,
|
|
55
|
+
search: Optional[str] = None,
|
|
56
|
+
) -> PaginatedResponse[EmailContact]:
|
|
57
|
+
return self._c._request("GET", "v1/email/contacts", params={
|
|
58
|
+
"page": page, "limit": limit, "search": search,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
def upsert_contact(
|
|
62
|
+
self,
|
|
63
|
+
*,
|
|
64
|
+
email: str,
|
|
65
|
+
first_name: Optional[str] = None,
|
|
66
|
+
last_name: Optional[str] = None,
|
|
67
|
+
phone: Optional[str] = None,
|
|
68
|
+
company: Optional[str] = None,
|
|
69
|
+
tags: Optional[List[str]] = None,
|
|
70
|
+
custom_fields: Optional[Dict[str, str]] = None,
|
|
71
|
+
) -> EmailContact:
|
|
72
|
+
body: Dict = {"email": email}
|
|
73
|
+
if first_name is not None:
|
|
74
|
+
body["firstName"] = first_name
|
|
75
|
+
if last_name is not None:
|
|
76
|
+
body["lastName"] = last_name
|
|
77
|
+
if phone is not None:
|
|
78
|
+
body["phone"] = phone
|
|
79
|
+
if company is not None:
|
|
80
|
+
body["company"] = company
|
|
81
|
+
if tags is not None:
|
|
82
|
+
body["tags"] = tags
|
|
83
|
+
if custom_fields is not None:
|
|
84
|
+
body["customFields"] = custom_fields
|
|
85
|
+
return self._c._request("POST", "v1/email/contacts", json=body)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class AsyncEmailResource:
|
|
89
|
+
def __init__(self, client: AsyncSlideClient) -> None:
|
|
90
|
+
self._c = client
|
|
91
|
+
|
|
92
|
+
async def send(
|
|
93
|
+
self,
|
|
94
|
+
*,
|
|
95
|
+
recipient: Dict,
|
|
96
|
+
template_id: str,
|
|
97
|
+
from_name: str,
|
|
98
|
+
from_email: str,
|
|
99
|
+
subject: Optional[str] = None,
|
|
100
|
+
reply_to: Optional[str] = None,
|
|
101
|
+
variables: Optional[Dict[str, str]] = None,
|
|
102
|
+
) -> SendEmailResponse:
|
|
103
|
+
body = {
|
|
104
|
+
"recipient": recipient,
|
|
105
|
+
"templateId": template_id,
|
|
106
|
+
"fromName": from_name,
|
|
107
|
+
"fromEmail": from_email,
|
|
108
|
+
}
|
|
109
|
+
if subject is not None:
|
|
110
|
+
body["subject"] = subject
|
|
111
|
+
if reply_to is not None:
|
|
112
|
+
body["replyTo"] = reply_to
|
|
113
|
+
if variables is not None:
|
|
114
|
+
body["variables"] = variables
|
|
115
|
+
return await self._c._request("POST", "v1/email/send", json=body)
|
|
116
|
+
|
|
117
|
+
async def list_templates(self) -> Dict:
|
|
118
|
+
return await self._c._request("GET", "v1/email/templates")
|
|
119
|
+
|
|
120
|
+
async def get_template(self, template_id: str) -> EmailTemplate:
|
|
121
|
+
return await self._c._request("GET", f"v1/email/templates/{template_id}")
|
|
122
|
+
|
|
123
|
+
async def list_contacts(
|
|
124
|
+
self,
|
|
125
|
+
*,
|
|
126
|
+
page: int = 1,
|
|
127
|
+
limit: int = 20,
|
|
128
|
+
search: Optional[str] = None,
|
|
129
|
+
) -> PaginatedResponse[EmailContact]:
|
|
130
|
+
return await self._c._request("GET", "v1/email/contacts", params={
|
|
131
|
+
"page": page, "limit": limit, "search": search,
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
async def upsert_contact(
|
|
135
|
+
self,
|
|
136
|
+
*,
|
|
137
|
+
email: str,
|
|
138
|
+
first_name: Optional[str] = None,
|
|
139
|
+
last_name: Optional[str] = None,
|
|
140
|
+
phone: Optional[str] = None,
|
|
141
|
+
company: Optional[str] = None,
|
|
142
|
+
tags: Optional[List[str]] = None,
|
|
143
|
+
custom_fields: Optional[Dict[str, str]] = None,
|
|
144
|
+
) -> EmailContact:
|
|
145
|
+
body: Dict = {"email": email}
|
|
146
|
+
if first_name is not None:
|
|
147
|
+
body["firstName"] = first_name
|
|
148
|
+
if last_name is not None:
|
|
149
|
+
body["lastName"] = last_name
|
|
150
|
+
if phone is not None:
|
|
151
|
+
body["phone"] = phone
|
|
152
|
+
if company is not None:
|
|
153
|
+
body["company"] = company
|
|
154
|
+
if tags is not None:
|
|
155
|
+
body["tags"] = tags
|
|
156
|
+
if custom_fields is not None:
|
|
157
|
+
body["customFields"] = custom_fields
|
|
158
|
+
return await self._c._request("POST", "v1/email/contacts", json=body)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING, Dict, Optional
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from ..client import SlideClient, AsyncSlideClient
|
|
6
|
+
|
|
7
|
+
from ..types import (
|
|
8
|
+
InstagramConversation,
|
|
9
|
+
InstagramProfile,
|
|
10
|
+
PaginatedResponse,
|
|
11
|
+
SendInstagramMessageResponse,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class InstagramResource:
|
|
16
|
+
def __init__(self, client: SlideClient) -> None:
|
|
17
|
+
self._c = client
|
|
18
|
+
|
|
19
|
+
def get_profile(self) -> InstagramProfile:
|
|
20
|
+
return self._c._request("GET", "v1/instagram/profile")
|
|
21
|
+
|
|
22
|
+
def list_conversations(
|
|
23
|
+
self, *, page: int = 1, limit: int = 20
|
|
24
|
+
) -> PaginatedResponse[InstagramConversation]:
|
|
25
|
+
return self._c._request("GET", "v1/instagram/conversations", params={"page": page, "limit": limit})
|
|
26
|
+
|
|
27
|
+
def send_message(
|
|
28
|
+
self, *, recipient_igsid: str, message: str
|
|
29
|
+
) -> SendInstagramMessageResponse:
|
|
30
|
+
return self._c._request("POST", "v1/instagram/messages", json={
|
|
31
|
+
"recipientIgsid": recipient_igsid,
|
|
32
|
+
"message": message,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
def get_insights(
|
|
36
|
+
self,
|
|
37
|
+
*,
|
|
38
|
+
period: Optional[str] = None,
|
|
39
|
+
since: Optional[str] = None,
|
|
40
|
+
until: Optional[str] = None,
|
|
41
|
+
) -> Dict:
|
|
42
|
+
return self._c._request("GET", "v1/instagram/insights", params={
|
|
43
|
+
"period": period, "since": since, "until": until,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AsyncInstagramResource:
|
|
48
|
+
def __init__(self, client: AsyncSlideClient) -> None:
|
|
49
|
+
self._c = client
|
|
50
|
+
|
|
51
|
+
async def get_profile(self) -> InstagramProfile:
|
|
52
|
+
return await self._c._request("GET", "v1/instagram/profile")
|
|
53
|
+
|
|
54
|
+
async def list_conversations(
|
|
55
|
+
self, *, page: int = 1, limit: int = 20
|
|
56
|
+
) -> PaginatedResponse[InstagramConversation]:
|
|
57
|
+
return await self._c._request("GET", "v1/instagram/conversations", params={"page": page, "limit": limit})
|
|
58
|
+
|
|
59
|
+
async def send_message(
|
|
60
|
+
self, *, recipient_igsid: str, message: str
|
|
61
|
+
) -> SendInstagramMessageResponse:
|
|
62
|
+
return await self._c._request("POST", "v1/instagram/messages", json={
|
|
63
|
+
"recipientIgsid": recipient_igsid,
|
|
64
|
+
"message": message,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
async def get_insights(
|
|
68
|
+
self,
|
|
69
|
+
*,
|
|
70
|
+
period: Optional[str] = None,
|
|
71
|
+
since: Optional[str] = None,
|
|
72
|
+
until: Optional[str] = None,
|
|
73
|
+
) -> Dict:
|
|
74
|
+
return await self._c._request("GET", "v1/instagram/insights", params={
|
|
75
|
+
"period": period, "since": since, "until": until,
|
|
76
|
+
})
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Union
|
|
3
|
+
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from ..client import SlideClient, AsyncSlideClient
|
|
6
|
+
|
|
7
|
+
from ..types import (
|
|
8
|
+
DiscountValidation,
|
|
9
|
+
ShopifyCollection,
|
|
10
|
+
ShopifyOrder,
|
|
11
|
+
ShopifyProduct,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ShopifyResource:
|
|
16
|
+
def __init__(self, client: SlideClient) -> None:
|
|
17
|
+
self._c = client
|
|
18
|
+
|
|
19
|
+
def search_products(self, *, q: str, limit: int = 10) -> Dict:
|
|
20
|
+
return self._c._request("GET", "v1/shopify/products/search", params={"q": q, "limit": limit})
|
|
21
|
+
|
|
22
|
+
def get_product_types(self) -> Dict:
|
|
23
|
+
return self._c._request("GET", "v1/shopify/products/types")
|
|
24
|
+
|
|
25
|
+
def get_product(self, product_id: str) -> ShopifyProduct:
|
|
26
|
+
return self._c._request("GET", f"v1/shopify/products/{product_id}")
|
|
27
|
+
|
|
28
|
+
def list_collections(self) -> Dict:
|
|
29
|
+
return self._c._request("GET", "v1/shopify/collections")
|
|
30
|
+
|
|
31
|
+
def get_collection_products(self, collection_id: str, *, limit: int = 50) -> Dict:
|
|
32
|
+
return self._c._request("GET", f"v1/shopify/collections/{collection_id}/products", params={"limit": limit})
|
|
33
|
+
|
|
34
|
+
def get_order_status(self, *, identifier: str) -> Union[ShopifyOrder, List[ShopifyOrder]]:
|
|
35
|
+
return self._c._request("GET", "v1/shopify/orders/status", params={"identifier": identifier})
|
|
36
|
+
|
|
37
|
+
def get_customer_orders(self, *, identifier: str) -> Dict:
|
|
38
|
+
return self._c._request("GET", "v1/shopify/customers/orders", params={"identifier": identifier})
|
|
39
|
+
|
|
40
|
+
def validate_discount(self, *, code: str) -> DiscountValidation:
|
|
41
|
+
return self._c._request("GET", "v1/shopify/discounts/validate", params={"code": code})
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AsyncShopifyResource:
|
|
45
|
+
def __init__(self, client: AsyncSlideClient) -> None:
|
|
46
|
+
self._c = client
|
|
47
|
+
|
|
48
|
+
async def search_products(self, *, q: str, limit: int = 10) -> Dict:
|
|
49
|
+
return await self._c._request("GET", "v1/shopify/products/search", params={"q": q, "limit": limit})
|
|
50
|
+
|
|
51
|
+
async def get_product_types(self) -> Dict:
|
|
52
|
+
return await self._c._request("GET", "v1/shopify/products/types")
|
|
53
|
+
|
|
54
|
+
async def get_product(self, product_id: str) -> ShopifyProduct:
|
|
55
|
+
return await self._c._request("GET", f"v1/shopify/products/{product_id}")
|
|
56
|
+
|
|
57
|
+
async def list_collections(self) -> Dict:
|
|
58
|
+
return await self._c._request("GET", "v1/shopify/collections")
|
|
59
|
+
|
|
60
|
+
async def get_collection_products(self, collection_id: str, *, limit: int = 50) -> Dict:
|
|
61
|
+
return await self._c._request("GET", f"v1/shopify/collections/{collection_id}/products", params={"limit": limit})
|
|
62
|
+
|
|
63
|
+
async def get_order_status(self, *, identifier: str) -> Union[ShopifyOrder, List[ShopifyOrder]]:
|
|
64
|
+
return await self._c._request("GET", "v1/shopify/orders/status", params={"identifier": identifier})
|
|
65
|
+
|
|
66
|
+
async def get_customer_orders(self, *, identifier: str) -> Dict:
|
|
67
|
+
return await self._c._request("GET", "v1/shopify/customers/orders", params={"identifier": identifier})
|
|
68
|
+
|
|
69
|
+
async def validate_discount(self, *, code: str) -> DiscountValidation:
|
|
70
|
+
return await self._c._request("GET", "v1/shopify/discounts/validate", params={"code": code})
|