Sending RSS feeds as alerts to your Instant Messenger

Instant Messaging is ubiquitous. Some studies have shown that younger internet users rely more on IM than email.
Unfortunately, because of the proprietary nature of the most popular systems and the stateless connections of HTTP, it hasn’t been a service that was easily integrated into PHP applications.
The “Jabber” open source protocol, developed by Jeremie Miller in 1998, 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.
This Hack is going to create a command line PHP Jabber client which 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 it’s 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 it off as an Instant Message.

Running the Hack
To complete the Hack, you will need at least one new Jabber account and a personal Jabber account as well to receive the Instant Message. There are many public servers and Jabber clients to choose from . A list of public servers can be found at http://www.jabber.org/network/ and a list of clients is available 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. We will use this account as a web agent or bot. If you need a personal Jabber account, register one 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. You can find it at http://cjphp.netflint.net/ . Put the file in the directory 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 it, we are going to create a simple command line client which will eventually act as a daemonized bridge to the information we want sent to us.

Copy the code into file called client.php. Modify the config variables on top to reflect your jabber accounts and server and put the file 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.
From the command line, run the script:

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 automagically send the current alerts again. Much thanks goes 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

The Code

< ?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();
	
?>

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 the help from the folks over at Weather Underground to help out. They graciously set up an RSS resource that gets fed a zip code and returns the current weather in XML.

The only trick is to set the client up to also be waiting for incoming messages.
class.jabber.php has a nice feature where you can 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);
}

We only want to respond to messages requesting weather forecasts here so we’ll use the format

weather:zipcode

Any incoming message beginning with the word weather followed by a colon will call the function weatherize().

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

Oct 29 2006 09:26 pm | Uncategorized |

2 Responses to “Sending RSS feeds as alerts to your Instant Messenger”

  1. on 29 Oct 2006 at 9:35 pm everybuddy.org » RSS to Jabber Hack

    […] RSS to Jabber in PHP Oct 29 2006 09:35 pm | Uncategorized | […]

  2. on 04 Nov 2006 at 6:26 am links for 2006-11-04 « My Weblog

    […] everybuddy.org » Sending RSS feeds as alerts to your Instant Messenger (tags: rss web20) […]

Leave a Reply