From 520f585b24b8979876973ed58dc58c1d5c6c6e71 Mon Sep 17 00:00:00 2001 From: Joel Kleier Date: Sun, 19 Mar 2023 22:13:33 -0500 Subject: [PATCH] initial twilio integration without upgrade steps --- castle/cms/interfaces/controlpanel.py | 30 +++++++ castle/cms/texting.py | 115 +++++++++++++++++++++----- 2 files changed, 125 insertions(+), 20 deletions(-) diff --git a/castle/cms/interfaces/controlpanel.py b/castle/cms/interfaces/controlpanel.py index 407eab883..2585dbd27 100644 --- a/castle/cms/interfaces/controlpanel.py +++ b/castle/cms/interfaces/controlpanel.py @@ -399,6 +399,11 @@ class IAPISettings(Interface): required=False, default=None) + plivo_enabled = schema.Bool( + title=u'Enable Plivo', + description=u'Enable or disable Plivo usage, if auth id/token/number are configured.', + default=True) + plivo_auth_id = schema.TextLine( title=u'Plivo Auth ID', description=u'Text messaging API', @@ -413,6 +418,31 @@ class IAPISettings(Interface): description=u'For making text messages from', required=False) + twilio_enabled = schema.Bool( + title=u'Enable Twilio', + description=u'Enable or disable Twilio usage, if account sid/auth token/from number are configured.', + default=False) + + twilio_override = schema.Bool( + title=u'Override use of Plivo with Twilio', + description=u'If both Twilio and Plivo config are enabled, then Twilio will take ' + u'precedence if this is enabled.', + default=False) + + twilio_account_sid = schema.TextLine( + title=u'Twilio Account SID', + required=False) + + twilio_auth_token = schema.TextLine( + title=u'Twilio Auth Token', + required=False) + + twilio_from = schema.TextLine( + title=u'Twilio From Number', + description=u'Phone number that will send sms messages', + required=False) + + etherpad_url = schema.TextLine( title=u'Etherpad URL', description=u'The full address of your Etherpad server, e.g. http://127.0.0.1:9001', diff --git a/castle/cms/texting.py b/castle/cms/texting.py index 57ff7e958..24330c3d7 100644 --- a/castle/cms/texting.py +++ b/castle/cms/texting.py @@ -4,34 +4,18 @@ from zope.component import getUtility import json +import logging import requests import string -def send(message, numbers): - if numbers == ALL_SUBSCRIBERS: - numbers = subscribe.get_phone_numbers() - - if isinstance(numbers, basestring): - numbers = [numbers] - - clean_numbers = [] - for number in numbers: - number = ''.join(n for n in number if n in string.digits) - if len(number) == 10: - # assume at least american.. - number = '1' + number - clean_numbers.append(number) +logger = logging.getLogger('castle.cms') - registry = getUtility(IRegistry) - auth_id = registry.get('castle.plivo_auth_id') - auth_token = registry.get('castle.plivo_auth_token') - src = registry.get('castle.plivo_phone_number') - src = ''.join(n for n in src if n in string.digits) +def plivo_send(message, auth_id, auth_token, src, numbers): params = { 'src': src, - 'dst': '<'.join(clean_numbers), + 'dst': '<'.join(numbers), 'text': message } resp = requests.post( @@ -45,6 +29,97 @@ def send(message, numbers): try: return 'error' not in resp.json() except Exception: + logger.error( + 'failed to send message through plivo', + exc_info=True) return False else: return False + + +def twilio_send(message, account_sid, auth_token, frm, to): + # Example curl: + # curl -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Messages.json" \ + # --data-urlencode "From=+15017122661" \ + # --data-urlencode "Body=Hi there" \ + # --data-urlencode "To=+15558675310" \ + # -u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN + + url = 'https://api.twilio.com/2010-04-01/Accounts/{}/Messages.json'.format(account_sid) + data = { + # the format used for phone numbers is "E.164" which means the number needs to + # start with a "+", have a country code, then have a phone number with area code + # US would be +15555555555 as example + "From": "+{}".format(frm), + "Body": message, + } + # twilio's api sends message to 1 recipient per call + sentall = True + for t in to: + # the format used for phone numbers is "E.164" which means the number needs to + # start with a "+", have a country code, then have a phone number with area code + # US would be +15555555555 as example + data["To"] = "+{}".format(t) + try: + resp = requests.post(url, data=data, auth=(account_sid, auth_token)) + resp.raise_for_status() + except Exception: + logger.error( + 'failed to send message through twilio to "{}"'.format(data["To"]), + exc_info=True) + sentall = False + + return sentall + + +def send(message, numbers): + if numbers == ALL_SUBSCRIBERS: + numbers = subscribe.get_phone_numbers() + + if isinstance(numbers, basestring): + numbers = [numbers] + + clean_numbers = [] + for number in numbers: + number = ''.join(n for n in number if n in string.digits) + if len(number) == 10: + # assume at least american.. + number = '1' + number + clean_numbers.append(number) + + registry = getUtility(IRegistry) + + plivo_enabled = registry.get('castle.plivo_enabled') + twilio_enabled = registry.get('castle.twilio_enabled') + twilio_override = registry.get('castle.twilio_override') + + plivo_auth_id = registry.get('castle.plivo_auth_id') + plivo_auth_token = registry.get('castle.plivo_auth_token') + plivo_src = registry.get('castle.plivo_phone_number') + plivo_src = ''.join(n for n in plivo_src if n in string.digits) + + plivo_configured = True + if plivo_auth_id is None or len(plivo_auth_id.strip()) <= 0: + plivo_configured = False + elif plivo_auth_token is None or len(plivo_auth_token.strip()) <= 0: + plivo_configured = False + elif plivo_src is None or len(plivo_src.strip()) <= 0: + plivo_configured = False + + twilio_account_sid = registry.get('castle.twilio_account_sid') + twilio_auth_token = registry.get('castle.twilio_auth_token') + twilio_from = registry.get('castle.twilio_from') + + twilio_configured = True + if twilio_account_sid is None or len(twilio_account_sid.strip()) <= 0: + twilio_configured = False + elif twilio_auth_token is None or len(twilio_auth_token.strip()) <= 0: + twilio_configured = False + elif twilio_from is None or len(twilio_from.strip()) <= 0: + plivo_configured = False + + if plivo_enabled and not twilio_override and plivo_configured: + plivo_send(message, plivo_auth_id, plivo_auth_token, plivo_src, clean_numbers) + elif twilio_enabled and twilio_configured: + twilio_send(message, twilio_account_sid, twilio_auth_token, twilio_from, clean_numbers) +