ccai-python 1.1.0__tar.gz → 1.2.0__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.
- {ccai_python-1.1.0 → ccai_python-1.2.0}/PKG-INFO +38 -15
- {ccai_python-1.1.0 → ccai_python-1.2.0}/README.md +37 -14
- {ccai_python-1.1.0 → ccai_python-1.2.0}/pyproject.toml +1 -1
- {ccai_python-1.1.0 → ccai_python-1.2.0}/setup.py +1 -1
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/sms/sms.py +32 -3
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python.egg-info/PKG-INFO +38 -15
- {ccai_python-1.1.0 → ccai_python-1.2.0}/tests/test_sms.py +42 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/setup.cfg +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/__init__.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/brand_service.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/campaign_service.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/ccai.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/contact_service.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/contact_test.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/contact_validator_service.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/email_send.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/email_service.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/mms_send.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/sms/__init__.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/sms/mms.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/sms_send.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/webhook.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/webhook_handler_example.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python/webhook_test.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python.egg-info/SOURCES.txt +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python.egg-info/dependency_links.txt +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python.egg-info/requires.txt +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/src/ccai_python.egg-info/top_level.txt +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/tests/test_ccai.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/tests/test_contact.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/tests/test_email.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/tests/test_mms.py +0 -0
- {ccai_python-1.1.0 → ccai_python-1.2.0}/tests/test_webhook.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ccai-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Python client for CloudContactAI API
|
|
5
5
|
Author: CloudContactAI LLC
|
|
6
6
|
License: MIT
|
|
@@ -21,7 +21,7 @@ Dynamic: requires-python
|
|
|
21
21
|
|
|
22
22
|
# CCAI Python Client
|
|
23
23
|
|
|
24
|
-
Version: 1.
|
|
24
|
+
Version: 1.2.0
|
|
25
25
|
|
|
26
26
|
A Python client for interacting with the CloudContactAI API.
|
|
27
27
|
|
|
@@ -70,6 +70,30 @@ campaign_response = ccai.sms.send(
|
|
|
70
70
|
print(f"Campaign sent with ID: {campaign_response.campaign_id}")
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
### SMS — Template-Controlled Accounts
|
|
74
|
+
|
|
75
|
+
If an account has been configured to enforce template-only messaging, all campaigns must reference a pre-approved template ID. Sending a free-text message to such an account will result in a `422` error.
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# Send to multiple recipients using a template
|
|
79
|
+
response = ccai.sms.send_with_template(
|
|
80
|
+
accounts=accounts,
|
|
81
|
+
template_id=12345, # the ID of the approved template
|
|
82
|
+
title="My Campaign"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Send to a single recipient using a template
|
|
86
|
+
response = ccai.sms.send_single_with_template(
|
|
87
|
+
first_name="John",
|
|
88
|
+
last_name="Doe",
|
|
89
|
+
phone="+15551234567",
|
|
90
|
+
template_id=12345,
|
|
91
|
+
title="My Campaign"
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The message body is resolved server-side from the template. Variable substitution (e.g. `${firstName}`) is applied automatically using the recipient's account data.
|
|
96
|
+
|
|
73
97
|
### MMS
|
|
74
98
|
|
|
75
99
|
```python
|
|
@@ -88,7 +112,6 @@ def track_progress(status):
|
|
|
88
112
|
# Create options with progress tracking
|
|
89
113
|
options = SMSOptions(
|
|
90
114
|
timeout=60,
|
|
91
|
-
retries=3,
|
|
92
115
|
on_progress=track_progress
|
|
93
116
|
)
|
|
94
117
|
|
|
@@ -316,22 +339,18 @@ def verify_and_handle_webhook(signature, client_id, event_hash, secret):
|
|
|
316
339
|
print("Invalid signature")
|
|
317
340
|
|
|
318
341
|
# Create a webhook handler for web frameworks
|
|
319
|
-
def
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
342
|
+
def handle_event(event):
|
|
343
|
+
if event.event_type == WebhookEventType.MESSAGE_SENT:
|
|
344
|
+
print(f"Message sent: {event.data.get('Message')} to {event.data.get('To')}")
|
|
345
|
+
elif event.event_type == WebhookEventType.MESSAGE_RECEIVED:
|
|
346
|
+
print(f"Message received: {event.data.get('Message')} from {event.data.get('From')}")
|
|
324
347
|
|
|
325
|
-
|
|
326
|
-
'on_message_sent': handle_message_sent,
|
|
327
|
-
'on_message_received': handle_message_received
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
webhook_handler = ccai.webhook.create_handler(handlers)
|
|
348
|
+
webhook_handler = ccai.webhook.create_handler({'on_event': handle_event})
|
|
331
349
|
|
|
332
350
|
# Use with Flask
|
|
333
|
-
|
|
351
|
+
import os
|
|
334
352
|
import json
|
|
353
|
+
from flask import Flask, request, jsonify
|
|
335
354
|
|
|
336
355
|
app = Flask(__name__)
|
|
337
356
|
|
|
@@ -435,6 +454,8 @@ campaign = ccai.campaigns.create({
|
|
|
435
454
|
"subUseCases": ["CUSTOMER_CARE", "TWO_FACTOR_AUTHENTICATION", "ACCOUNT_NOTIFICATION"],
|
|
436
455
|
"description": "Security codes and support messaging.",
|
|
437
456
|
"messageFlow": "Users opt-in via signup form at https://example.com/signup",
|
|
457
|
+
"termsLink": "https://example.com/terms",
|
|
458
|
+
"privacyLink": "https://example.com/privacy",
|
|
438
459
|
"hasEmbeddedLinks": True,
|
|
439
460
|
"hasEmbeddedPhone": False,
|
|
440
461
|
"isAgeGated": False,
|
|
@@ -475,6 +496,8 @@ ccai.campaigns.delete(campaign["id"])
|
|
|
475
496
|
|
|
476
497
|
> Note: `MIXED` and `LOW_VOLUME_MIXED` campaigns require 2–3 `subUseCases`.
|
|
477
498
|
|
|
499
|
+
> `termsLink` and `privacyLink` are optional fields on the campaign dict.
|
|
500
|
+
|
|
478
501
|
#### Sub-Use Cases
|
|
479
502
|
|
|
480
503
|
`TWO_FACTOR_AUTHENTICATION`, `ACCOUNT_NOTIFICATION`, `CUSTOMER_CARE`, `DELIVERY_NOTIFICATION`, `FRAUD_ALERT`, `MARKETING`, `POLLING_VOTING`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CCAI Python Client
|
|
2
2
|
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
|
|
5
5
|
A Python client for interacting with the CloudContactAI API.
|
|
6
6
|
|
|
@@ -49,6 +49,30 @@ campaign_response = ccai.sms.send(
|
|
|
49
49
|
print(f"Campaign sent with ID: {campaign_response.campaign_id}")
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
### SMS — Template-Controlled Accounts
|
|
53
|
+
|
|
54
|
+
If an account has been configured to enforce template-only messaging, all campaigns must reference a pre-approved template ID. Sending a free-text message to such an account will result in a `422` error.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
# Send to multiple recipients using a template
|
|
58
|
+
response = ccai.sms.send_with_template(
|
|
59
|
+
accounts=accounts,
|
|
60
|
+
template_id=12345, # the ID of the approved template
|
|
61
|
+
title="My Campaign"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Send to a single recipient using a template
|
|
65
|
+
response = ccai.sms.send_single_with_template(
|
|
66
|
+
first_name="John",
|
|
67
|
+
last_name="Doe",
|
|
68
|
+
phone="+15551234567",
|
|
69
|
+
template_id=12345,
|
|
70
|
+
title="My Campaign"
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The message body is resolved server-side from the template. Variable substitution (e.g. `${firstName}`) is applied automatically using the recipient's account data.
|
|
75
|
+
|
|
52
76
|
### MMS
|
|
53
77
|
|
|
54
78
|
```python
|
|
@@ -67,7 +91,6 @@ def track_progress(status):
|
|
|
67
91
|
# Create options with progress tracking
|
|
68
92
|
options = SMSOptions(
|
|
69
93
|
timeout=60,
|
|
70
|
-
retries=3,
|
|
71
94
|
on_progress=track_progress
|
|
72
95
|
)
|
|
73
96
|
|
|
@@ -295,22 +318,18 @@ def verify_and_handle_webhook(signature, client_id, event_hash, secret):
|
|
|
295
318
|
print("Invalid signature")
|
|
296
319
|
|
|
297
320
|
# Create a webhook handler for web frameworks
|
|
298
|
-
def
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
321
|
+
def handle_event(event):
|
|
322
|
+
if event.event_type == WebhookEventType.MESSAGE_SENT:
|
|
323
|
+
print(f"Message sent: {event.data.get('Message')} to {event.data.get('To')}")
|
|
324
|
+
elif event.event_type == WebhookEventType.MESSAGE_RECEIVED:
|
|
325
|
+
print(f"Message received: {event.data.get('Message')} from {event.data.get('From')}")
|
|
303
326
|
|
|
304
|
-
|
|
305
|
-
'on_message_sent': handle_message_sent,
|
|
306
|
-
'on_message_received': handle_message_received
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
webhook_handler = ccai.webhook.create_handler(handlers)
|
|
327
|
+
webhook_handler = ccai.webhook.create_handler({'on_event': handle_event})
|
|
310
328
|
|
|
311
329
|
# Use with Flask
|
|
312
|
-
|
|
330
|
+
import os
|
|
313
331
|
import json
|
|
332
|
+
from flask import Flask, request, jsonify
|
|
314
333
|
|
|
315
334
|
app = Flask(__name__)
|
|
316
335
|
|
|
@@ -414,6 +433,8 @@ campaign = ccai.campaigns.create({
|
|
|
414
433
|
"subUseCases": ["CUSTOMER_CARE", "TWO_FACTOR_AUTHENTICATION", "ACCOUNT_NOTIFICATION"],
|
|
415
434
|
"description": "Security codes and support messaging.",
|
|
416
435
|
"messageFlow": "Users opt-in via signup form at https://example.com/signup",
|
|
436
|
+
"termsLink": "https://example.com/terms",
|
|
437
|
+
"privacyLink": "https://example.com/privacy",
|
|
417
438
|
"hasEmbeddedLinks": True,
|
|
418
439
|
"hasEmbeddedPhone": False,
|
|
419
440
|
"isAgeGated": False,
|
|
@@ -454,6 +475,8 @@ ccai.campaigns.delete(campaign["id"])
|
|
|
454
475
|
|
|
455
476
|
> Note: `MIXED` and `LOW_VOLUME_MIXED` campaigns require 2–3 `subUseCases`.
|
|
456
477
|
|
|
478
|
+
> `termsLink` and `privacyLink` are optional fields on the campaign dict.
|
|
479
|
+
|
|
457
480
|
#### Sub-Use Cases
|
|
458
481
|
|
|
459
482
|
`TWO_FACTOR_AUTHENTICATION`, `ACCOUNT_NOTIFICATION`, `CUSTOMER_CARE`, `DELIVERY_NOTIFICATION`, `FRAUD_ALERT`, `MARKETING`, `POLLING_VOTING`
|
|
@@ -30,8 +30,9 @@ class Account(BaseModel):
|
|
|
30
30
|
class SMSCampaign(BaseModel):
|
|
31
31
|
"""SMS campaign data model"""
|
|
32
32
|
accounts: List[Account] = Field(..., description="List of recipient accounts")
|
|
33
|
-
message: str = Field(
|
|
33
|
+
message: str = Field(default="", description="Message content with optional variables")
|
|
34
34
|
title: str = Field(..., description="Campaign title")
|
|
35
|
+
template_id: Optional[int] = Field(default=None, alias="templateId", description="Optional template ID for template-controlled accounts")
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
class SMSResponse(BaseModel):
|
|
@@ -99,11 +100,12 @@ class SMS:
|
|
|
99
100
|
message: str,
|
|
100
101
|
title: str,
|
|
101
102
|
sender_phone: Optional[str] = None,
|
|
102
|
-
options: Optional[SMSOptions] = None
|
|
103
|
+
options: Optional[SMSOptions] = None,
|
|
104
|
+
template_id: Optional[int] = None
|
|
103
105
|
) -> SMSResponse:
|
|
104
106
|
if not accounts:
|
|
105
107
|
raise ValueError("At least one account is required")
|
|
106
|
-
if not message:
|
|
108
|
+
if not message and template_id is None:
|
|
107
109
|
raise ValueError("Message is required")
|
|
108
110
|
if not title:
|
|
109
111
|
raise ValueError("Campaign title is required")
|
|
@@ -151,6 +153,8 @@ class SMS:
|
|
|
151
153
|
}
|
|
152
154
|
if sender_phone:
|
|
153
155
|
payload["senderPhone"] = sender_phone
|
|
156
|
+
if template_id is not None:
|
|
157
|
+
payload["templateId"] = template_id
|
|
154
158
|
|
|
155
159
|
try:
|
|
156
160
|
if options and options.on_progress:
|
|
@@ -193,3 +197,28 @@ class SMS:
|
|
|
193
197
|
)
|
|
194
198
|
return self.send([account], message, title, sender_phone, options)
|
|
195
199
|
|
|
200
|
+
def send_with_template(
|
|
201
|
+
self,
|
|
202
|
+
accounts: List[Union[Account, Dict[str, str]]],
|
|
203
|
+
template_id: int,
|
|
204
|
+
title: str,
|
|
205
|
+
sender_phone: Optional[str] = None,
|
|
206
|
+
options: Optional[SMSOptions] = None
|
|
207
|
+
) -> SMSResponse:
|
|
208
|
+
"""Send SMS using a pre-approved template (for template-controlled accounts)."""
|
|
209
|
+
return self.send(accounts, "", title, sender_phone, options, template_id)
|
|
210
|
+
|
|
211
|
+
def send_single_with_template(
|
|
212
|
+
self,
|
|
213
|
+
first_name: str,
|
|
214
|
+
last_name: str,
|
|
215
|
+
phone: str,
|
|
216
|
+
template_id: int,
|
|
217
|
+
title: str,
|
|
218
|
+
sender_phone: Optional[str] = None,
|
|
219
|
+
options: Optional[SMSOptions] = None
|
|
220
|
+
) -> SMSResponse:
|
|
221
|
+
"""Send SMS to a single recipient using a pre-approved template."""
|
|
222
|
+
account = Account(first_name=first_name, last_name=last_name, phone=phone)
|
|
223
|
+
return self.send_with_template([account], template_id, title, sender_phone, options)
|
|
224
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ccai-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Python client for CloudContactAI API
|
|
5
5
|
Author: CloudContactAI LLC
|
|
6
6
|
License: MIT
|
|
@@ -21,7 +21,7 @@ Dynamic: requires-python
|
|
|
21
21
|
|
|
22
22
|
# CCAI Python Client
|
|
23
23
|
|
|
24
|
-
Version: 1.
|
|
24
|
+
Version: 1.2.0
|
|
25
25
|
|
|
26
26
|
A Python client for interacting with the CloudContactAI API.
|
|
27
27
|
|
|
@@ -70,6 +70,30 @@ campaign_response = ccai.sms.send(
|
|
|
70
70
|
print(f"Campaign sent with ID: {campaign_response.campaign_id}")
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
### SMS — Template-Controlled Accounts
|
|
74
|
+
|
|
75
|
+
If an account has been configured to enforce template-only messaging, all campaigns must reference a pre-approved template ID. Sending a free-text message to such an account will result in a `422` error.
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# Send to multiple recipients using a template
|
|
79
|
+
response = ccai.sms.send_with_template(
|
|
80
|
+
accounts=accounts,
|
|
81
|
+
template_id=12345, # the ID of the approved template
|
|
82
|
+
title="My Campaign"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Send to a single recipient using a template
|
|
86
|
+
response = ccai.sms.send_single_with_template(
|
|
87
|
+
first_name="John",
|
|
88
|
+
last_name="Doe",
|
|
89
|
+
phone="+15551234567",
|
|
90
|
+
template_id=12345,
|
|
91
|
+
title="My Campaign"
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The message body is resolved server-side from the template. Variable substitution (e.g. `${firstName}`) is applied automatically using the recipient's account data.
|
|
96
|
+
|
|
73
97
|
### MMS
|
|
74
98
|
|
|
75
99
|
```python
|
|
@@ -88,7 +112,6 @@ def track_progress(status):
|
|
|
88
112
|
# Create options with progress tracking
|
|
89
113
|
options = SMSOptions(
|
|
90
114
|
timeout=60,
|
|
91
|
-
retries=3,
|
|
92
115
|
on_progress=track_progress
|
|
93
116
|
)
|
|
94
117
|
|
|
@@ -316,22 +339,18 @@ def verify_and_handle_webhook(signature, client_id, event_hash, secret):
|
|
|
316
339
|
print("Invalid signature")
|
|
317
340
|
|
|
318
341
|
# Create a webhook handler for web frameworks
|
|
319
|
-
def
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
342
|
+
def handle_event(event):
|
|
343
|
+
if event.event_type == WebhookEventType.MESSAGE_SENT:
|
|
344
|
+
print(f"Message sent: {event.data.get('Message')} to {event.data.get('To')}")
|
|
345
|
+
elif event.event_type == WebhookEventType.MESSAGE_RECEIVED:
|
|
346
|
+
print(f"Message received: {event.data.get('Message')} from {event.data.get('From')}")
|
|
324
347
|
|
|
325
|
-
|
|
326
|
-
'on_message_sent': handle_message_sent,
|
|
327
|
-
'on_message_received': handle_message_received
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
webhook_handler = ccai.webhook.create_handler(handlers)
|
|
348
|
+
webhook_handler = ccai.webhook.create_handler({'on_event': handle_event})
|
|
331
349
|
|
|
332
350
|
# Use with Flask
|
|
333
|
-
|
|
351
|
+
import os
|
|
334
352
|
import json
|
|
353
|
+
from flask import Flask, request, jsonify
|
|
335
354
|
|
|
336
355
|
app = Flask(__name__)
|
|
337
356
|
|
|
@@ -435,6 +454,8 @@ campaign = ccai.campaigns.create({
|
|
|
435
454
|
"subUseCases": ["CUSTOMER_CARE", "TWO_FACTOR_AUTHENTICATION", "ACCOUNT_NOTIFICATION"],
|
|
436
455
|
"description": "Security codes and support messaging.",
|
|
437
456
|
"messageFlow": "Users opt-in via signup form at https://example.com/signup",
|
|
457
|
+
"termsLink": "https://example.com/terms",
|
|
458
|
+
"privacyLink": "https://example.com/privacy",
|
|
438
459
|
"hasEmbeddedLinks": True,
|
|
439
460
|
"hasEmbeddedPhone": False,
|
|
440
461
|
"isAgeGated": False,
|
|
@@ -475,6 +496,8 @@ ccai.campaigns.delete(campaign["id"])
|
|
|
475
496
|
|
|
476
497
|
> Note: `MIXED` and `LOW_VOLUME_MIXED` campaigns require 2–3 `subUseCases`.
|
|
477
498
|
|
|
499
|
+
> `termsLink` and `privacyLink` are optional fields on the campaign dict.
|
|
500
|
+
|
|
478
501
|
#### Sub-Use Cases
|
|
479
502
|
|
|
480
503
|
`TWO_FACTOR_AUTHENTICATION`, `ACCOUNT_NOTIFICATION`, `CUSTOMER_CARE`, `DELIVERY_NOTIFICATION`, `FRAUD_ALERT`, `MARKETING`, `POLLING_VOTING`
|
|
@@ -289,5 +289,47 @@ class TestSMS(unittest.TestCase):
|
|
|
289
289
|
)
|
|
290
290
|
|
|
291
291
|
|
|
292
|
+
@patch.object(CCAI, 'request')
|
|
293
|
+
def test_send_with_template_id(self, mock_request):
|
|
294
|
+
"""Test sending SMS with a template ID"""
|
|
295
|
+
mock_request.return_value = {
|
|
296
|
+
"id": "msg-tpl-1",
|
|
297
|
+
"status": "sent",
|
|
298
|
+
"campaign_id": "camp-tpl-1"
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
response = self.ccai.sms.send_with_template(
|
|
302
|
+
accounts=[self.account],
|
|
303
|
+
template_id=12345,
|
|
304
|
+
title="Template Campaign"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
self.assertEqual(response.id, "msg-tpl-1")
|
|
308
|
+
|
|
309
|
+
call_args = mock_request.call_args
|
|
310
|
+
payload = call_args.kwargs.get("data") or call_args[1].get("data")
|
|
311
|
+
self.assertEqual(payload["templateId"], 12345)
|
|
312
|
+
self.assertEqual(payload["message"], "")
|
|
313
|
+
|
|
314
|
+
@patch.object(CCAI, 'request')
|
|
315
|
+
def test_send_single_with_template_id(self, mock_request):
|
|
316
|
+
"""Test sending SMS to a single recipient with a template ID"""
|
|
317
|
+
mock_request.return_value = {"id": "msg-tpl-2", "status": "sent"}
|
|
318
|
+
|
|
319
|
+
response = self.ccai.sms.send_single_with_template(
|
|
320
|
+
first_name="Jane",
|
|
321
|
+
last_name="Smith",
|
|
322
|
+
phone="+14156961732",
|
|
323
|
+
template_id=99,
|
|
324
|
+
title="Single Template Campaign"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
self.assertEqual(response.id, "msg-tpl-2")
|
|
328
|
+
|
|
329
|
+
call_args = mock_request.call_args
|
|
330
|
+
payload = call_args.kwargs.get("data") or call_args[1].get("data")
|
|
331
|
+
self.assertEqual(payload["templateId"], 99)
|
|
332
|
+
|
|
333
|
+
|
|
292
334
|
if __name__ == '__main__':
|
|
293
335
|
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
|
|
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
|