Subversion Repositories Charlottetown Transit Map

[/] [asterisk/] [thebus.php] - Rev 44

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 && $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");

}

Compare with Previous | Blame | View Log