4
0
mirror of https://github.com/cwinfo/envayasms.git synced 2025-07-03 21:57:43 +00:00

version 3.0 - real-time AMQP connections; change server API format from XML to JSON, update PHP server library; persistent storage of pending messages

This commit is contained in:
Jesse Young
2012-04-04 14:16:26 -07:00
parent f53ccc3cc9
commit 239ee1fd52
71 changed files with 3627 additions and 832 deletions

View File

@ -1,9 +1,33 @@
<?php
$PASSWORDS = array(
'16505551212' => 'rosebud',
'16505551213' => 's3krit',
);
$PHONE_NUMBERS = array_keys($PASSWORDS);
ini_set('display_errors','0');
$OUTGOING_DIR_NAME = __DIR__."/outgoing_sms";
/*
* This password must match the password in the EnvayaSMS app settings,
* otherwise example/www/gateway.php will return an "Invalid request signature" error.
*/
$PASSWORD = 'rosebud';
/*
* example/send_sms.php uses the local file system to queue outgoing messages
* in this directory.
*/
$OUTGOING_DIR_NAME = __DIR__."/outgoing_sms";
/*
* AMQP allows you to send outgoing messages in real-time (i.e. 'push' instead of polling).
* In order to use AMQP, you would need to install an AMQP server such as RabbitMQ, and
* also enter the AMQP connection settings in the app. (The settings in the EnvayaSMS app
* should use the same vhost and queue name, but may use a different host/port/user/password.)
*/
$AMQP_SETTINGS = array(
'host' => 'localhost',
'port' => 5672,
'user' => 'guest',
'password' => 'guest',
'vhost' => '/',
'queue_name' => "envayasms"
);

View File

@ -1,46 +1,33 @@
<?php
/*
* Command line script to simulate sending an outgoing SMS from the server.
* Command line script to send an outgoing SMS from the server.
*
* The message will be queued on the server until the next time
* EnvayaSMS checks for outgoing messages.
* This example script queues outgoing messages using the local filesystem.
* The messages are sent the next time EnvayaSMS sends an ACTION_OUTGOING request to www/gateway.php.
*/
require_once __DIR__."/config.php";
require_once dirname(__DIR__)."/EnvayaSMS.php";
$arg_len = sizeof($argv);
if ($arg_len == 4)
{
$from = $argv[1];
$to = $argv[2];
$message = $argv[3];
}
else if ($arg_len == 3)
if (sizeof($argv) == 3)
{
$from = $PHONE_NUMBERS[0];
$to = $argv[1];
$message = $argv[2];
$body = $argv[2];
}
else
{
error_log("Usage: php send_sms.php [<from>] <to> \"<message>\"");
error_log("Examples: ");
error_log(" php send_sms.php 16505551212 16504449876 \"hello world\"");
error_log("Usage: php send_sms.php <to> \"<message>\"");
error_log("Example: ");
error_log(" php send_sms.php 16504449876 \"hello world\"");
die;
}
$id = uniqid("");
$message = new EnvayaSMS_OutgoingMessage();
$message->id = uniqid("");
$message->to = $to;
$message->message = $body;
$filename = "$OUTGOING_DIR_NAME/$id.json";
file_put_contents($filename, json_encode(array(
'from' => $from,
'to' => $to,
'message' => $message,
'id' => $id
)));
echo "Message $id added to outgoing queue\n";
file_put_contents("$OUTGOING_DIR_NAME/{$message->id}.json", json_encode($message));
echo "Message {$message->id} added to filesystem queue\n";

View File

