Subversion Repositories Plazes

[/] [NokiaChat/] [plazes_xmpp_daemon.php] - Rev 145

Compare with Previous | Blame | View Log

<?php
/**
  * plazes_xmpp_daemon.php - A PHP script that accepts locations from Nokia Chat and returns nearby Plazes.
  *
  * Nokia Chat (http://www.nokia.com/betalabs/chat) is a chat application for
  * Series 60 mobile devices that speaks XMPP (Jabber).  It supports sending
  * location information using the XEP-0080: User Location protocol extension
  * (http://www.xmpp.org/extensions/xep-0080.html), sending the current latitude
  * and longitude (from the internal GPS) to contacts.
  *
  * This script is a 'bot' that signs on to Nokia Chat (which requires a Nokia
  * Chat account, which you need to sign up for on-device) and then responds
  * to received locations by returning nearby Plazes from Plazes.net.
  * 
  * Requirements for use:
  * 
  *  - PHP 5 or greater
  *  - XMPPHP (http://code.google.com/p/xmpphp/)
  *  - Nokia Chat account
  *
  * This script borrows heavily from Laconia (http://laconi.ca/), with thanks.
  *     
  * EXAMPLE USAGE
  *
  *  php plazes_xmpp_daemon.php > debug.log
  *
  * KNOWN ISSUES
  *
  *  - After a while the script disconnects from Nokia Chat, and although
  *    the script keeps running, and no errors are generated, it needs
  *    to be restarted to reconnect.  I'm sure there's a simple solution
  *    for this.
  *
  *  - This is a rough proof-of-concept hack, not intended for production use.
  *
  * 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 0.1, July 27, 2008
  * @link http://ruk.ca/article/4915 Nokia Chat + Robot + Plazes (original blog post)
  * @link http://plazes.net/doc Plazes.net API Documentation
  * @link http://ruk.ca/article/4910 Inside Nokia Chat’s XMPP
  * @author Peter Rukavina <peter@rukavina.net>
  * @copyright Copyright &copy; 2008, Reinvented Inc.
  * @license http://www.fsf.org/licensing/licenses/gpl.txt GNU Public License
  */

// User configurable options
$ovi_login '';                // Your Nokia Chat username (without the @ovi.com)
$ovi_password '';                // Your Nokia Chat password
include("XMPPHP/XMPP.php");        // Where is the XMPP.php class?  Change this, if required, to point to the right place.
// End of User configurable options

$ovi_host "ovi.com";
$ovi_username $ovi_login "@" $ovi_host;

// Make sure the script never times out (may not actually be required for a command line script).
set_time_limit(0);

