Hack 88. Send RSS Feeds to Your IM Application Using Jabber
Use PHP and Jabber to send RSS feeds to your instant messaging application. Instant messaging is ubiquitous. Some studies have shown that younger Internet users rely more on IM than on email. Unfortunately, because of the proprietary nature of the most popular IM systems and the stateless connections of HTTP, IM hasn't been easily integrated into PHP applications. The Jabber open source protocol, developed by Jeremie Miller in 1998 (and now called the Extensible Messaging and Presence Protocol [XMPP]), is a native XML streaming protocol and IETF-approved Internet standard for presence and messaging technologies. Important to us, though, is that XMPP allows for PHP scripts to access IM applications. This hack creates a command-line PHP Jabber client that uses the freely available class.jabber.php as a bridge to the XMPP protocol. Another popular XML protocol called RSS allows a site to syndicate its content as a feed. Newsreaders and web pages poll a feed URL periodically, looking for new content items. The Jabber client we create will poll some existing weather RSS feeds for a new weather alert and send that alert off as an instant message. 9.4.1. The CodeSave the code in Example 9-7 as client.php. Example 9-7. A Jabber client example<?php /* CONFIG VARIABLES */ // jabber server you are registed at $SERVER = 'yourserver'; //username and password for your special account $USERNAME = 'yourusername'; $PASSWORD = 'yourpassword'; // jabber id for your personal account $PERSONAL = 'username@yourserver'; //rss url for the alerts you want $NOAA = 'http://www.nws.noaa.gov/alerts/ct.rss'; /* END CONFIG */ function send($to, $msg) { global $JABBER; $JABBER->SendMessage("$to","normal", NULL, array("body" => htmlspecialchars($msg)),$payload); } //overrides jabber.class.php handler function Handler_message_normal($message) { global $JABBER; $from = $JABBER->GetInfoFromMessageFrom($message); $body = $JABBER->GetInfoFromMessageBody($message); if (substr ($body ,0,3) == SMS) { $bodyparts = explode(":", $body); $zip = $bodyparts[1]; weatherize($from, $zip); } } function Handler_message_chat($message) { Handler_message_normal($message); } //RSS functions adapted from PHP RSS Reader v1.1 By Richard James Kendall function startElement($parser, $name, $attrs) { global $rss_channel, $currently_writing, $main; switch($name) { case "RSS": case "RDF:RDF": case "ITEMS": $currently_writing = ""; break; case "CHANNEL": $main = "CHANNEL"; break; case "IMAGE": $main = "IMAGE"; $rss_channel["IMAGE"] = array( ); break; case "ITEM": $main = "ITEMS"; break; default: $currently_writing = $name; break; } } function endElement($parser, $name) { global $rss_channel, $currently_writing, $item_counter; $currently_writing = ""; if ($name == "ITEM") { $item_counter++; } } function characterData($parser, $data) { global $rss_channel, $currently_writing, $main, $item_counter; if ($currently_writing != "") { switch($main) { case "CHANNEL": if (isset($rss_channel[$currently_writing])) { $rss_channel[$currently_writing] .= $data; } else { $rss_channel[$currently_writing] = $data; } break; case "IMAGE": if (isset($rss_channel[$main][$currently_writing])) { $rss_channel[$main][$currently_writing] .= $data; } else { $rss_channel[$main][$currently_writing] = $data; } break; case "ITEMS": if (isset($rss_channel[$main][$item_counter][$currently_writing])) { $rss_channel[$main][$item_counter][$currently_writing] .= $data; } else { $rss_channel[$main][$item_counter][$currently_writing] = $data; } break; } } } function parseXML($url) { global $rss_channel, $currently_writing, $main, $item_counter; $file = $url; $last_item = $_REQUEST['last_item']; $rss_channel = array( ); $currently_writing = ""; $main = ""; $item_counter = 0; $xml_parser = xml_parser_create( ); xml_set_element_handler($xml_parser, "startElement", "endElement"); xml_set_character_data_handler($xml_parser, "characterData"); if (!($fp = fopen($file, "r"))) { die("could not open XML input"); } while ($data = fread($fp, 4096)) { if (!xml_parse($xml_parser, $data, feof($fp))) { die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($xml_parser)), xml_get_current_line_number($xml_parser))); } } xml_parser_free($xml_parser); } function NOAA( ) { global $rss_channel, $currently_writing, $main, $item_counter; global $last_item; global $message; $message=""; parseXML($NOAA); if (isset($rss_channel["ITEMS"])) { if (count($rss_channel["ITEMS"]) > 0) { for($i = 0;$i < count($rss_channel["ITEMS"]);$i++) { if ($rss_channel["ITEMS"][count($rss_channel["ITEMS"])- 1]["TITLE"] == $last_item) { break; } //nothing new $message .= $rss_channel["ITEMS"][$i]["TITLE"]."\r\n".$rss_ channel["ITEMS"][$i]["LINK"]."\r\n\r\n"; } } else { $message = "There are no articles in this feed."; } } $last_item = $rss_channel["ITEMS"][count($rss_channel["ITEMS"])-1]["TITLE"]; If ($message != '') { send($PERSONAL, $message); } } function weatherize($from, $zip) { global $rss_channel, $currently_writing, $main, $item_counter; $wunderurl = 'http://www.wunderground.com/cgi-bin/findweather/ getForecast?brand=rss_full&query='.$zip; parseXML($wunderurl); if (isset($rss_channel["ITEMS"])) { if (count($rss_channel["ITEMS"]) > 0) { for($i = 0;$i < count($rss_channel["ITEMS"]);$i++) { $wunderground .= $rss_channel["ITEMS"][$i]["TITLE"]."\r\n".$rss_ channel["ITEMS"][$i]["LINK"]."\r\n".$rss_ channel["ITEMS"][$i]["DESCRIPTION"]."\r\n\r\n"; } } else { $message = "There are no articles in this feed."; } } send($from, $wunderground); } //End RSS functions require("class.jabber.php"); $JABBER = new Jabber; $JABBER->server = $SERVER; $JABBER->port = "5222"; $JABBER->username = $USERNAME; $JABBER->password = $PASSWORD; $JABBER->resource = "client.php"; $JABBER->enable_logging = FALSE; $JABBER->Connect( ) or die("Couldn't connect!"); $JABBER->SendAuth( ) or die("Couldn't authenticate!"); $JABBER->SubscriptionAcceptRequest($PERSONAL); while(true) { $JABBER->SendPresence(NULL, NULL, "online"); NOAA( ); $JABBER->CruiseControl(15 * 60); } // may never get here but… $JABBER->Disconnect( ); ?> 9.4.2. Running the HackTo complete the hack, you will need at least one new Jabber account, as well as a personal Jabber account to receive the instant message. You have many public servers and Jabber clients to choose from.
Download and install a client if you don't have one already and register an account with one of the Jabber servers. You'll use this account as a web agent (sometimes called a robot, or just bot). If you need a personal Jabber account, register for that as well. Next, you'll need to download the freely available class.jabber.php, originally written by Carl "Gossip" Zottmann and currently maintained by Nathan "Fritzy" Fritz. The class is available online at http://cjphp.netflint.net/. Put the file into the directory in which you'll be putting the rest of this hack's files. This class greatly simplifies the process of interacting with the XMPP protocol. Using this class, we are going to create a simple command-line client that will eventually act as a daemonized (that's not demonized, for those of you who just got concerned) bridge, giving us access from PHP all the way into an IM client. Copy the code in Example 9-7 into the file called client.php. Modify the configuration variables (bolded near the top of the script) to reflect your Jabber accounts and your server information and put the script in the same directory as class.jabber.php. Add the Jabber account you are using to connect with the script to your personal Jabber account contact list (this is essentially adding a buddy to your Jabber IM list). Then, from the command line, run the script using the PHP interpreter: php client.php & You should receive an instant message with all the weather alerts currently in effect. Every 15 minutes, the script will check to see if there are any changes. If there are, it will auto-magically send the current alerts again. An example RSS feed is shown in Figure 9-6.
9.4.3. Hacking the HackI'm sure most people would also be interested in getting their daily weather on demand as well. I was, so I got help from the folks at Weather Underground (http://www.wunderground.com/). They graciously set up an RSS resource that gets fed a Zip Code and returns the current weather in XML. The only trick here is that you need to set up the client to be waiting for incoming messages as well. class.jabber.php has a nice feature that lets you define functions to override the default message handlers: function Handler_message_normal($message) { global $JABBER; $from = $JABBER->GetInfoFromMessageFrom($message); $body = $JABBER->GetInfoFromMessageBody($message); if (substr ($body ,0,8) == weather:) { $bodyparts = explode(":", $body); $zip = $bodyparts[1]; weatherize($from, $zip); } } function Handler_message_chat($message) { Handler_message_normal($message); } Figure 9-6. The RSS feed in your instant messengerWe want to respond only to messages requesting weather forecasts here, so we'll use this simple format: weather:zipcode Any incoming message beginning with the word weather, followed by a colon will call the function weatherize(). An example of this is shown in Figure 9-7. In a similar way, you can add whatever custom functions you want to respond to additional keywords and return custom messages. Matthew Terenzio Figure 9-7. Having a conversation with your PHP script over Jabber9.4.4. See Also
|