Subversion Repositories Plazes

[/] [NokiaChat/] [plazes_xmpp_daemon.php] - Blame information for rev 145

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 145 peter
<?php
2
/**
3
  * plazes_xmpp_daemon.php - A PHP script that accepts locations from Nokia Chat and returns nearby Plazes.
4
  *
5
  * Nokia Chat (http://www.nokia.com/betalabs/chat) is a chat application for
6
  * Series 60 mobile devices that speaks XMPP (Jabber).  It supports sending
7
  * location information using the XEP-0080: User Location protocol extension
8
  * (http://www.xmpp.org/extensions/xep-0080.html), sending the current latitude
9
  * and longitude (from the internal GPS) to contacts.
10
  *
11
  * This script is a 'bot' that signs on to Nokia Chat (which requires a Nokia
12
  * Chat account, which you need to sign up for on-device) and then responds
13
  * to received locations by returning nearby Plazes from Plazes.net.
14
  * 
15
  * Requirements for use:
16
  * 
17
  *  - PHP 5 or greater
18
  *  - XMPPHP (http://code.google.com/p/xmpphp/)
19
  *  - Nokia Chat account
20
  *
21
  * This script borrows heavily from Laconia (http://laconi.ca/), with thanks.
22
  *     
23
  * EXAMPLE USAGE
24
  *
25
  *  php plazes_xmpp_daemon.php > debug.log
26
  *
27
  * KNOWN ISSUES
28
  *
29
  *  - After a while the script disconnects from Nokia Chat, and although
30
  *    the script keeps running, and no errors are generated, it needs
31
  *    to be restarted to reconnect.  I'm sure there's a simple solution
32
  *    for this.
33
  *
34
  *  - This is a rough proof-of-concept hack, not intended for production use.
35
  *
36
  * This program is free software; you can redistribute it and/or modify
37
  * it under the terms of the GNU General Public License as published by
38
  * the Free Software Foundation; either version 2 of the License, or (at
39
  * your option) any later version.
40
  *
41
  * This program is distributed in the hope that it will be useful, but
42
  * WITHOUT ANY WARRANTY; without even the implied warranty of
43
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44
  * General Public License for more details.
45
  *
46
  * You should have received a copy of the GNU General Public License
47
  * along with this program; if not, write to the Free Software
48
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
49
  * USA
50
  *
51
  * @version 0.1, July 27, 2008
52
  * @link http://ruk.ca/article/4915 Nokia Chat + Robot + Plazes (original blog post)
53
  * @link http://plazes.net/doc Plazes.net API Documentation
54
  * @link http://ruk.ca/article/4910 Inside Nokia Chat’s XMPP
55
  * @author Peter Rukavina <peter@rukavina.net>
56
  * @copyright Copyright &copy; 2008, Reinvented Inc.
57
  * @license http://www.fsf.org/licensing/licenses/gpl.txt GNU Public License
58
  */
59
 
60
// User configurable options
61
$ovi_login '';                // Your Nokia Chat username (without the @ovi.com)
62
$ovi_password '';                // Your Nokia Chat password
63
include("XMPPHP/XMPP.php");        // Where is the XMPP.php class?  Change this, if required, to point to the right place.
64
// End of User configurable options
65
 
66
$ovi_host "ovi.com";
67
$ovi_username $ovi_login "@" $ovi_host;
68
 
69
// Make sure the script never times out (may not actually be required for a command line script).
70
set_time_limit(0);
71
 
72
// Extend the stock XMPPHP_XMPP class, as we need to enhance the presence_handler
73
// and message_handler functions.
74
class NOKIACHAT_XMPP extends XMPPHP_XMPP {
75
 
76
    /**
77
     * Presence handler
78
     *
79
     * Modified to only allow @ovi.com auto-subscriptions, and to send instructions on subscribe.
80
     *
81
     * @param string $xml
82
     */
83
    public function presence_handler($xml) {
84
        $payload['type'] = (isset($xml->attrs['type'])) ? $xml->attrs['type'] : 'available';
85
        $payload['show'] = (isset($xml->sub('show')->data)) ? $xml->sub('show')->data $payload['type'];
86
        $payload['from'] = $xml->attrs['from'];
87
        $payload['status'] = (isset($xml->sub('status')->data)) ? $xml->sub('status')->data '';
88
        $this->log->log("Presence: {$payload['from']} [{$payload['show']}] {$payload['status']}",  XMPPHP_Log::LEVEL_DEBUG);
89
        if($xml->attrs['type'] == 'subscribe') {
90
            if($this->auto_subscribe) {
91
                if (preg_match("/.*@ovi\.com/",$xml->attrs['from'])) {
92
                    $this->send("<presence type='subscribed' to='{$xml->attrs['from']}' from='{$this->fulljid}' /><presence type='subscribe' to='{$xml->attrs['from']}' from='{$this->fulljid}' />");
93
                    $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>");
94
                }
95
                else {
96
                    $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>");
97
                }
98
            }
99
            $this->event('subscription_requested'$payload);
100
        } elseif($xml->attrs['type'] == 'subscribed') {
101
            $this->event('subscription_accepted'$payload);
102
        } else {
103
            $this->event('presence'$payload);
104
        }
105
    }
106
 
107
    /**
108
     * Message handler
109
     * 
110
     * The Nokia Chat 'location' data message comes in like this:
111
     *
112
     * <message from='ruk@ovi.com/20c1' to='reinvented@ovi.com/xmpphp'>
113
     * <body>This message contains a location.</body>
114
     * <geoloc xmlns='http://jabber.org/protocol/geoloc' xml:lang='en'><lat>46.235961937499</lat><lon>-63.129662276767 </lon></geoloc>
115
     * <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>
116
     * </message>
117
     *
118
     * @param string $xml
119
     */
120
    public function message_handler($xml) {
121
        if(isset($xml->attrs['type'])) {
122
            $payload['type'] = $xml->attrs['type'];
123
        } else {
124
            $payload['type'] = 'chat';
125
        }
126
        $payload['from'] = $xml->attrs['from'];
127
        $payload['body'] = $xml->sub('body')->data;
128
        // If we find a 'geoloc' element, there's a location included in the message, and we parse it out...
129
        if ($xml->sub('geoloc')) {
130
            $payload['latitude'] = $xml->sub('geoloc')->sub('lat')->data;
131
            $payload['longitude'] = $xml->sub('geoloc')->sub('lon')->data;
132
            $this->locations[$payload['from']] = array($payload['latitude'],$payload['longitude']);
133
            $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>";
134
        }
135
        $this->log->log("Message: {$xml->sub('body')->data}"XMPPHP_Log::LEVEL_DEBUG);
136
        $this->event('message'$payload);
137
    }
138
 
139
}
140
 