@ -0,0 +1,45 @@
<?php
/*
* Command line script to send an outgoing SMS from the server.
*
* Requires an AMQP server to be configured in config.php, and
* pushes SMS to the phone immediately using the real-time connection.
*/
require_once __DIR__."/config.php";
require_once dirname(__DIR__)."/EnvayaSMS.php";
require_once __DIR__."/php-amqplib/amqp.inc";
if (sizeof($argv) == 3)
{
$to = $argv[1];
$body = $argv[2];
}
else
{
error_log("Usage: php send_sms_amqp.php <to> \"<message>\"");
die;
}
$message = new EnvayaSMS_OutgoingMessage();
$message->id = uniqid("");
$message->to = $to;
$message->message = $body;
$conn = new AMQPConnection($AMQP_SETTINGS['host'], $AMQP_SETTINGS['port'],
$AMQP_SETTINGS['user'], $AMQP_SETTINGS['password'], $AMQP_SETTINGS['vhost']);
$ch = $conn->channel();
$ch->queue_declare($AMQP_SETTINGS['queue_name'], false, true, false, false);
$event = new EnvayaSMS_Event_Send(array($message));
$msg = new AMQPMessage($event->render(), array('content_type' => 'application/json', 'delivery-mode' => 2));
$ch->basic_publish($msg, '', $AMQP_SETTINGS['queue_name']);
$ch->close();
$conn->close();
echo "Message {$message->id} added to AMQP queue\n";

View File

@ -1,44 +1,38 @@
<?php
/*
* This example script implements the EnvayaSMS API.
*
* It sends an auto-reply to each incoming message, and sends outgoing SMS
* that were previously queued by example/send_sms.php .
*
* To use this file, set the URL to this file as as the the Server URL in the EnvayaSMS app.
* The password in the EnvayaSMS app settings must be the same as $PASSWORD in config.php.
*/
require_once dirname(__DIR__)."/config.php";
require_once dirname(dirname(__DIR__))."/EnvayaSMS.php";
ini_set('display_errors','0');
// this example implementation uses the filesystem to store outgoing SMS messages,
// but presumably a production implementation would use another storage method
$request = EnvayaSMS::get_request();
$phone_number = $request->phone_number;
header("Content-Type: {$request->get_response_type()}");
$password = @$PASSWORDS[$phone_number];
header("Content-Type: text/xml");
if (!isset($password) || !$request->is_validated($password))
if (!$request->is_validated($PASSWORD))
{
header("HTTP/1.1 403 Forbidden");
error_log("Invalid request signature");
echo EnvayaSMS::get_error_xml("Invalid request signature");
error_log("Invalid password");
echo $request->render_error_response("Invalid password");
return;
}
// append to EnvayaSMS app log
$app_log = $request->log;
if ($app_log)
{
$log_file = dirname(__DIR__)."/log/sms_".preg_replace('#[^\w]#', '', $request->phone_number).".log";
$f = fopen($log_file, "a");
fwrite($f, $app_log);
fclose($f);
}
$action = $request->get_action();
switch ($action->type)
{
case EnvayaSMS::ACTION_INCOMING:
// Send an auto-reply for each incoming message.
$type = strtoupper($action->message_type);
error_log("Received $type from {$action->from}");
@ -62,25 +56,29 @@ switch ($action->type)
$reply->message = "You said: {$action->message}";
error_log("Sending reply: {$reply->message}");
echo $action->get_response_xml(array($reply));
echo $request->render_response(array(
new EnvayaSMS_Event_Send(array($reply))
));
return;
case EnvayaSMS::ACTION_OUTGOING:
$messages = array();
// In this example implementation, outgoing SMS messages are queued
// on the local file system by send_sms.php.
$dir = opendir($OUTGOING_DIR_NAME);
while ($file = readdir($dir))
{
if (preg_match('#\.json$#', $file))
{
$data = json_decode(file_get_contents("$OUTGOING_DIR_NAME/$file"), true);
if ($data && @$data['from'] == $phone_number)
if ($data)
{
$sms = new EnvayaSMS_OutgoingMessage();
$sms->id = $data['id'];
$sms->to = $data['to'];
$sms->from = $data['from'];
$sms->message = $data['message'];
$messages[] = $sms;
}
@ -88,33 +86,40 @@ switch ($action->type)
}
closedir($dir);
echo $action->get_response_xml($messages);
$events = array();
if ($messages)
{
$events[] = new EnvayaSMS_Event_Send($messages);
}
echo $request->render_response($events);
return;
case EnvayaSMS::ACTION_SEND_STATUS:
$id = $action->id;
error_log("message $id status: {$action->status}");
// delete file with matching id
if (preg_match('#^\w+$#', $id) && unlink("$OUTGOING_DIR_NAME/$id.json"))
if (preg_match('#^\w+$#', $id))
{
echo EnvayaSMS::get_success_xml();
}
else
{
header("HTTP/1.1 404 Not Found");
echo EnvayaSMS::get_error_xml("Invalid id");
}
unlink("$OUTGOING_DIR_NAME/$id.json");
}
echo $request->render_response();
return;
case EnvayaSMS::ACTION_DEVICE_STATUS:
error_log("device_status = {$action->status}");
echo EnvayaSMS::get_success_xml();
return;
echo $request->render_response();
return;
case EnvayaSMS::ACTION_TEST:
echo EnvayaSMS::get_success_xml();
return;
echo $request->render_response();
return;
default:
header("HTTP/1.1 404 Not Found");
echo EnvayaSMS::get_error_xml("Invalid action");
echo $request->render_error_response("The server does not support the requested action.");
return;
}

