@ -0,0 +1,114 @@ | |||||
<?php | |||||
namespace App\Http\Controllers; | |||||
use Illuminate\Http\Request; | |||||
use DB, Twitter; | |||||
use \App\PhoneNumber; | |||||
class SMSController extends Controller | |||||
{ | |||||
/** | |||||
* Create a new controller instance. | |||||
* | |||||
* @return void | |||||
*/ | |||||
public function __construct() | |||||
{ | |||||
$this->middleware('auth'); | |||||
} | |||||
private function max_message_length($numbers) { | |||||
$max_name_length = 0; | |||||
foreach($numbers as $n) { | |||||
if(strlen($n->name) > $max_name_length) { | |||||
$max_name_length = strlen($n->name); | |||||
} | |||||
} | |||||
return 160 - $max_name_length - 1; | |||||
} | |||||
public function index() { | |||||
$this->authorize('admin'); | |||||
$numbers = PhoneNumber::orderBy('name')->get(); | |||||
$maxlen = $this->max_message_length($numbers); | |||||
return view('sms', [ | |||||
'numbers' => $numbers, | |||||
'maxlen' => $maxlen | |||||
]); | |||||
} | |||||
public function save(Request $request) { | |||||
$this->authorize('admin'); | |||||
if(preg_match_all('/(.+)\s+([0-9\-]+)/', $request->input('input'), $matches)) { | |||||
$contacts = []; | |||||
$errors = []; | |||||
foreach($matches[1] as $i=>$name) { | |||||
if(preg_match('/^1?(\d{3})-?(\d{3})-?(\d{4})$/', $matches[2][$i], $pm)) { | |||||
$contacts[] = [trim($name), $pm[1].'-'.$pm[2].'-'.$pm[3]]; | |||||
} else { | |||||
$errors[] = $matches[0][$i]; | |||||
} | |||||
} | |||||
if(count($errors)) { | |||||
$request->session()->flash('status', 'danger'); | |||||
$request->session()->flash('status-message', 'There was a problem with some of your entries. No numbers were changed. The lines below had errors:<br>'.implode('<br>', array_map('htmlspecialchars', $errors))); | |||||
} else { | |||||
DB::table('phone_numbers')->delete(); | |||||
foreach($contacts as $c) { | |||||
$p = new PhoneNumber; | |||||
$p->name = $c[0]; | |||||
$p->phone = $c[1]; | |||||
$p->save(); | |||||
} | |||||
$request->session()->flash('status', 'success'); | |||||
$request->session()->flash('status-message', 'Phone numbers saved!'); | |||||
} | |||||
} else { | |||||
$request->session()->flash('status', 'danger'); | |||||
$request->session()->flash('status-message', 'Invalid input, phone numbers were not modified'); | |||||
} | |||||
return redirect('sms'); | |||||
} | |||||
public function send(Request $request) { | |||||
$this->authorize('admin'); | |||||
$numbers = PhoneNumber::orderBy('name')->get(); | |||||
$maxlen = $this->max_message_length($numbers); | |||||
if(strlen($request->input('text')) <= $maxlen) { | |||||
$client = new \Twilio\Rest\Client(env('TWILIO_SID'), env('TWILIO_TOKEN')); | |||||
foreach($numbers as $number) { | |||||
$text = $request->input('text'); | |||||
$text = str_replace('{name}', $number->name, $text); | |||||
$message = $client->messages->create( | |||||
'+1'.str_replace('-','',$number->phone), | |||||
array( | |||||
'from' => env('TWILIO_NUMBER'), | |||||
'body' => $text | |||||
) | |||||
); | |||||
} | |||||
$request->session()->flash('status', 'success'); | |||||
$request->session()->flash('status-message', 'Message was sent! It will take about 1 second per number to deliver all the messages!'); | |||||
} else { | |||||
$request->session()->flash('status', 'danger'); | |||||
$request->session()->flash('status-message', 'Your message was too long!'); | |||||
} | |||||
return redirect('sms'); | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
<?php | |||||
namespace App; | |||||
use Illuminate\Database\Eloquent\Model; | |||||
class PhoneNumber extends Model | |||||
{ | |||||
protected $fillable = [ | |||||
'name', 'phone' | |||||
]; | |||||
} |
@ -0,0 +1,16 @@ | |||||
<?php | |||||
namespace App; | |||||
use Illuminate\Database\Eloquent\Model; | |||||
class SentMessage extends Model | |||||
{ | |||||
protected $fillable = [ | |||||
'text', 'phone' | |||||
]; | |||||
public function user() { | |||||
return $this->belongsTo('\App\User'); | |||||
} | |||||
} |
@ -0,0 +1,33 @@ | |||||
<?php | |||||
use Illuminate\Support\Facades\Schema; | |||||
use Illuminate\Database\Schema\Blueprint; | |||||
use Illuminate\Database\Migrations\Migration; | |||||
class BulkSMS extends Migration | |||||
{ | |||||
/** | |||||
* Run the migrations. | |||||
* | |||||
* @return void | |||||
*/ | |||||
public function up() | |||||
{ | |||||
Schema::create('phone_numbers', function (Blueprint $table) { | |||||
$table->increments('id'); | |||||
$table->timestamps(); | |||||
$table->string('name', 255); | |||||
$table->string('phone', 255); | |||||
}); | |||||
} | |||||
/** | |||||
* Reverse the migrations. | |||||
* | |||||
* @return void | |||||
*/ | |||||
public function down() | |||||
{ | |||||
Schema::dropIfExists('phone_numbers'); | |||||
} | |||||
} |
@ -0,0 +1,33 @@ | |||||
<?php | |||||
use Illuminate\Support\Facades\Schema; | |||||
use Illuminate\Database\Schema\Blueprint; | |||||
use Illuminate\Database\Migrations\Migration; | |||||
class SmsLog extends Migration | |||||
{ | |||||
/** | |||||
* Run the migrations. | |||||
* | |||||
* @return void | |||||
*/ | |||||
public function up() | |||||
{ | |||||
Schema::create('sent_messages', function (Blueprint $table) { | |||||
$table->increments('id'); | |||||
$table->timestamps(); | |||||
$table->text('text'); | |||||
$table->integer('user_id'); | |||||
}); | |||||
} | |||||
/** | |||||
* Reverse the migrations. | |||||
* | |||||
* @return void | |||||
*/ | |||||
public function down() | |||||
{ | |||||
Schema::dropIfExists('sent_messages'); | |||||
} | |||||
} |
@ -0,0 +1,46 @@ | |||||
@extends('layouts.app') | |||||
@section('content') | |||||
<div class="container"> | |||||
<div class="row"> | |||||
<div class="col-md-8 col-md-offset-2"> | |||||
@if(session('status')) | |||||
<div class="alert alert-{{ session('status') }}" role="alert">{!! session('status-message') !!}</div> | |||||
@endif | |||||
<form action="{{ route('sms-send') }}" method="post"> | |||||
<h3>Broadcast a Message</h3> | |||||
<p>Enter a message to send. The message length is limited based on the longest person's name. To include the person's name, enter the placeholder <code>{name}</code>.</p> | |||||
<div class="form-group"> | |||||
<textarea name="text" class="form-control" rows="2" maxlength="{{ $maxlen }}">Hi {name}!</textarea> | |||||
</div> | |||||
<button type="submit" class="btn btn-primary">Broadcast</button> | |||||
{{ csrf_field() }} | |||||
</form> | |||||
<form action="{{ route('sms-save') }}" method="post"> | |||||
<h3>Phone Numbers</h3> | |||||
<p>Enter your phone number database, one person per line. First name followed by their phone number. Numbers will be normalized to US format when saved.</p> | |||||
<div class="form-group"> | |||||
<textarea name="input" class="form-control" rows="8"> | |||||
@foreach($numbers as $number) | |||||
{{ $number->name }} {{ $number->phone }} | |||||
@endforeach</textarea> | |||||
</div> | |||||
<button type="submit" class="btn btn-primary">Save</button> | |||||
{{ csrf_field() }} | |||||
</form> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
@endsection |