141
// Create a new XMPP connection, connecting to the Nokia Chat account that will
142
// run everything for us.
143
$conn = new NOKIACHAT_XMPP('chat.ovi.com'5222$ovi_login$ovi_password'plazes_xmpp_daemon'$ovi_hosttrue99);
144
$conn->connect();
145
 
146
// When contacts request a subscription, automatically allow it rather than requiring confirmation.
147
$conn->autoSubscribe();
148
 
149
// Start a timer.
150
$timer time();
151
 
152
// Just hang around forever (probably a better way to do this?)
153
while(!$DONE) {
154
 
155
    // Send a presence update every 60 seconds.
156
    // This may or may not be required -- I added it because the script
157
    // seemed to just suddenly disconnect without cause and I thought
158
    // this might help.  It seems that maybe it actually doesn't?
159
    $now time();
160
    if (($now $timer) > 60) {
161
        $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>");
162
        $timer time();
163
    }
164
 
165
    // This is ripped right out of Laconia (http://laconi.ca/), but heavily modified to suit.
166
    // There's a lot of debugging output here that could be handled more elegantly.
167
    $payloads $conn->processUntil(array('message''presence''end_stream''session_start'));
168
    foreach($payloads as $event) {
169
        $pl $event[1];
170
        switch($event[0]) {
171
            case 'message':
172
                print "---------------------------------------------------------------------------------\n";
173
                print "Message from: {$pl['from']}\n";
174
                if($pl['subject']) print "Subject: {$pl['subject']}\n";
175
                print  $pl['body'] . "\n";
176
                print "---------------------------------------------------------------------------------\n";
177
                print print_r($event,1);
178
                print "---------------------------------------------------------------------------------\n";
179
                if ($pl['responses']) {
180
                    foreach($pl['responses'] as $key => $r) {
181
                        $conn->send("<message from=\"$ovi_username\" to=\"" $pl['from'] . "\" type=\"" $pl['type'] . "\">" $r "</message>");
182
                        sleep(1);
183
                    }
184
                }
185
                else if (($conn->locations[$pl['from']]) and (preg_match("/help/i",$pl['body']))) {
186
                    $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>");
187
                }
188
                // If we know where we are, then we can look up Plazes using the keyword we received.
189
                else if ($conn->locations[$pl['from']]) {
190
                    $url "http://plazes.net/plazes.xml?q=" urlencode($pl['body']) . "&near=" $conn->locations[$pl['from']][0] . "," $conn->locations[$pl['from']][1];
191
                    $fp fopen($url,"r");
192
                    $plazesxml stream_get_contents($fp);
193
                    fclose($fp);
194
                    $plazes = new SimpleXMLElement($plazesxml);
195
 
196
                    if (!sizeof($plazes)) {
197
                        $conn->send("<message from=\"$ovi_username\" to=\"" $pl['from'] . "\" type=\"" $pl['type'] . "\"><body>No matching Plazes found around your location.  Sorry.</body></message>");
198
                    }
199
                    else {
200
                        $counter '';
201
                        $responses = array();
202
                        $responses[] = '<body>Here are the closest matching Plazes to you:</body>';
203
 
204
                        foreach($plazes->plaze as $plaze) {
205
                            if ($counter <= 2) {
206
                                print "PLAZE FOUND: " print_r($plaze,1) . "\n";
207
                                $plazename htmlentities($plaze->name);
208
                                $responses[] = "<body><![CDATA[" $plazename " http://plazes.com/plazes/" strval($plaze->id) . "]]></body>";
209
                                $responses[] = "<geoloc xmlns='http://jabber.org/protocol/geoloc' xml:lang='en'><lat>" strval($plaze->latitude) . "</lat><lon>" strval($plaze->longitude) . "</lon></geoloc>";
210
                            }
211
                            $counter++;
212
                        }
213
                        foreach($responses as $key => $r) {
214
                            $conn->send("<message from=\"$ovi_username\" to=\"" $pl['from'] . "\" type=\"" $pl['type'] . "\">" $r "</message>");
215
                            // Nokia Chat got confused unless I inserted a 1 second delay between messages.
216
                            sleep(1);
217
                        }
218
                    }
219
                }
220
                else {
221
                    $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>");
222
                }
223
                break;
224
            case 'presence':
225
                print "Presence: {$pl['from']} [{$pl['show']}] {$pl['status']}\n";
226
                break;
227
            case 'subscription_requested':
228
                print "Subscription requested: {$pl['from']} [{$pl['show']}] {$pl['status']}\n";
229
                break;
230
            case 'session_start':
231
                $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>");
232
                break;
233
        }
234
    }
235
}