// Extend the stock XMPPHP_XMPP class, as we need to enhance the presence_handler
// and message_handler functions.
class NOKIACHAT_XMPP extends XMPPHP_XMPP {

    
/**
     * Presence handler
     *
     * Modified to only allow @ovi.com auto-subscriptions, and to send instructions on subscribe.
     *
     * @param string $xml
     */
    
public function presence_handler($xml) {
        
$payload['type'] = (isset($xml->attrs['type'])) ? $xml->attrs['type'] : 'available';
        
$payload['show'] = (isset($xml->sub('show')->data)) ? $xml->sub('show')->data $payload['type'];
        
$payload['from'] = $xml->attrs['from'];
        
$payload['status'] = (isset($xml->sub('status')->data)) ? $xml->sub('status')->data '';
        
$this->log->log("Presence: {$payload['from']} [{$payload['show']}] {$payload['status']}",  XMPPHP_Log::LEVEL_DEBUG);
        if(
$xml->attrs['type'] == 'subscribe') {
            if(
$this->auto_subscribe) {
                if (
preg_match("/.*@ovi\.com/",$xml->attrs['from'])) {
                    
$this->send("<presence type='subscribed' to='{$xml->attrs['from']}' from='{$this->fulljid}' /><presence type='subscribe' to='{$xml->attrs['from']}' from='{$this->fulljid}' />");
                    
$this->send("<message to='{$xml->attrs['from']}' from='{$this->fulljid}'><body>This is the Plazes robot for Nokia Chat.  Turn on your GPS and use the Nokia Chat 'send location' option to send me your location and I'll send you back nearby Plazes.</body></message>");
                }
                else {
                    
$this->send("<message to='{$xml->attrs['from']}' from='{$this->fulljid}'><body>This is the Plazes robot for Nokia Chat.  You need a Nokia Chat account to use it.  See http://www.nokia.com/betalabs/chat for details and download.</body></message>");
                }
            }
            
$this->event('subscription_requested'$payload);
        } elseif(
$xml->attrs['type'] == 'subscribed') {
            
$this->event('subscription_accepted'$payload);
        } else {
            
$this->event('presence'$payload);
        }
    }

    
/**
     * Message handler
     * 
     * The Nokia Chat 'location' data message comes in like this:
     *
     * <message from='ruk@ovi.com/20c1' to='reinvented@ovi.com/xmpphp'>
     * <body>This message contains a location.</body>
     * <geoloc xmlns='http://jabber.org/protocol/geoloc' xml:lang='en'><lat>46.235961937499</lat><lon>-63.129662276767 </lon></geoloc>
     * <x xmlns='jabber:x:oob'><url>http://www.mapquest.com/maps/map.adp?latlongtype=decimal&amp;latitude=46.235961937499&amp;longitude=-63.129662276767</url></x>
     * </message>
     *
     * @param string $xml
     */
    
public function message_handler($xml) {
        if(isset(
$xml->attrs['type'])) {
            
$payload['type'] = $xml->attrs['type'];
        } else {
            
$payload['type'] = 'chat';
        }
        
$payload['from'] = $xml->attrs['from'];
        
$payload['body'] = $xml->sub('body')->data;
        
// If we find a 'geoloc' element, there's a location included in the message, and we parse it out...
        
if ($xml->sub('geoloc')) {
            
$payload['latitude'] = $xml->sub('geoloc')->sub('lat')->data;
            
$payload['longitude'] = $xml->sub('geoloc')->sub('lon')->data;
            
$this->locations[$payload['from']] = array($payload['latitude'],$payload['longitude']);
            
$payload['responses'][] = "<body>Okay, I've got your location.  Now send me a search keyword, like: 'restaurant', 'coffee shop', or 'hotel', and I'll find Plazes nearby.  Send 'help' for more details.</body>";
        }
        
$this->log->log("Message: {$xml->sub('body')->data}"XMPPHP_Log::LEVEL_DEBUG);
        
$this->event('message'$payload);
    }

}

// Create a new XMPP connection, connecting to the Nokia Chat account that will
// run everything for us.
$conn = new NOKIACHAT_XMPP('chat.ovi.com'5222$ovi_login$ovi_password'plazes_xmpp_daemon'$ovi_hosttrue99);
$conn->connect();

// When contacts request a subscription, automatically allow it rather than requiring confirmation.
$conn->autoSubscribe();

// Start a timer.
$timer time();

