Skip to content

Commit c28e518

Browse files
authored
[TOOLSLIBS-434] SMS Custom Response (#191)
* wires up SmsCustomResponse * adds custom response url * adds custom response class * adds SmsCustomResponse class * typo fix
1 parent 34d7479 commit c28e518

File tree

5 files changed

+151
-2
lines changed

5 files changed

+151
-2
lines changed

tests/devices/test_sms.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import requests
77

88
import urbanairship as ua
9-
from tests import TEST_KEY, TEST_SECRET
9+
from tests import TEST_KEY, TEST_SECRET, TEST_TOKEN
1010

1111

1212
class TestSMS(unittest.TestCase):
@@ -173,3 +173,70 @@ def test_url(self):
173173
self.interaction.url,
174174
"https://go.urbanairship.com/api/sms/15035556789/keywords",
175175
)
176+
177+
178+
class TestSmsCustomResponse(unittest.TestCase):
179+
def setUp(self) -> None:
180+
self.maxDiff = 2000
181+
airship = ua.Airship(key=TEST_KEY, token=TEST_TOKEN)
182+
self.mo_id = "886f53d4-3e0f-46d7-930e-c2792dac6e0a"
183+
self.custom_response = ua.SmsCustomResponse(
184+
airship=airship,
185+
mobile_originated_id=self.mo_id,
186+
)
187+
188+
def test_mms_payload(self):
189+
self.custom_response.mms = ua.mms(
190+
fallback_text="mms alert",
191+
url="http://www.airship.com",
192+
content_type="image/gif",
193+
content_length=12345,
194+
shorten_links=True,
195+
)
196+
197+
self.assertEqual(
198+
self.custom_response._payload,
199+
{
200+
"mobile_originated_id": self.mo_id,
201+
"mms": {
202+
"fallback_text": "mms alert",
203+
"slides": [
204+
{
205+
"media": {
206+
"content_type": "image/gif",
207+
"url": "http://www.airship.com",
208+
"content_length": 12345,
209+
}
210+
}
211+
],
212+
"shorten_links": True,
213+
},
214+
},
215+
)
216+
217+
def test_sms_payload(self):
218+
self.custom_response.sms = ua.sms(alert="sms alert", shorten_links=False)
219+
220+
self.assertEqual(
221+
self.custom_response._payload,
222+
{
223+
"sms": {"alert": "sms alert", "shorten_links": False},
224+
"mobile_originated_id": self.mo_id,
225+
},
226+
)
227+
228+
def test_neither_payload_raises(self):
229+
with self.assertRaises(ValueError, msg="One of mms or sms must be set."):
230+
self.custom_response._payload
231+
232+
def test_both_payloads_raises(self):
233+
self.custom_response.sms = ua.sms(alert="test_alert")
234+
self.custom_response.mms = ua.mms(
235+
content_length=12345,
236+
content_type="image/png",
237+
fallback_text="test mms",
238+
url="url",
239+
)
240+
241+
with self.assertRaises(ValueError, msg="Cannot use both mms and sms."):
242+
self.custom_response._payload

urbanairship/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
Segment,
3030
SegmentList,
3131
Sms,
32+
SmsCustomResponse,
3233
StaticList,
3334
StaticLists,
3435
SubscriptionList,

urbanairship/core.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def __init__(self, location=None):
4545
self.sms_url = self.channel_url + "sms/"
4646
self.sms_opt_out_url = self.sms_url + "opt-out/"
4747
self.sms_uninstall_url = self.sms_url + "uninstall/"
48+
self.sms_custom_response_url = self.base_url + "sms/custom-response/"
4849
self.email_url = self.channel_url + "email/"
4950
self.email_tags_url = self.email_url + "tags/"
5051
self.email_uninstall_url = self.email_url + "uninstall/"

urbanairship/devices/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
from .named_users import NamedUser, NamedUserList, NamedUserTags
77
from .open_channel import OpenChannel
88
from .segment import Segment, SegmentList
9-
from .sms import Sms, KeywordInteraction
9+
from .sms import Sms, KeywordInteraction, SmsCustomResponse
1010
from .static_lists import StaticList, StaticLists
1111
from .subscription_lists import SubscriptionList

urbanairship/devices/sms.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import logging
44
import re
55
import sys
6+
from typing import Dict, Optional
67

78
logger = logging.getLogger("urbanairship")
89

@@ -347,3 +348,82 @@ def post(self):
347348
)
348349

349350
return response
351+
352+
353+
class SmsCustomResponse:
354+
"""Respond to a mobile originated message based on a keyword consumed by your
355+
custom response webhook, using a mobile-originated ID. Please see the documentation
356+
at https://docs.airship.com/api/ua/?http#operation-api-sms-custom-response-post for
357+
details on use of this feature.
358+
359+
One of `mms` or `sms` is required.
360+
361+
:param airship: [required] An urbanairship.Airship instance, created with
362+
bearer token authentication.
363+
:param mobile_originated_id: [required] The identifier that you received through
364+
your SMS webhook corresponding to the mobile-originated message that you're
365+
issuing a custom response to.
366+
:param sms: [optional] An SMS platform override object, created using
367+
`ua.sms()`. This defines the message sent in response.
368+
:param mms: [optional] An MMS platform override object, created using
369+
`us.mms()`. The defines the message sent in response.
370+
"""
371+
372+
def __init__(
373+
self,
374+
airship,
375+
mobile_originated_id: str,
376+
sms: Optional[Dict] = None,
377+
mms: Optional[Dict] = None,
378+
) -> None:
379+
self.airship = airship
380+
self.mobile_originated_id = mobile_originated_id
381+
self.sms = sms
382+
self.mms = mms
383+
384+
@property
385+
def sms(self) -> Optional[Dict]:
386+
return self._sms
387+
388+
@sms.setter
389+
def sms(self, value: Optional[Dict]) -> None:
390+
self._sms = value
391+
392+
@property
393+
def mms(self) -> Optional[Dict]:
394+
return self._mms
395+
396+
@mms.setter
397+
def mms(self, value: Optional[Dict]) -> None:
398+
self._mms = value
399+
400+
@property
401+
def _payload(self) -> Dict:
402+
if all((self.mms, self.sms)):
403+
raise ValueError("Cannot use both mms and sms.")
404+
if all((self.sms is None, self.mms is None)):
405+
raise ValueError("One of mms or sms must be set.")
406+
407+
payload = {"mobile_originated_id": self.mobile_originated_id}
408+
409+
if self.sms is not None:
410+
payload["sms"] = self.sms
411+
if self.mms is not None:
412+
payload.update(self.mms)
413+
414+
return payload
415+
416+
def send(self) -> Dict:
417+
"""Sends the response using the mobile_originated_id value
418+
419+
:return: An API response dictionary
420+
"""
421+
response = self.airship.request(
422+
method="POST",
423+
body=json.dumps(self._payload),
424+
url=self.airship.urls.get("sms_custom_response_url"),
425+
content_type="application/json",
426+
version=3,
427+
)
428+
429+
return response.json()

0 commit comments

Comments
 (0)