Send RSS Feeds to Your IM Application Using Jabber





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.

The Code

Save the code in Figure as client.php.

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 = '[email protected]';
//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( );

?>

Running the Hack

To 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.

You can find a list of public servers at http://www.jabber.org/network/ and a list of clients at http://www.jabber.org/software/clients.shtml.


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 Figure 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.

Many thanks go to Richard James Kendall. The RSS parser functions used in the script are adapted from his PHP RSS Reader, which you can find at http://richardjameskendall.com/.


Hacking the Hack

I'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);
	}

The RSS feed in your instant messenger


We 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.

In a similar way, you can add whatever custom functions you want to respond to additional keywords and return custom messages.

Matthew Terenzio

Having a conversation with your PHP script over Jabber


See Also



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows