Compare with Previous | Blame | View Log
#!/usr/local/bin/php -q
<?php
/**
* thebus.php - An Asterisk AGI script to drive a automated telephone bus schedule
* for Charlottetown, Prince Edward Island.
*
* There is a working demonstration of this code at +1 (902) 367-3694.
*
* Requirements:
*
* - Asterisk (obviously - http://asterisk.org)
* - PHPAGI (http://phpagi.sourceforge.net/)
* - MySQL (http://mysql.org/)
* - Cepstral TTS engine and voice (http://cepstral.com/)
* - SoX (http://sox.sourceforge.net/)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* @version 1.1, April 8, 2009
* @link http://ruk.ca/wiki/Charlottetown_Transit_Map
* @author Peter Rukavina <peter@rukavina.net>
* @copyright Reinvented Inc., 2009
* @license http://www.fsf.org/licensing/licenses/gpl.txt GNU Public License
*/
// -------------------------------------------
// User configurable settings
// -------------------------------------------
// MySQL Settings
// You can get the route data from SVN at http://svn.reinvented.net/CharlottetownTransitMap/routedata/
// Drop it into a MySQL database, and then update the settings for the database here.
$dbhost = "localhost"; // change this if your MySQL server is elsewhere
$dbuser = ""; // MySQL username
$dbpass = ""; // MySQL password
$dbase = "bus"; // change this if you use a database other than 'bus'
// Executables
// The script uses the low-cost Cepstral text to speech engine and voices.
// More information and purchase at http://cepstral.com/
$swiftbin = "/usr/local/bin/swift";
$swiftvoice = "Allison";
// The script uses SoX to convert WAV files into GSM files.
// More information at http://sox.sourceforge.net/
$soxbin = "/usr/bin/sox";
// -------------------------------------------
// End of user configurable settings
// -------------------------------------------
$starttime = mktime();
$nextstops = "Press " . GetStopsMenu(1);
system("$swiftbin -n $swiftvoice -p \"audio/channels=1,audio/sampling-rate=8000\" -o /tmp/thebus-menu.wav \"$nextstops\" ");
system("$soxbin /tmp/thebus-menu.wav /var/lib/asterisk/sounds/thebus-menu.gsm");
set_time_limit(0);
require('phpagi-2.14/phpagi.php');
$now = mktime();
$dow = strftime("%u",$now);
$agi = new AGI();
LogAction($agi,"ANSWER");
$agi->answer();
list($nextstops,$nextstopsin) = GetNextStops();
$agi->swift("Charlotte Town Bus Schedule at " . strftime("%I:%M %p"));
if ($dow == 7) {
$agi->swift("There is no bus service on Sundays. Service resumes Monday morning.");
$DONE = 1;
}
else if ($dow == 6) {
$agi->swift("This is Saturday: from 9:00 a.m. to mid-afternoon buses stop at the Charlottetown Farmer's Market in both directions.");
}
if ($dow != 7) {
if ($nextstopsin[1]['O']) {
$agi->swift("The next bus from Confederation Centre is " . $nextstopsin[1]['O'] . $nextstops[1]['O'] . ".");
LogAction($agi,"The next bus from Confederation Centre is " . $nextstopsin[1]['O'] . $nextstops[1]['O'] . ".");
}
if ($nextstopsin[8]['I']) {
$agi->swift("The next bus from Charlottetown Mall is " . $nextstopsin[8]['I'] .$nextstops[8]['I'] . ".");
LogAction($agi,"The next bus from Charlottetown Mall is " . $nextstopsin[8]['I'] .$nextstops[8]['I'] . ".");
}
if ((!$nextstopsin[8]['I']) and (!$nextstopsin[1]['O'])) {
$agi->swift("There are no more buses scheduled for today.");
LogAction($agi,"There are no more buses scheduled for today.");
$DONE = 1;
}
else {
$DONE = 0;
}
}
while (!$DONE) {
$stop = $agi->get_data('thebus-menu',3000,1);
if ($stop['data'] == 'timeout') {
$counter++;
}
else if ($stop['result']) {
$whatstop = $stop['result'];
LogAction($agi,"PRESSED $whatstop");
if (is_numeric($whatstop)) {
SayStops($agi,$whatstop);
$counter++;
}
else {
$agi->swift("Just press the number of the stop you want the schedule for. To speak with a real person, hang up and call Charlottetown Transit at 566-9962.");
}
}
if ($counter > 2) {
$DONE = 1;
}
}
$agi->swift("Thank you for using the bus schedule. Call again any time.");
LogAction($agi,"HANGUP");
LogAction($agi,"CALL LASTED " . (mktime() - $starttime) . " SECONDS");
$agi->hangup();
function SayStops($agi,$whatstop) {
$stopname = GetStopName($whatstop);
$agi->swift($stopname . ":");
LogAction($agi,$stopname . ":");
$out = GetStops(1,$whatstop,'O');
if ($out <> '') {
$agi->swift("Next stop out-bound from downtown " . $out);
LogAction($agi,"Next stop out-bound from downtown " . $out);
}
$in = GetStops(1,$whatstop,'I');
if ($in <> '') {
$agi->swift("Next stop in-bound to downtown " . $in);
LogAction($agi,"Next stop in-bound to downtown " . $in);
}
if (($in == '') and ($out == '')) {
$agi->swift("There are no more buses scheduled for today.");
LogAction($agi,"There are no more buses scheduled for today.");
}
}
function LogAction($agi,$action) {
ConnectToMYSQL();
$query = "INSERT into telephonelog (actiondate,uniqid,callerid,callername,action) values (
'" . strftime("%Y-%m-%d %H:%M:%S") . "',
'" . $agi->request['agi_uniqueid'] . "',
'" . addslashes($agi->request['agi_callerid']) . "',
'" . addslashes($agi->request['agi_calleridname']) . "',
'" . addslashes($action) . "')";
$result = MYSQL_QUERY($query);
$fp = fopen("/www/logs/thebus-telephone.log","a");
fwrite($fp,strftime("%Y-%m-%d %H:%M:%S") . "\t");
fwrite($fp,$agi->request['agi_uniqueid'] . "\t");
fwrite($fp,$agi->request['agi_callerid'] . "\t");
fwrite($fp,$agi->request['agi_calleridname'] . "\t");
fwrite($fp,$action . "\n");
fclose($fp);
}
function ordinal($n)
{
$ln = (int)substr($n, -1);
$sln = (int)substr($n, -2);
$r = array('st','nd','rd');
$es = (($sln < 11 || $sln > 19) && $ln > 0 && $ln < 4);
return $n . ($es ? $r[$ln - 1] : 'th');
}
function GetNextStops() {
ConnectToMYSQL();
$dow = strftime("%u");
$now = strftime("%H:%M:%S");
$nownumber = TimeAsNumber($now);
$query = "SELECT stopnumber,stoptime,schedule.inout from schedule,runs where
(runs.dow like '%$dow%') and
(schedule.run = runs.run) and
stoptime > '$now'
order by stoptime";
$result = MYSQL_QUERY($query);
$currentrecord = 0;
$howmanyrecords = MYSQL_NUMROWS($result);
$nextstop = array();
if ($howmanyrecords > 0) {
while ($currentrecord < $howmanyrecords) {
$stopnumber = mysql_result($result,$currentrecord,"stopnumber");
$stoptime = mysql_result($result,$currentrecord,"stoptime");
$inout = mysql_result($result,$currentrecord,"inout");
$stoptimeasnumber = TimeAsNumber($stoptime);
if ($nextstop[$stopnumber][$inout] == '') {
$nextstop[$stopnumber][$inout] = OClock($stoptimeasnumber);
$nextstopin[$stopnumber][$inout] = TimeDifference($nownumber,$stoptimeasnumber);
}
$currentrecord++;
}
}
return array($nextstop,$nextstopin);
}
function GetStopsMenu($routenumber) {
ConnectToMYSQL();
$query2 = "SELECT stopnumber,stopname from stops where telephonestop = 'Y' order by stopnumber";
$result2 = MYSQL_QUERY($query2);
$currentrecord2 = 0;
$howmanyrecords2 = MYSQL_NUMROWS($result2);
$outstring = "";
while ($currentrecord2 < $howmanyrecords2) {
$stopnumber = mysql_result($result2,$currentrecord2,"stopnumber");
$stopname = mysql_result($result2,$currentrecord2,"stopname");
$stopname = str_replace("/"," ",$stopname);
$stopname = str_replace("UPEI","U P E I",$stopname);
$stopname = str_replace("Coop","Co-op",$stopname);
$stopname = str_replace("Sobeys","Sobee's",$stopname);
$outstring .= "$stopnumber for $stopname, ";
$currentrecord2++;
}
return $outstring;
}
function GetStops($routenumber,$stopnumber,$direction) {
ConnectToMYSQL();
$now = strftime("%H:%M:%S");
$nownumber = TimeAsNumber($now);
$dow = strftime("%u");
$query2 = "SELECT stoptime,schedule.run from schedule,routes,runs where
(runs.dow like '%$dow%') and
(schedule.run = runs.run) and
(routes.routenumber = schedule.routenumber) and
(routes.operating = 1) and
stopnumber='$stopnumber' and
schedule.routenumber='$routenumber' and
`inout`='$direction' order by stoptime";
$result2 = MYSQL_QUERY($query2);
$currentrecord2 = 0;
$howmanyrecords2 = MYSQL_NUMROWS($result2);
$selectedcurrent = 0; // Flag to determine whether we've selected the "current" stop
$selectedthen = 0;
$outstring = "";
$counter = 0;
$maxstops = 5;
if ($howmanyrecords2 > 0) {
while ($currentrecord2 < $howmanyrecords2) {
$stoptime = mysql_result($result2,$currentrecord2,"stoptime");
$run = mysql_result($result2,$currentrecord2,"run");
$stoptimeasnumber = TimeAsNumber($stoptime);
if ($stoptimeasnumber >= $nownumber) {
if (!$selectedcurrent) {
$outstring .= TimeDifference($nownumber,$stoptimeasnumber);
$selectedcurrent = 1;
}
else if (!$selectedthen) {
$outstring .= " then at ";
$selectedthen = 1;
}
$outstring .= OClock($stoptimeasnumber);
$counter++;
}
if ($counter >= $maxstops) {
break;
}
$currentrecord2++;
}
if ($outstring <> '') {
if ($counter > 1) {
$outstring = substr($outstring,0,strlen($outstring)-2);
$lastcomma = strrpos($outstring,",");
$outstring = substr($outstring,0,$lastcomma) . " and " . substr($outstring,$lastcomma+1);
}
}
}
return $outstring;
}
function OClock($stoptimeasnumber) {
$outstring = '';
$minutes = intval(strftime("%M",$stoptimeasnumber));
if ($minutes == 0) {
$outstring .= strftime("%I:%M",$stoptimeasnumber) . " o'clock, ";
}
else {
$outstring .= strftime("%I:%M",$stoptimeasnumber) . ", ";
}
return $outstring;
}
function TimeDifference($now,$then) {
$outstring = '';
$difftime = ceil(($then - $now) / 60);
if ($difftime <= 60) {
$outstring .= " in $difftime minutes at ";
}
else {
$hours = intval($difftime / 60);
if ($hours == 1) {
$outstring .= " in $hours hour ";
}
else {
$outstring .= " in $hours hours ";
}
$minutes = $difftime - ($hours * 60);
if ($minutes > 0) {
$outstring .= " and $minutes minutes at ";
}
else {
$outstring .= " at ";
}
}
return $outstring;
}
function GetStopName($stopnumber) {
ConnectToMYSQL();
$outstring = '';
$query2 = "SELECT stopname from stops where stopnumber='$stopnumber'";
$result2 = MYSQL_QUERY($query2);
$currentrecord2 = 0;
$howmanyrecords2 = MYSQL_NUMROWS($result2);
if ($howmanyrecords2 > 0) {
$stopname = mysql_result($result2,$currentrecord2,"stopname");
$stopname = str_replace("/"," ",$stopname);
$stopname = str_replace("UPEI","U P E I",$stopname);
$stopname = str_replace("Coop","Co-op",$stopname);
$stopname = str_replace("Sobeys","Sobee's",$stopname);
$outstring .= $stopname;
}
return $outstring;
}
function TimeAsNumber($time) {
list($hour,$minute,$second) = split(":",$time);
$timeasnumber = mktime($hour,$minute,$second,1,1,2005); // The date is irrelevant here -- any will do
return $timeasnumber;
}
/**
* Format time.
* Accept a time like 13:50:00 and return it as 1:50 PM
* @param string $time Unformatted time.
* @return string Formatted time.
*/
function FormatTime($time) {
if ($time <> "") {
list($hours,$minutes,$seconds) = split(":",$time);
$timeout = strftime("%I:%M %p",mktime($hours,$minutes,0,1,1,2005));
if (substr($timeout,0,1) == "0") {
$timeout = substr($timeout,1);
}
}
else {
$timeout = "";
}
$timeout = str_replace(" AM","a",$timeout);
$timeout = str_replace(" PM","p",$timeout);
return $timeout;
}
function ConnectToMYSQL() {
global $dbhost,$dbuser,$dbpass,$dbase;
MYSQL_CONNECT($dbhost,$dbuser,$dbpass);
MYSQL_SELECT_DB( $dbase ) or die( "Unable to select database");
}