View File

@ -0,0 +1,87 @@
<?php
/*
* An example implementation of the EnvayaSMS server API that uses AMQP
* to send outgoing messages in real-time, intended to be used together with
* example/send_sms_amqp.php.
*
* To use this file, set the URL to this file as as the the Server URL in the EnvayaSMS app.
* The password in the EnvayaSMS app settings must be the same as $PASSWORD in config.php.
*/
require_once dirname(__DIR__)."/config.php";
require_once dirname(dirname(__DIR__))."/EnvayaSMS.php";
$request = EnvayaSMS::get_request();
header("Content-Type: {$request->get_response_type()}");
if (!$request->is_validated($PASSWORD))
{
header("HTTP/1.1 403 Forbidden");
error_log("Invalid password");
echo $request->render_error_response("Invalid password");
return;
}
$action = $request->get_action();
switch ($action->type)
{
case EnvayaSMS::ACTION_INCOMING:
// Doesn't do anything with incoming messages
error_log("Received {$action->message_type} from {$action->from}: {$action->message}");
echo $request->render_response();
return;
case EnvayaSMS::ACTION_OUTGOING:
// Doesn't need to do anything when polling for outgoing messages
// since they should be sent via the AMQP connection.
// Optionally, you could use AMQP basic_get to retrieve any messages
// from the AMQP queue so that it works in both polling and push modes.
error_log("No messages here, use AMQP instead");
echo $request->render_response(array(
new EnvayaSMS_Event_Log("No messages via polling, use AMQP instead")
));
return;
case EnvayaSMS::ACTION_AMQP_STARTED:
// The main point of this action is to allow the server to kick off old
// AMQP connections (that weren't closed properly) before their heartbeat timeout
// expires. This makes it possible to use long heartbeat timeouts to maximize
// the phone's battery life.
// With RabbitMQ, this can be done using the management API:
// GET /queues/VHOST/QUEUE_NAME
// to get the connection name for each consumer other than the current one
// DELETE /connections/CONNECTION_NAME
// to close the connection for each consumer other than the current one
error_log("AMQP connection started with consumer tag {$action->consumer_tag}");
echo $request->render_response();
return;
case EnvayaSMS::ACTION_SEND_STATUS:
error_log("message {$action->id} status: {$action->status}");
echo $request->render_response();
return;
case EnvayaSMS::ACTION_DEVICE_STATUS:
error_log("device_status = {$action->status}");
echo $request->render_response();
return;
case EnvayaSMS::ACTION_TEST:
echo $request->render_response();
return;
default:
header("HTTP/1.1 404 Not Found");
echo $request->render_error_response("The server does not support the requested action.");
return;
}

