Skip to content

Commit 333a42f

Browse files
authored
Add twilio sms webhook if configured to auto create (#128)
1 parent 4ca799b commit 333a42f

File tree

6 files changed

+104
-4
lines changed

6 files changed

+104
-4
lines changed

llmstack/apps/apis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from django.core.validators import validate_email
77
from django.db.models import Q
88
from django.forms import ValidationError
9-
from django.http import HttpResponse, StreamingHttpResponse
9+
from django.http import StreamingHttpResponse
1010
from django.shortcuts import get_object_or_404
1111
from django.utils.decorators import method_decorator
1212
from django.views.decorators.cache import cache_page

llmstack/apps/app_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .types.web import WebApp
88
from .types.discord import DiscordApp
99
from .types.slack import SlackApp
10+
from .types.twilio_sms import TwilioSmsApp
1011

1112

1213
class AppTypeFactory:

llmstack/apps/integration_configs.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,12 @@ class DiscordIntegrationConfig(AppIntegrationConfig):
6464
bot_token: str = ''
6565
public_key: str = ''
6666
slash_command_id: Optional[str] = None
67-
67+
68+
6869
class TwilioIntegrationConfig(AppIntegrationConfig):
6970
config_type = 'twilio'
7071
is_encrypted = True
7172
account_sid: str = ''
7273
auth_token: str = ''
7374
phone_numbers: list = []
74-
75+
auto_create_sms_webhook = False

llmstack/apps/models.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
logger = logging.getLogger(__name__)
1818

19+
1920
class AppVisibility(models.IntegerChoices):
2021
PRIVATE = 0, 'Private' # only the owner of the app and listed emails can access the app
2122
# only members of the organization can access the app
@@ -237,7 +238,7 @@ def slack_config(self):
237238
def discord_config(self):
238239
profile = Profile.objects.get(user=self.owner)
239240
return DiscordIntegrationConfig().from_dict(self.discord_integration_config, profile.decrypt_value) if self.discord_integration_config else None
240-
241+
241242
@property
242243
def twilio_config(self):
243244
profile = Profile.objects.get(user=self.owner)
@@ -408,3 +409,7 @@ def update_app_pre_save(sender, instance, **kwargs):
408409
instance.type, 'slack',
409410
)
410411
instance = slack_app_type_handler_cls.pre_save(instance)
412+
413+
twilio_sms_type_handler_cls = AppTypeFactory.get_app_type_handler(
414+
instance.type, 'twilio_sms')
415+
instance = twilio_sms_type_handler_cls.pre_save(instance)

llmstack/apps/types/twilio_sms.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import logging
2+
from typing import List
3+
4+
from pydantic import Field
5+
import requests
6+
from llmstack.apps.models import App
7+
from llmstack.apps.types.app_type_interface import AppTypeInterface, BaseSchema
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
class TwilioSmsAppConfigSchema(BaseSchema):
13+
account_sid: str = Field(
14+
title='Account SID', description="Account SID of the Twilio account. Your account's SID can be found in the console.", required=True,
15+
)
16+
auth_token: str = Field(
17+
title='Auth Token', widget='password',
18+
description="Auth token of the Twilio account. Your account's auth token can be found in the console.", required=True,
19+
)
20+
phone_numbers: List[str] = Field(
21+
title='Phone Numbers', description='Phone numbers to send SMS messages from.', required=True,
22+
)
23+
auto_create_sms_webhook: bool = Field(default=False, title='Auto Create SMS Webhook',
24+
description='Automatically create an SMS webhook for the phone numbers.', required=False,
25+
)
26+
27+
28+
class TwilioSmsApp(AppTypeInterface[TwilioSmsAppConfigSchema]):
29+
@staticmethod
30+
def slug() -> str:
31+
return 'twilio_sms'
32+
33+
@staticmethod
34+
def name() -> str:
35+
return 'Twilio SMS App'
36+
37+
@staticmethod
38+
def description() -> str:
39+
return 'Send SMS messages from Twilio.'
40+
41+
@classmethod
42+
def pre_save(self, app: App):
43+
if app.is_published and app.twilio_config:
44+
config = app.twilio_config
45+
phone_numbers = config.get('phone_numbers', [])
46+
47+
account_sid = config.get('account_sid', None)
48+
auth_token = config.get('auth_token', None)
49+
auto_create_sms_webhook = config.get(
50+
'auto_create_sms_webhook', False)
51+
52+
if not phone_numbers:
53+
raise Exception(
54+
'You must provide at least one phone number to send SMS messages from.')
55+
if not account_sid or not auth_token:
56+
raise Exception(
57+
'You must provide an account SID and auth token to send SMS messages from.')
58+
59+
headers = {
60+
'Content-Type': 'application/x-www-form-urlencoded',
61+
'Accept': 'application/json'
62+
}
63+
auth = (account_sid, auth_token)
64+
for phone_number in phone_numbers:
65+
ph_no = phone_number.strip().replace('+', '').replace('-', '')
66+
response = requests.get(
67+
f'https://api.twilio.com/2010-04-01/Accounts/{account_sid}/IncomingPhoneNumbers.json?PhoneNumber=%2B{ph_no}',
68+
headers=headers, auth=auth)
69+
if response.status_code != 200:
70+
raise Exception(
71+
f'Invalid phone number {phone_number}. Please provide a valid phone number that you own.')
72+
twilio_phone_number_resource = response.json()[
73+
'incoming_phone_numbers'][0]
74+
sms_url = twilio_phone_number_resource['sms_url']
75+
# Create SMS webhook if it doesn't exist
76+
if auto_create_sms_webhook and (not sms_url or sms_url != f'https://trypromptly.com/api/apps/{app.uuid}/twiliosms/run'):
77+
# Update twilio phone number resource with voice webhook
78+
response = requests.post(
79+
f'https://api.twilio.com/2010-04-01/Accounts/{account_sid}/IncomingPhoneNumbers/{twilio_phone_number_resource["sid"]}.json',
80+
headers=headers, auth=auth, data={
81+
'SmsUrl': f'https://trypromptly.com/api/apps/{app.uuid}/twiliosms/run',
82+
})
83+
if response.status_code != 200:
84+
raise Exception(
85+
f'Failed to update SMS webhook for phone number {phone_number}. Error: {response.text}')
86+
87+
return app

llmstack/client/src/components/apps/AppTwilioConfigEditor.jsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ const twilioConfigSchema = {
3030
type: "string",
3131
},
3232
},
33+
auto_create_sms_webhook: {
34+
type: "boolean",
35+
title: "Create Twilio SMS Webhook",
36+
description:
37+
"Update Twilio SMS Webhook to point to send message to application",
38+
},
3339
},
3440
};
3541

0 commit comments

Comments
 (0)