customerio 2.0__tar.gz → 2.2__tar.gz
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.
- {customerio-2.0/customerio.egg-info → customerio-2.2}/PKG-INFO +1 -1
- {customerio-2.0 → customerio-2.2}/README.md +50 -15
- {customerio-2.0 → customerio-2.2}/customerio/__init__.py +1 -1
- {customerio-2.0 → customerio-2.2}/customerio/__version__.py +1 -1
- {customerio-2.0 → customerio-2.2}/customerio/api.py +134 -2
- {customerio-2.0 → customerio-2.2/customerio.egg-info}/PKG-INFO +1 -1
- {customerio-2.0 → customerio-2.2}/tests/server.py +9 -12
- {customerio-2.0 → customerio-2.2}/tests/test_api.py +35 -1
- {customerio-2.0 → customerio-2.2}/AUTHORS +0 -0
- {customerio-2.0 → customerio-2.2}/LICENSE +0 -0
- {customerio-2.0 → customerio-2.2}/customerio/client_base.py +0 -0
- {customerio-2.0 → customerio-2.2}/customerio/constants.py +0 -0
- {customerio-2.0 → customerio-2.2}/customerio/regions.py +0 -0
- {customerio-2.0 → customerio-2.2}/customerio/track.py +0 -0
- {customerio-2.0 → customerio-2.2}/customerio.egg-info/SOURCES.txt +0 -0
- {customerio-2.0 → customerio-2.2}/customerio.egg-info/dependency_links.txt +0 -0
- {customerio-2.0 → customerio-2.2}/customerio.egg-info/requires.txt +0 -0
- {customerio-2.0 → customerio-2.2}/customerio.egg-info/top_level.txt +0 -0
- {customerio-2.0 → customerio-2.2}/setup.cfg +0 -0
- {customerio-2.0 → customerio-2.2}/setup.py +0 -0
- {customerio-2.0 → customerio-2.2}/tests/__init__.py +0 -0
- {customerio-2.0 → customerio-2.2}/tests/test_customerio.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
<p align=
|
|
1
|
+
<p align=center>
|
|
2
2
|
<a href="https://customer.io">
|
|
3
|
-
<img src="https://
|
|
3
|
+
<img src="https://avatars.githubusercontent.com/u/1152079?s=200&v=4" height="60">
|
|
4
4
|
</a>
|
|
5
|
-
<p align="center">Power automated communication that people like to receive.</p>
|
|
6
5
|
</p>
|
|
7
6
|
|
|
8
7
|
[](https://gitpod.io/#https://github.com/customerio/customerio-python/)
|
|
@@ -14,7 +13,7 @@
|
|
|
14
13
|
|
|
15
14
|
# Customer.io Python
|
|
16
15
|
|
|
17
|
-
This module has been tested with Python 3.6, 3.7, 3.8 and 3.9.
|
|
16
|
+
This module has been tested with Python 3.6, 3.7, 3.8 and 3.9. If you're new to Customer.io, we recommend that you integrate with our [Data Pipelines Python library](https://github.com/customerio/cdp-analytics-python) instead.
|
|
18
17
|
|
|
19
18
|
## Installing
|
|
20
19
|
|
|
@@ -54,7 +53,7 @@ values for that field.
|
|
|
54
53
|
|
|
55
54
|
You can pass any keyword arguments to the `identify` and `track` methods. These kwargs will be converted to custom attributes.
|
|
56
55
|
|
|
57
|
-
See original REST documentation [here](http://customer.io/docs/api/
|
|
56
|
+
See original REST documentation [here](http://customer.io/docs/api/track/#operation/identify)
|
|
58
57
|
|
|
59
58
|
### Track a custom event
|
|
60
59
|
|
|
@@ -70,7 +69,7 @@ cio.track(customer_id="5", name='purchased', price=23.45, product="widget")
|
|
|
70
69
|
|
|
71
70
|
You can pass any keyword arguments to the `identify` and `track` methods. These kwargs will be converted to custom attributes.
|
|
72
71
|
|
|
73
|
-
See original REST documentation [here](http://customer.io/docs/api/
|
|
72
|
+
See original REST documentation [here](http://customer.io/docs/api/track/#operation/track)
|
|
74
73
|
|
|
75
74
|
### Backfill a custom event
|
|
76
75
|
|
|
@@ -95,7 +94,7 @@ Event timestamp may be passed as a ```datetime.datetime``` object, an integer or
|
|
|
95
94
|
|
|
96
95
|
Keyword arguments to backfill work the same as a call to ```cio.track```.
|
|
97
96
|
|
|
98
|
-
See original REST documentation [here](http://customer.io/docs/api/
|
|
97
|
+
See original REST documentation [here](http://customer.io/docs/api/track/#operation/track)
|
|
99
98
|
|
|
100
99
|
### Track an anonymous event
|
|
101
100
|
|
|
@@ -107,7 +106,7 @@ An anonymous event is an event associated with a person you haven't identified.
|
|
|
107
106
|
|
|
108
107
|
#### Anonymous invite events
|
|
109
108
|
|
|
110
|
-
If you previously sent [invite events](https://customer.io/docs/anonymous-invite-emails/), you can achieve the same functionality by sending an anonymous event with the anonymous identifier set to `None`. To send anonymous invites, your event *must* include a `recipient` attribute.
|
|
109
|
+
If you previously sent [invite events](https://customer.io/docs/journeys/anonymous-invite-emails/), you can achieve the same functionality by sending an anonymous event with the anonymous identifier set to `None`. To send anonymous invites, your event *must* include a `recipient` attribute.
|
|
111
110
|
|
|
112
111
|
```python
|
|
113
112
|
cio.track_anonymous(anonymous_id=None, name="invite", first_name="alex", recipient="alex.person@example.com")
|
|
@@ -122,7 +121,7 @@ Deletes the customer profile for a specified customer.
|
|
|
122
121
|
|
|
123
122
|
This method returns nothing. Attempts to delete non-existent customers will not raise any errors.
|
|
124
123
|
|
|
125
|
-
See original REST documentation [here](
|
|
124
|
+
See original REST documentation [here](https://customer.io/docs/api/track/#operation/delete)
|
|
126
125
|
|
|
127
126
|
|
|
128
127
|
You can pass any keyword arguments to the `identify` and `track` methods. These kwargs will be converted to custom attributes.
|
|
@@ -175,7 +174,7 @@ cio.suppress(customer_id="1")
|
|
|
175
174
|
|
|
176
175
|
Suppresses the specified customer. They will be deleted from Customer.io, and we will ignore all further attempts to identify or track activity for the suppressed customer ID
|
|
177
176
|
|
|
178
|
-
See REST documentation [here](https://
|
|
177
|
+
See REST documentation [here](https://customer.io/docs/api/track/#operation/suppress)
|
|
179
178
|
|
|
180
179
|
### Unsuppress a customer
|
|
181
180
|
```python
|
|
@@ -184,19 +183,22 @@ cio.unsuppress(customer_id="1")
|
|
|
184
183
|
|
|
185
184
|
Unsuppresses the specified customer. We will remove the supplied id from our suppression list and start accepting new identify and track calls for the customer as normal
|
|
186
185
|
|
|
187
|
-
See REST documentation [here](https://
|
|
186
|
+
See REST documentation [here](https://customer.io/docs/api/track/#operation/unsuppress)
|
|
188
187
|
|
|
189
188
|
### Send Transactional Messages
|
|
190
189
|
|
|
191
|
-
To use the [Transactional API](https://customer.io/docs/transactional-api), instantiate the Customer.io object using an [app key](https://customer.io/docs/managing-credentials#app-api-keys) and create a request object
|
|
190
|
+
To use the [Transactional API](https://customer.io/docs/journeys/transactional-api), instantiate the Customer.io object using an [app key](https://customer.io/docs/managing-credentials#app-api-keys) and create a request object for your message type.
|
|
192
191
|
|
|
193
|
-
|
|
192
|
+
## Email
|
|
193
|
+
|
|
194
|
+
SendEmailRequest requires:
|
|
195
|
+
* `transactional_message_id`: the ID of the transactional message you want to send, or the `body`, `_from`, and `subject` of a new message.
|
|
194
196
|
* `to`: the email address of your recipients
|
|
195
197
|
* an `identifiers` object containing the `id` of your recipient. If the `id` does not exist, Customer.io will create it.
|
|
196
198
|
* a `message_data` object containing properties that you want reference in your message using Liquid.
|
|
197
199
|
* You can also send attachments with your message. Use `attach` to encode attachments.
|
|
198
200
|
|
|
199
|
-
Use `send_email` referencing your request to send a transactional message. [Learn more about transactional messages and `SendEmailRequest` properties](https://customer.io/docs/transactional-api).
|
|
201
|
+
Use `send_email` referencing your request to send a transactional message. [Learn more about transactional messages and `SendEmailRequest` properties](https://customer.io/docs/journeys/transactional-api).
|
|
200
202
|
|
|
201
203
|
```python
|
|
202
204
|
from customerio import APIClient, Regions, SendEmailRequest
|
|
@@ -204,6 +206,7 @@ client = APIClient("your API key", region=Regions.US)
|
|
|
204
206
|
|
|
205
207
|
request = SendEmailRequest(
|
|
206
208
|
to="person@example.com",
|
|
209
|
+
_from="override.sender@example.com",
|
|
207
210
|
transactional_message_id="3",
|
|
208
211
|
message_data={
|
|
209
212
|
"name": "person",
|
|
@@ -219,13 +222,45 @@ request = SendEmailRequest(
|
|
|
219
222
|
}
|
|
220
223
|
)
|
|
221
224
|
|
|
222
|
-
with open("
|
|
225
|
+
with open("receipt.pdf", "rb") as f:
|
|
223
226
|
request.attach('receipt.pdf', f.read())
|
|
224
227
|
|
|
225
228
|
response = client.send_email(request)
|
|
226
229
|
print(response)
|
|
227
230
|
```
|
|
228
231
|
|
|
232
|
+
## Push
|
|
233
|
+
|
|
234
|
+
SendPushRequest requires:
|
|
235
|
+
* `transactional_message_id`: the ID of the transactional push message you want to send.
|
|
236
|
+
* an `identifiers` object containing the `id` or `email` of your recipient. If the profile does not exist, Customer.io will create it.
|
|
237
|
+
|
|
238
|
+
Use `send_push` referencing your request to send a transactional message. [Learn more about transactional messages and `SendPushRequest` properties](https://customer.io/docs/journeys/transactional-api).
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from customerio import APIClient, Regions, SendPushRequest
|
|
242
|
+
client = APIClient("your API key", region=Regions.US)
|
|
243
|
+
|
|
244
|
+
request = SendPushRequest(
|
|
245
|
+
transactional_message_id="3",
|
|
246
|
+
message_data={
|
|
247
|
+
"name": "person",
|
|
248
|
+
"items": [
|
|
249
|
+
{
|
|
250
|
+
"name": "shoes",
|
|
251
|
+
"price": "59.99",
|
|
252
|
+
},
|
|
253
|
+
]
|
|
254
|
+
},
|
|
255
|
+
identifiers={
|
|
256
|
+
"id": "2",
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
response = client.send_push(request)
|
|
261
|
+
print(response)
|
|
262
|
+
```
|
|
263
|
+
|
|
229
264
|
## Notes
|
|
230
265
|
- The Customer.io Python SDK depends on the [`Requests`](https://pypi.org/project/requests/) library which includes [`urllib3`](https://pypi.org/project/urllib3/) as a transitive dependency. The [`Requests`](https://pypi.org/project/requests/) library leverages connection pooling defined in [`urllib3`](https://pypi.org/project/urllib3/). [`urllib3`](https://pypi.org/project/urllib3/) only attempts to retry invocations of `HTTP` methods which are understood to be idempotent (See: [`Retry.DEFAULT_ALLOWED_METHODS`](https://github.com/urllib3/urllib3/blob/main/src/urllib3/util/retry.py#L184)). Since the `POST` method is not considered to be idempotent, any invocations which require `POST` are not retried.
|
|
231
266
|
|
|
@@ -2,5 +2,5 @@ import warnings
|
|
|
2
2
|
|
|
3
3
|
from customerio.client_base import CustomerIOException
|
|
4
4
|
from customerio.track import CustomerIO
|
|
5
|
-
from customerio.api import APIClient, SendEmailRequest
|
|
5
|
+
from customerio.api import APIClient, SendEmailRequest, SendPushRequest, SendSMSRequest
|
|
6
6
|
from customerio.regions import Regions
|
|
@@ -22,6 +22,18 @@ class APIClient(ClientBase):
|
|
|
22
22
|
resp = self.send_request('POST', self.url + "/v1/send/email", request)
|
|
23
23
|
return json.loads(resp)
|
|
24
24
|
|
|
25
|
+
def send_push(self, request):
|
|
26
|
+
if isinstance(request, SendPushRequest):
|
|
27
|
+
request = request._to_dict()
|
|
28
|
+
resp = self.send_request('POST', self.url + "/v1/send/push", request)
|
|
29
|
+
return json.loads(resp)
|
|
30
|
+
|
|
31
|
+
def send_sms(self, request):
|
|
32
|
+
if isinstance(request, SendSMSRequest):
|
|
33
|
+
request = request._to_dict()
|
|
34
|
+
resp = self.send_request('POST', self.url + "/v1/send/sms", request)
|
|
35
|
+
return json.loads(resp)
|
|
36
|
+
|
|
25
37
|
# builds the session.
|
|
26
38
|
def _build_session(self):
|
|
27
39
|
session = super()._build_session()
|
|
@@ -30,7 +42,7 @@ class APIClient(ClientBase):
|
|
|
30
42
|
return session
|
|
31
43
|
|
|
32
44
|
class SendEmailRequest(object):
|
|
33
|
-
'''An object with all the options avaiable for triggering a transactional message'''
|
|
45
|
+
'''An object with all the options avaiable for triggering a transactional email message'''
|
|
34
46
|
def __init__(self,
|
|
35
47
|
transactional_message_id=None,
|
|
36
48
|
to=None,
|
|
@@ -96,7 +108,7 @@ class SendEmailRequest(object):
|
|
|
96
108
|
self.attachments[name] = content
|
|
97
109
|
|
|
98
110
|
def _to_dict(self):
|
|
99
|
-
'''Build a request payload
|
|
111
|
+
'''Build a request payload from the object'''
|
|
100
112
|
field_map = dict(
|
|
101
113
|
# from is reservered keyword hence the object has the field
|
|
102
114
|
# `_from` but in the request payload we map it to `from`
|
|
@@ -132,3 +144,123 @@ class SendEmailRequest(object):
|
|
|
132
144
|
data[name] = value
|
|
133
145
|
|
|
134
146
|
return data
|
|
147
|
+
|
|
148
|
+
class SendPushRequest(object):
|
|
149
|
+
'''An object with all the options avaiable for triggering a transactional push message'''
|
|
150
|
+
def __init__(self,
|
|
151
|
+
transactional_message_id=None,
|
|
152
|
+
to=None,
|
|
153
|
+
identifiers=None,
|
|
154
|
+
title=None,
|
|
155
|
+
message=None,
|
|
156
|
+
disable_message_retention=None,
|
|
157
|
+
send_to_unsubscribed=None,
|
|
158
|
+
queue_draft=None,
|
|
159
|
+
message_data=None,
|
|
160
|
+
send_at=None,
|
|
161
|
+
language=None,
|
|
162
|
+
image_url=None,
|
|
163
|
+
link=None,
|
|
164
|
+
custom_data=None,
|
|
165
|
+
custom_payload=None,
|
|
166
|
+
device=None,
|
|
167
|
+
sound=None
|
|
168
|
+
):
|
|
169
|
+
|
|
170
|
+
self.transactional_message_id = transactional_message_id
|
|
171
|
+
self.to = to
|
|
172
|
+
self.identifiers = identifiers
|
|
173
|
+
self.disable_message_retention = disable_message_retention
|
|
174
|
+
self.send_to_unsubscribed = send_to_unsubscribed
|
|
175
|
+
self.queue_draft = queue_draft
|
|
176
|
+
self.message_data = message_data
|
|
177
|
+
self.send_at = send_at
|
|
178
|
+
self.language = language
|
|
179
|
+
|
|
180
|
+
self.title = title
|
|
181
|
+
self.message = message
|
|
182
|
+
self.image_url = image_url
|
|
183
|
+
self.link = link
|
|
184
|
+
self.custom_data = custom_data
|
|
185
|
+
self.custom_payload = custom_payload
|
|
186
|
+
self.device = device
|
|
187
|
+
self.sound = sound
|
|
188
|
+
|
|
189
|
+
def _to_dict(self):
|
|
190
|
+
'''Build a request payload from the object'''
|
|
191
|
+
field_map = dict(
|
|
192
|
+
# field name is the same as the payload field name
|
|
193
|
+
transactional_message_id="transactional_message_id",
|
|
194
|
+
to="to",
|
|
195
|
+
identifiers="identifiers",
|
|
196
|
+
disable_message_retention="disable_message_retention",
|
|
197
|
+
send_to_unsubscribed="send_to_unsubscribed",
|
|
198
|
+
queue_draft="queue_draft",
|
|
199
|
+
message_data="message_data",
|
|
200
|
+
send_at="send_at",
|
|
201
|
+
language="language",
|
|
202
|
+
|
|
203
|
+
title="title",
|
|
204
|
+
message="message",
|
|
205
|
+
image_url="image_url",
|
|
206
|
+
link="link",
|
|
207
|
+
custom_data="custom_data",
|
|
208
|
+
custom_payload="custom_payload",
|
|
209
|
+
device="custom_device",
|
|
210
|
+
sound="sound"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
data = {}
|
|
214
|
+
for field, name in field_map.items():
|
|
215
|
+
value = getattr(self, field, None)
|
|
216
|
+
if value is not None:
|
|
217
|
+
data[name] = value
|
|
218
|
+
|
|
219
|
+
return data
|
|
220
|
+
|
|
221
|
+
class SendSMSRequest(object):
|
|
222
|
+
'''An object with all the options avaiable for triggering a transactional push message'''
|
|
223
|
+
def __init__(self,
|
|
224
|
+
transactional_message_id=None,
|
|
225
|
+
to=None,
|
|
226
|
+
identifiers=None,
|
|
227
|
+
disable_message_retention=None,
|
|
228
|
+
send_to_unsubscribed=None,
|
|
229
|
+
queue_draft=None,
|
|
230
|
+
message_data=None,
|
|
231
|
+
send_at=None,
|
|
232
|
+
language=None,
|
|
233
|
+
):
|
|
234
|
+
|
|
235
|
+
self.transactional_message_id = transactional_message_id
|
|
236
|
+
self.to = to
|
|
237
|
+
self.identifiers = identifiers
|
|
238
|
+
self.disable_message_retention = disable_message_retention
|
|
239
|
+
self.send_to_unsubscribed = send_to_unsubscribed
|
|
240
|
+
self.queue_draft = queue_draft
|
|
241
|
+
self.message_data = message_data
|
|
242
|
+
self.send_at = send_at
|
|
243
|
+
self.language = language
|
|
244
|
+
|
|
245
|
+
def _to_dict(self):
|
|
246
|
+
'''Build a request payload from the object'''
|
|
247
|
+
field_map = dict(
|
|
248
|
+
# field name is the same as the payload field name
|
|
249
|
+
transactional_message_id="transactional_message_id",
|
|
250
|
+
to="to",
|
|
251
|
+
identifiers="identifiers",
|
|
252
|
+
disable_message_retention="disable_message_retention",
|
|
253
|
+
send_to_unsubscribed="send_to_unsubscribed",
|
|
254
|
+
queue_draft="queue_draft",
|
|
255
|
+
message_data="message_data",
|
|
256
|
+
send_at="send_at",
|
|
257
|
+
language="language",
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
data = {}
|
|
261
|
+
for field, name in field_map.items():
|
|
262
|
+
value = getattr(self, field, None)
|
|
263
|
+
if value is not None:
|
|
264
|
+
data[name] = value
|
|
265
|
+
|
|
266
|
+
return data
|
|
@@ -3,7 +3,6 @@ try:
|
|
|
3
3
|
except ImportError:
|
|
4
4
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
5
5
|
|
|
6
|
-
from functools import wraps
|
|
7
6
|
from random import randint
|
|
8
7
|
import json
|
|
9
8
|
import ssl
|
|
@@ -11,12 +10,11 @@ import time
|
|
|
11
10
|
import threading
|
|
12
11
|
import unittest
|
|
13
12
|
|
|
14
|
-
def
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return bar
|
|
13
|
+
def create_ssl_context():
|
|
14
|
+
"""Create SSL context for Python 3.12+ compatibility"""
|
|
15
|
+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
16
|
+
context.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
17
|
+
return context
|
|
20
18
|
|
|
21
19
|
request_counts = dict()
|
|
22
20
|
|
|
@@ -75,12 +73,11 @@ class HTTPSTestCase(unittest.TestCase):
|
|
|
75
73
|
def setUpClass(cls):
|
|
76
74
|
# create a server
|
|
77
75
|
cls.server = HTTPServer(("localhost", 0), Handler)
|
|
78
|
-
#
|
|
79
|
-
|
|
76
|
+
# create SSL context for Python 3.12+ compatibility
|
|
77
|
+
context = create_ssl_context()
|
|
78
|
+
context.load_cert_chain('./tests/server.pem')
|
|
80
79
|
# upgrade to https
|
|
81
|
-
cls.server.socket =
|
|
82
|
-
certfile='./tests/server.pem',
|
|
83
|
-
server_side=True)
|
|
80
|
+
cls.server.socket = context.wrap_socket(cls.server.socket, server_side=True)
|
|
84
81
|
# start server instance in new thread
|
|
85
82
|
cls.server_thread = threading.Thread(target=cls.server.serve_forever)
|
|
86
83
|
cls.server_thread.start()
|
|
@@ -5,7 +5,7 @@ import json
|
|
|
5
5
|
import sys
|
|
6
6
|
import unittest
|
|
7
7
|
|
|
8
|
-
from customerio import APIClient, SendEmailRequest, Regions, CustomerIOException
|
|
8
|
+
from customerio import APIClient, SendEmailRequest, SendPushRequest, SendSMSRequest, Regions, CustomerIOException
|
|
9
9
|
from customerio.__version__ import __version__ as ClientVersion
|
|
10
10
|
from tests.server import HTTPSTestCase
|
|
11
11
|
|
|
@@ -77,5 +77,39 @@ class TestAPIClient(HTTPSTestCase):
|
|
|
77
77
|
|
|
78
78
|
self.client.send_email(email)
|
|
79
79
|
|
|
80
|
+
def test_send_push(self):
|
|
81
|
+
self.client.http.hooks = dict(response=partial(self._check_request, rq={
|
|
82
|
+
'method': 'POST',
|
|
83
|
+
'authorization': "Bearer app_api_key",
|
|
84
|
+
'content_type': 'application/json',
|
|
85
|
+
'url_suffix': '/v1/send/push',
|
|
86
|
+
'body': {"identifiers": {"id":"customer_1"}, "transactional_message_id": 100, "title": "transactional push message", "message": "push message content"}
|
|
87
|
+
}))
|
|
88
|
+
|
|
89
|
+
push = SendPushRequest(
|
|
90
|
+
identifiers={"id":"customer_1"},
|
|
91
|
+
transactional_message_id=100,
|
|
92
|
+
title="transactional push message",
|
|
93
|
+
message="push message content"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self.client.send_push(push)
|
|
97
|
+
|
|
98
|
+
def test_send_sms(self):
|
|
99
|
+
self.client.http.hooks = dict(response=partial(self._check_request, rq={
|
|
100
|
+
'method': 'POST',
|
|
101
|
+
'authorization': "Bearer app_api_key",
|
|
102
|
+
'content_type': 'application/json',
|
|
103
|
+
'url_suffix': '/v1/send/sms',
|
|
104
|
+
'body': {"identifiers": {"id":"customer_1"}, "transactional_message_id": 100}
|
|
105
|
+
}))
|
|
106
|
+
|
|
107
|
+
sms = SendSMSRequest(
|
|
108
|
+
identifiers={"id":"customer_1"},
|
|
109
|
+
transactional_message_id=100,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
self.client.send_sms(sms)
|
|
113
|
+
|
|
80
114
|
if __name__ == '__main__':
|
|
81
115
|
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|