View File

@ -31,13 +31,26 @@ body
<tr><th>Phone Number</th><td><input id='phone_number' type='text' /></td></tr>
<tr><th>Password</th><td><input id='password' type='password' /></td></tr>
<tr><th>Log Messages</th><td><textarea id='log' style='width:250px'></textarea></td></tr>
<tr><th>Send Limit</th><td><input id='send_limit' value='100' type='text' /></td></tr>
<tr><th>Settings Version</th><td><input id='settings_version' value='1' type='text' /></td></tr>
<tr><th>Battery</th><td><input id='battery' value='100' type='text' /></td></tr>
<tr><th>Power Source</th><td><select id='power'>
<option value='0'>0 (battery)</option>
<option value='1'>1 (USB)</option>
<option value='2'>2 (AC)</option>
</select></td></tr>
<tr><th>Network Type</th><td><input id='network' value='WIFI' type='text' /></td></tr>
<tr><th>Current Timestamp</th><td><input id='now' type='text' /></td></tr>
<tr><th>Action</th><td><select id='action' onchange='actionChanged()' onkeypress='actionChanged()'>
<option value='incoming'>incoming</option>
<option value='outgoing'>outgoing</option>
<option value='send_status'>send_status</option>
<option value='device_status'>device_status</option>
<option value='test'>test</option>
<option value='amqp_started'>amqp_started</option>
</select></td></tr>
</table>
<div id='action_incoming'>
@ -47,6 +60,7 @@ body
<tr><th>Message Type</th><td><select id='message_type'>
<option value='sms'>sms</option>
<option value='mms'>mms</option>
<option value='call'>call</option>
</select></td></tr>
<tr><th>Message</th><td><textarea id='message' style='width:250px'></textarea></td></tr>
<tr><th>Timestamp</th><td><input id='timestamp' type='text' /></td></tr>
@ -64,6 +78,7 @@ body
<option value='sent'>sent</option>
<option value='failed'>failed</option>
<option value='queued'>queued</option>
<option value='cancelled'>cancelled</option>
</select></td></tr>
<tr><th>Error Message</th><td><input id='error' type='text' size='50' /></td></tr>
</table>
@ -80,10 +95,16 @@ body
<option value='power_disconnected'>power_disconnected</option>
<option value='battery_low'>battery_low</option>
<option value='battery_okay'>battery_okay</option>
<option value='send_limit_exceeded'>send_limit_exceeded</option>
</select></td></tr>
</table>
</div>
<div id='action_amqp_started' style='display:none'>
<h4>Parameters for action=amqp_started:</h4>
<table class='smsTable'>
<tr><th>Consumer Tag</th><td><input id='consumer_tag' type='text' /></td></tr>
</table>
</div>
<script type='text/javascript'>
@ -110,11 +131,17 @@ function performAction() {
var action = $('action').value;
var params = {
version: '13',
version: '29',
phone_number: $('phone_number').value,
action: action,
log: $('log').value
};
log: $('log').value,
send_limit: $('send_limit').value,
settings_version: $('settings_version').value,
battery: $('battery').value,
power: $('power').value,
network: $('network').value,
now: $('now').value
};
if (action == 'incoming')
{
@ -133,6 +160,10 @@ function performAction() {
{
params.status = $('device_status').value;
}
else if (action == 'amqp_started')
{
params.status = $('consumer_tag').value;
}
var xhr = (window.ActiveXObject && !window.XMLHttpRequest) ? new ActiveXObject("Msxml2.XMLHTTP") : new XMLHttpRequest();
@ -189,8 +220,9 @@ function performAction() {
xhr.send(paramStr);
}
$('server_url').value = location.href.replace("test.html","");
$('server_url').value = location.href.replace("test.html","gateway.php");
$('timestamp').value = new Date().getTime();
$('now').value = new Date().getTime();
</script>