@ -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 |