2
2
import urllib .parse
3
3
from string import digits
4
4
5
+ from django .apps import apps
6
+ from django .db .models import F , Q
5
7
from phonenumbers import COUNTRY_CODE_TO_REGION_CODE
6
8
from twilio .base .exceptions import TwilioRestException
7
9
from twilio .rest import Client
@@ -95,9 +97,10 @@ def finish_verification(self, number: str, code: str):
95
97
normalized_number , _ = self ._normalize_phone_number (number )
96
98
if normalized_number :
97
99
try :
98
- verification_check = self ._twilio_api_client .verify .services (
99
- live_settings .TWILIO_VERIFY_SERVICE_SID
100
- ).verification_checks .create (to = normalized_number , code = code )
100
+ client , verify_service_sid = self ._verify_sender (number )
101
+ verification_check = client .verify .services (verify_service_sid ).verification_checks .create (
102
+ to = normalized_number , code = code
103
+ )
101
104
logger .info (f"TwilioPhoneProvider.finish_verification: verification_status { verification_check .status } " )
102
105
if verification_check .status == "approved" :
103
106
return normalized_number
@@ -133,46 +136,45 @@ def _message_to_twiml(self, message: str, with_gather=False):
133
136
)
134
137
135
138
def _call_create (self , twiml_query : str , to : str , with_callback : bool ):
139
+ client , from_ = self ._phone_sender (to )
136
140
url = "http://twimlets.com/echo?Twiml=" + twiml_query
137
141
if with_callback :
138
142
status_callback = get_call_status_callback_url ()
139
143
status_callback_events = ["initiated" , "ringing" , "answered" , "completed" ]
140
- return self . _twilio_api_client .calls .create (
144
+ return client .calls .create (
141
145
url = url ,
142
146
to = to ,
143
- from_ = self . _twilio_number ,
147
+ from_ = from_ ,
144
148
method = "GET" ,
145
149
status_callback = status_callback ,
146
150
status_callback_event = status_callback_events ,
147
151
status_callback_method = "POST" ,
148
152
)
149
153
else :
150
- return self . _twilio_api_client .calls .create (
154
+ return client .calls .create (
151
155
url = url ,
152
156
to = to ,
153
- from_ = self . _twilio_number ,
157
+ from_ = from_ ,
154
158
method = "GET" ,
155
159
)
156
160
157
161
def _messages_create (self , number : str , text : str , with_callback : bool ):
162
+ client , from_ = self ._sms_sender (number )
158
163
if with_callback :
159
164
status_callback = get_sms_status_callback_url ()
160
- return self ._twilio_api_client .messages .create (
161
- body = text , to = number , from_ = self ._twilio_number , status_callback = status_callback
162
- )
165
+ return client .messages .create (body = text , to = number , from_ = from_ , status_callback = status_callback )
163
166
else :
164
- return self . _twilio_api_client .messages .create (
167
+ return client .messages .create (
165
168
body = text ,
166
169
to = number ,
167
- from_ = self . _twilio_number ,
170
+ from_ = from_ ,
168
171
)
169
172
170
173
def _send_verification_code (self , number : str , via : str ):
171
174
# https://www.twilio.com/docs/verify/api/verification?code-sample=code-start-a-verification-with-sms&code-language=Python&code-sdk-version=6.x
172
175
try :
173
- verification = self ._twilio_api_client .verify .services (
174
- live_settings .TWILIO_VERIFY_SERVICE_SID
175
- ).verifications .create (to = number , channel = via )
176
+ client , verify_service_sid = self ._verify_sender (number )
177
+ verification = client .verify .services (verify_service_sid ).verifications .create (to = number , channel = via )
176
178
logger .info (f"TwilioPhoneProvider._send_verification_code: verification status { verification .status } " )
177
179
except TwilioRestException as e :
178
180
logger .error (f"Twilio verification start error: { e } to number { number } " )
@@ -202,7 +204,7 @@ def _normalize_phone_number(self, number: str):
202
204
# Use responsibly
203
205
def _parse_number (self , number : str ):
204
206
try :
205
- response = self ._twilio_api_client .lookups .phone_numbers (number ).fetch ()
207
+ response = self ._default_twilio_api_client .lookups .phone_numbers (number ).fetch ()
206
208
return True , response .phone_number , self ._get_calling_code (response .country_code )
207
209
except TwilioRestException as e :
208
210
if e .code == 20404 :
@@ -217,24 +219,50 @@ def _parse_number(self, number: str):
217
219
return False , None , None
218
220
219
221
@property
220
- def _twilio_api_client (self ):
222
+ def _default_twilio_api_client (self ):
221
223
if live_settings .TWILIO_API_KEY_SID and live_settings .TWILIO_API_KEY_SECRET :
222
224
return Client (
223
225
live_settings .TWILIO_API_KEY_SID , live_settings .TWILIO_API_KEY_SECRET , live_settings .TWILIO_ACCOUNT_SID
224
226
)
225
227
else :
226
228
return Client (live_settings .TWILIO_ACCOUNT_SID , live_settings .TWILIO_AUTH_TOKEN )
227
229
230
+ @property
231
+ def _default_twilio_number (self ):
232
+ return live_settings .TWILIO_NUMBER
233
+
234
+ def _twilio_sender (self , sender_type , to ):
235
+ _ , _ , country_code = self ._parse_number (to )
236
+ TwilioSender = apps .get_model ("twilioapp" , sender_type )
237
+ sender = (
238
+ TwilioSender .objects .filter (Q (country_code = country_code ) | Q (country_code__isnull = True ))
239
+ .order_by (F ("country_code" ).desc (nulls_last = True ))
240
+ .first ()
241
+ )
242
+
243
+ if sender :
244
+ return sender .account .get_twilio_api_client (), sender
245
+
246
+ return self ._default_twilio_api_client , None
247
+
248
+ def _sms_sender (self , to ):
249
+ client , sender = self ._twilio_sender ("TwilioSmsSender" , to )
250
+ return client , sender .sender if sender else self ._default_twilio_number
251
+
252
+ def _phone_sender (self , to ):
253
+ client , sender = self ._twilio_sender ("TwilioPhoneCallSender" , to )
254
+ return client , sender .number if sender else self ._default_twilio_number
255
+
256
+ def _verify_sender (self , to ):
257
+ client , sender = self ._twilio_sender ("TwilioVerificationSender" , to )
258
+ return client , sender .verify_service_sid if sender else live_settings .TWILIO_VERIFY_SERVICE_SID
259
+
228
260
def _get_calling_code (self , iso ):
229
261
for code , isos in COUNTRY_CODE_TO_REGION_CODE .items ():
230
262
if iso .upper () in isos :
231
263
return code
232
264
return None
233
265
234
- @property
235
- def _twilio_number (self ):
236
- return live_settings .TWILIO_NUMBER
237
-
238
266
def _escape_call_message (self , message ):
239
267
# https://www.twilio.com/docs/api/errors/12100
240
268
message = message .replace ("&" , "&" )
0 commit comments