93 lines
3.5 KiB
Python
93 lines
3.5 KiB
Python
def bulk_send_sms(request):
|
|
"""
|
|
Sends bulk SMS to selected voters using Twilio API.
|
|
"""
|
|
if request.method != 'POST':
|
|
return redirect('voter_advanced_search')
|
|
|
|
selected_tenant_id = request.session.get("tenant_id")
|
|
if not selected_tenant_id:
|
|
messages.warning(request, "Please select a campaign first.")
|
|
return redirect("index")
|
|
|
|
tenant = get_object_or_404(Tenant, id=selected_tenant_id)
|
|
settings = getattr(tenant, 'settings', None)
|
|
if not settings:
|
|
messages.error(request, "Campaign settings not found.")
|
|
return redirect('voter_advanced_search')
|
|
|
|
account_sid = settings.twilio_account_sid
|
|
auth_token = settings.twilio_auth_token
|
|
from_number = settings.twilio_from_number
|
|
|
|
if not account_sid or not auth_token or not from_number:
|
|
messages.error(request, "Twilio configuration is incomplete in Campaign Settings.")
|
|
return redirect('voter_advanced_search')
|
|
|
|
message_body = request.POST.get('message_body')
|
|
if not message_body:
|
|
messages.error(request, "Message body cannot be empty.")
|
|
return redirect('voter_advanced_search')
|
|
|
|
select_all_results = request.POST.get('select_all_results') == 'true'
|
|
|
|
if select_all_results:
|
|
voters, _ = get_filtered_voter_queryset(request, tenant, data_source='POST')
|
|
voters = voters.filter(phone_type='cell').exclude(phone='')
|
|
else:
|
|
voter_ids = request.POST.getlist('selected_voters')
|
|
voters = Voter.objects.filter(tenant=tenant, id__in=voter_ids, phone_type='cell').exclude(phone='')
|
|
|
|
if not voters.exists():
|
|
messages.warning(request, "No voters with a valid cell phone number were selected.")
|
|
return redirect('voter_advanced_search')
|
|
|
|
success_count = 0
|
|
fail_count = 0
|
|
|
|
auth_str = f"{account_sid}:{auth_token}"
|
|
auth_header = base64.b64encode(auth_str.encode()).decode()
|
|
url = f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Messages.json"
|
|
|
|
interaction_type, _ = InteractionType.objects.get_or_create(tenant=tenant, name="SMS Text")
|
|
|
|
for voter in voters:
|
|
digits = re.sub(r'\D', '', str(voter.phone))
|
|
if len(digits) == 10:
|
|
to_number = f"+1{digits}"
|
|
elif len(digits) == 11 and digits.startswith('1'):
|
|
to_number = f"+{digits}"
|
|
else:
|
|
fail_count += 1
|
|
continue
|
|
|
|
data_dict = {
|
|
'To': to_number,
|
|
'From': from_number,
|
|
'Body': message_body
|
|
}
|
|
data = urllib.parse.urlencode(data_dict).encode()
|
|
|
|
req = urllib.request.Request(url, data=data, method='POST')
|
|
req.add_header("Authorization", f"Basic {auth_header}")
|
|
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=10) as response:
|
|
if response.status in [200, 201]:
|
|
success_count += 1
|
|
Interaction.objects.create(
|
|
voter=voter,
|
|
type=interaction_type,
|
|
date=timezone.now(),
|
|
description='Mass SMS Text',
|
|
notes=message_body
|
|
)
|
|
else:
|
|
fail_count += 1
|
|
except Exception as e:
|
|
logger.error(f"Error sending SMS to {voter.phone}: {e}")
|
|
fail_count += 1
|
|
|
|
messages.success(request, f"Bulk SMS process completed: {success_count} successful, {fail_count} failed/skipped.")
|
|
return redirect('voter_advanced_search')
|