// Just hang around forever (probably a better way to do this?)
while(!$DONE) {

    
// Send a presence update every 60 seconds.
    // This may or may not be required -- I added it because the script
    // seemed to just suddenly disconnect without cause and I thought
    // this might help.  It seems that maybe it actually doesn't?
    
$now time();
    if ((
$now $timer) > 60) {
        
$conn->send("<presence><c xmlns='http://jabber.org/protocol/caps' node='http://chat.ovi.com/caps' ver='1.0' ext='mobi loc-1 vim-1'/><priority>1</priority><status>Send me your location from Nokia Chat</status></presence>");
        
$timer time();
    }

    
// This is ripped right out of Laconia (http://laconi.ca/), but heavily modified to suit.
    // There's a lot of debugging output here that could be handled more elegantly.
    
$payloads $conn->processUntil(array('message''presence''end_stream''session_start'));
    foreach(
$payloads as $event) {
        
$pl $event[1];
        switch(
$event[0]) {
            case 
'message':
                print 
"---------------------------------------------------------------------------------\n";
                print 
"Message from: {$pl['from']}\n";
                if(
$pl['subject']) print "Subject: {$pl['subject']}\n";
                print  
$pl['body'] . "\n";
                print 
"---------------------------------------------------------------------------------\n";
                print 
print_r($event,1);
                print 
"---------------------------------------------------------------------------------\n";
                if (
$pl['responses']) {
                    foreach(
$pl['responses'] as $key => $r) {
                        
$conn->send("<message from=\"$ovi_username\" to=\"" $pl['from'] . "\" type=\"" $pl['type'] . "\">" $r "</message>");
                        
sleep(1);
                    }
                }
                else if ((
$conn->locations[$pl['from']]) and (preg_match("/help/i",$pl['body']))) {
                    
$conn->send("<message to='" $pl['from'] . "' from='$ovi_username'><body>Send the name of a Plaze, or a category keyword: airport, bar, club, coffee shop, conference, home, hotel, landmark, movie theater, museum, office, railway_station, restaurant, shop, stadium, theater, university.</body></message>");
                }
                
// If we know where we are, then we can look up Plazes using the keyword we received.
                
else if ($conn->locations[$pl['from']]) {
                    
$url "http://plazes.net/plazes.xml?q=" urlencode($pl['body']) . "&near=" $conn->locations[$pl['from']][0] . "," $conn->locations[$pl['from']][1];
                    
$fp fopen($url,"r");
                    
$plazesxml stream_get_contents($fp);
                    
fclose($fp);
                    
$plazes = new SimpleXMLElement($plazesxml);

                    if (!
sizeof($plazes)) {
                        
$conn->send("<message from=\"$ovi_username\" to=\"" $pl['from'] . "\" type=\"" $pl['type'] . "\"><body>No matching Plazes found around your location.  Sorry.</body></message>");
                    }
                    else {
                        
$counter '';
                        
$responses = array();
                        
$responses[] = '<body>Here are the closest matching Plazes to you:</body>';

                        foreach(
$plazes->plaze as $plaze) {
                            if (
$counter <= 2) {
                                print 
"PLAZE FOUND: " print_r($plaze,1) . "\n";
                                
$plazename htmlentities($plaze->name);
                                
$responses[] = "<body><![CDATA[" $plazename " http://plazes.com/plazes/" strval($plaze->id) . "]]></body>";
                                
$responses[] = "<geoloc xmlns='http://jabber.org/protocol/geoloc' xml:lang='en'><lat>" strval($plaze->latitude) . "</lat><lon>" strval($plaze->longitude) . "</lon></geoloc>";
                            }
                            
$counter++;
                        }
                        foreach(
$responses as $key => $r) {
                            
$conn->send("<message from=\"$ovi_username\" to=\"" $pl['from'] . "\" type=\"" $pl['type'] . "\">" $r "</message>");
                            
// Nokia Chat got confused unless I inserted a 1 second delay between messages.
                            
sleep(1);
                        }
                    }
                }
                else {
                    
$conn->send("<message to='" $pl['from'] . "' from='$ovi_username'><body>This is the Plazes robot for Nokia Chat. To start, turn on your GPS and use the Nokia Chat 'send location' option to send me your location.</body></message>");
                }
                break;
            case 
'presence':
                print 
"Presence: {$pl['from']} [{$pl['show']}] {$pl['status']}\n";
                break;
            case 
'subscription_requested':
                print 
"Subscription requested: {$pl['from']} [{$pl['show']}] {$pl['status']}\n";
                break;
            case 
'session_start':
                
$conn->send("<presence><c xmlns='http://jabber.org/protocol/caps' node='http://chat.ovi.com/caps' ver='1.0' ext='mobi loc-1 vim-1'/><priority>1</priority><status>Send me your location from Nokia Chat</status></presence>");
                break;
        }
    }
}

Compare with Previous | Blame | View Log