site nav‎‏‎‎‏‎

categories‎ > ‎code‎ > ‎

filterfeed.php - A PHP Friendfeed Filter

posted 10 Oct 2009 09:41 by Slippy Lane   [ updated 12 Oct 2009 15:29 ]
First published in August, 2008

<?php

  /*
  Slippy's Filtered Friendfeed SMS-via-Google-Calendar Notificator
  GData routines taken from the API Guide samples and modified where necessary.
  All other code is my own.

  ### PLEASE READ THE FOLLOWING NOTES ###

  Note: Requires PHP5 and the Zend GData extensions
          Also requires user to have a Google account configured with
          Google Calendar. To receive SMS notifications, user must
          configure their Google Calendar settings with their mobile
          telephone details.

  To run this script, you'll need a computer running a webserver, PHP5 and the Zend GData extensions
  It's beyond the scope of this project to explain how to install the above dependencies here, but whatever
  operating system you use (personally, I'm on Ubuntu Gutsy), there are guides all over the web on how
  to prepare your computer for running the GData extensions.
  If you aren't able to run your own PHP server, get in touch (slippy.lane@gmail.com) and maybe we can
  arrange a time for me to open up the local server for remote access so you can try the script out in
  your browser.
 
  This script is not intended for heavy use, only to prove a concept. My primary reason for sharing
  it like this is, well, because I, like a lot of people, think code should be free, and also so that others
  with a similar interest can learn these techniques from it - Using PHP to automatically process an
  XML/Atom feed, and using the Zend GData framework to create Google Calendar events and send
  SMS notifications.
  */
  
  include "Zend/Loader.php";
  Zend_Loader::loadClass('Zend_Gdata');
  Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
  Zend_Loader::loadClass('Zend_Gdata_Calendar');

  #Retrieves an event from a Google Calendar data feed
  function getEvent($client, $eventId)
  {
    $gdataCal = new Zend_Gdata_Calendar($client);
    $query = $gdataCal->newEventQuery();
    $query->setUser('default');
    $query->setVisibility('private');
    $query->setProjection('full');
    $query->setEvent($eventId);

    try {
      $eventEntry = $gdataCal->getCalendarEventEntry($query);
      return $eventEntry;
    } catch (Zend_Gdata_App_Exception $e) {
      var_dump($e);
      return null;
    }
  }

  #Creates an event in the default Google Calendar referenced by $client
  function createEvent($client,$title,$desc,$where,$startDate,$startTime,$endDate,$endTime,$tzOffset)
  {
    $rem_method="sms";
    $rem_minutes="5";
    $gdataCal = new Zend_Gdata_Calendar($client);
    $newEvent = $gdataCal->newEventEntry();

    $newEvent->title = $gdataCal->newTitle($title);
    $newEvent->where = array($gdataCal->newWhere($where));
    $newEvent->content = $gdataCal->newContent("$desc");

    $when = $gdataCal->newWhen();
    $when->startTime = "{$startDate}T{$startTime}:00.000{$tzOffset}:00";
    $when->endTime = "{$endDate}T{$endTime}:00.000{$tzOffset}:00";
    $newEvent->when = array($when);
    // Upload the event to the calendar server
    // A copy of the event as it is recorded on the server is returned
    $createdEvent = $gdataCal->insertEvent($newEvent);
    return $createdEvent->id->text;
  }

  #Adds a reminder to an event, referenced by $eventId
  function setReminder($client, $eventId, $minutes=15)
  {
    $gc = new Zend_Gdata_Calendar($client);
    $method = "sms";
    if ($event = getEvent($client, $eventId)) {
      $times = $event->when;
      foreach ($times as $when) {
          $reminder = $gc->newReminder();
          $reminder->setMinutes($minutes);
          $reminder->setMethod($method);
          $when->setReminders(array($reminder));
      }
      $eventNew = $event->save();
      return $eventNew;
    } else {
      return null;
    }
  }

  #Simplifies creating an event and adding a reminder for the scope of this project.
  function notifyNow($client,$title,$desc,$location)
  {
    $reminderTime = getdate(time() + (6*60)); #Set the time of the event for 6 minutes in the future
    $rem_mins_before = 5;                     #And send an SMS reminder 5 minutes before the above time
    #Create the date and time strings to attach to the event
    $whendate = $reminderTime['year']."-".str_pad($reminderTime['mon'],2,'0',STR_PAD_LEFT)."-".str_pad($reminderTime['mday'],2,'0',STR_PAD_LEFT);
    $whentime = str_pad($reminderTime['hours'],2,'0',STR_PAD_LEFT).":".str_pad($reminderTime['minutes'],2,'0',STR_PAD_LEFT);
    #Create the event. $eventID contains the event's ID URI, and will be false if the create failed
    $eventID = createEvent($client, $title, $desc, $location, $whendate, $whentime, $whendate, $whentime, '+00');
    if ($eventID!==false) #success!
    {
      #The next two lines extract the id code from the id URI
      $pos = (strrpos($eventID,"/")+1);
      $newEventID = substr($eventID,$pos);
      #And now we attach the 5 minute SMS reminder to the event.
      $newEvent = setReminder ($client, $newEventID, $rem_mins_before);
    }
    if ($newEvent!==false) {return $newEvent;} else {return false;} # return the event object for success, false for failure
  }

  function recurse ($items) # Does what it says on the tin - recurses through an array of objects for the scope of this project
  {
    global $level;
    global $html;
    $level = $level + 1;
    foreach ($items as $item)
    {
      if ($item['href'])
      {
        $html .=  str_repeat('.',($level*2)).'<a href="'.$item['href'].'">'.$item.' ['.
              $item['href'].']</a><br/>';
      }
      else if ($item)
      {
        if (ltrim(rtrim($item))!="")
        {
          $html .= str_repeat('.',($level*2)).$item.'<br/>';
        }
      }
      recurse($item);
    }
    $level = $level - 1;
  }


  #
  # Configuration time...
  #

  #These two lines load the atom feed from friendfeed.com into a simplexml object $xml
  $file = "http://friendfeed.com/public?format=atom";
  $xml = @simplexml_load_file($file) or die ("no file loaded") ;

  $level = 0; #Set the initial recurse level used in Recurse()


  if (isset($_POST['user'])) #Do we have a username?
    {$user = ltrim(rtrim($_POST['user']));} #Good, put it in $user
  else
    {$user = '';}
  if (isset($_POST['pass'])) #Do we have a password?
    {$pass = ltrim(rtrim($_POST['pass']));} #Good, put it in $pass
  else
    {$pass = '';}
  if (!isset($_POST['alert'])) #SMS Alert Checkbox checked?
    {$alertchecked='';}
  else
    {$alertchecked='checked="on"';} #Make sure it's checked when it gets drawn.
  if (!isset($_POST['changed'])) #View only new/updated checkbox checked?
    {$checked='';}
  else
    {$checked='checked="on"';} #Same as SMS alert checkbox thingy

  if (($user!=='')&&($pass!=='')) #So, we've got a username and password, yes?
  {
    #The next two lines log us in to the google calendar service.
    $service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
    $client = Zend_Gdata_ClientLogin::getHttpClient($user,$pass,$service);
    #And this one sets the $loggedin variable appropriately.
    if ($client!==false) {$loggedin = true;} else {$loggedin = false;}
  }
  else
  {
    #No user/pass. Not logged in.
    $loggedin = false;
  }

  #This is all that messy html stuff. It always looks like this coz I do it late at night! Draws tha page header, login/filter/options form
  $html = "<html><head><title>Slippy's Filtered FriendFeed SMS-via-Google Calendar Notificator</title></head><body>".
            "<H2>Slippy's Filtered FriendFeed SMS-via-Google Calendar Notificator</H2>".
            '<H3>by Slippy Lane</H3>'.
            '<b>21st December 2007</b><br/><br/><center><table bgcolor="lightyellow" width="100%" border="0"><tr><td width="60%" valign="top">'.
            '<hr>Description: An experiment in using simplexml in PHP to filter an atom feed (from friendfeed.com) using various criteria, optionally display only new/updated matches and optionally create Google Calendar event which sends notification via SMS on new matches.<br/><br/>'.
            'Note. This page contains a form and needs to submit POST data on every refresh, so it is not programmed to auto-refresh. I could go down the rout of using a Google Authorisation cookie and persistent session information, but the way I see it, automatic refresh varies from browser to browser. To make it auto-refresh, I load the page in Firefox, right-click then choose "reload every 30 seconds", then when it asks me if I want to resubmit POST data every time, I tell it yes, please.</td><td width="50"></td><td valign="top">'.
            '<hr/><center><form name="friendfeedfilter" method="post" action="'.$_SERVER['PHP_SELF'].'">';

  #A little snippet to tell us if we're logged in or not.
  if ($loggedin)
    {$html .= '<font color="green">You are logged in.</font><br/>';}
  else
    {$html .= '<font color="red">You are not logged in.</font><br/>';}

  #And the rest of the messy stuff
  $html .=  '<table border="0"><tr><td>GCal User </td><td>'.
            '<input type="text" name="user" id="user" size="20" value="'.ltrim(rtrim($_POST['user'])).'"/></td></tr>'.
            '<tr><td>Pass </td><td><input type="password" name="pass" id="pass" size="20" value="'.
            ltrim(rtrim($_POST['pass'])).'"/></td></tr></table>'.
            '<input type="checkbox" name="alert" id="alert"'.$alertchecked.'/>'.
            ' Select this checkbox to have an SMS notification sent via GCal for each match.<br/>'.
            '<input type="checkbox" name="changed" id="changed"'.$checked.'/>'.
            ' Select this checkbox to display only new or updated items.<br/>'.
            '<table border="0"><tr><td>Filter by Friend</td><td>'.
            '<input name="friend" type="text" size="20" value="'.
            ltrim(rtrim($_POST['friend'])).'"'."/></td></tr>".
            '<tr><td>by Post Type</td><td><input name="location" type="text" size="20" value="'.
            ltrim(rtrim($_POST['location'])).'"/></td></tr></table>'.
            '<div align="center"><input name="send" type="submit" value="Enable the filter"></div>'.
            '</form></center></td></tr><tr><td><hr/></td><td></td><td><hr/></td></tr></table></center>'.
            '<b><a href="'.$xml->id.'">'.$xml->title."</a></b><br/>";

  $items = array();
  if ((isset($_POST['friend'])) or (isset($_POST['location'])))
  {
    #A little bit of output, telling the user what filters are active.
    $html .= "<hr/>";
    if ($_POST['friend'])
    {
      $html .= "Filtering by friend: ".$_POST['friend']."<br/>";
      if ($alertchecked!=='') $html .= "Sending SMS on new matches.<br/>";
    }
    if ($_POST['location']) $html .= "Filtering by location: ".$_POST['location']."<br/>";
    if ($checked !=='') $html .= "Only displaying new/updated matches.<br/>";
    $html .= "<hr/>";
  }

  #So now let's iterate throught the <entry> tags in the atom document.
  foreach ($xml->entry as $entry)
  {
    #Set all the relevant variables and the item array
    $title = $entry->title;
    $item = array();
    $id = explode(':',$entry->id);
    $item['id']         = $id[2];
    $item['updated']    = $entry->updated;
    $item['friend']     = $title->div->a[0];
    $item['action']     = (string) $title->div;       # the "Add reminder" bit falls over
    $item['location']   = (string) $title->div->a[1]; # if these stay as simplexml objects,
    $item['links']      = (string) $entry->link;      # so we convert them to strings here.
    $item['content']    = $entry->content->div->div;

    #This section decides whether the current entry gets displayed or not.
    $match = false; #Default to "not displayed"
    if ($_POST['friend']) #Are we filtering by person?
    {
      if (stripos($item['friend'],$_POST['friend'])!==false) #Yes. Is this person a match?
      {
        $match = true; #Yes. Set $match appropriately
      }
    }
    else if ($_POST['location']) #Are we filtering by post location?
    {
      if (stripos($item['location'],$_POST['location'])!==false) #Yes. Is this item a match?
      {
        $match = true; #Yes. Set $match appropriately
      }
    }
    else $match = true; #We're not filtering so display all items.

    if ($match)
    {
      #Format the feed content ready for output
      $outstr1 = '<a href="http://friendfeed.com'.
                  $item['friend']['href'].
                  '">'.
                  $item['friend'].
                  '</a> '.
                  $item['action']." -- ".
                  ' <a href="'.
                  $item['location']['href'].
                  '">'.
                  $item['location'].
                  "</a>";
      $flag="none";

      #Set some flags that tell us whether the item is new/updated/old and whether it is to be displayed or not.
      if (!isset($_SESSION[$item['id']]))
      {
        $flag="new";
        $_SESSION[$item['id']] = htmlspecialchars($item['updated']);
        $outstr0 =  "<b>[*]</b>";
      }
      else
      {
        if ($_SESSION[$item['id']] != htmlspecialchars($item['updated']))
        {
          $flag="upd";
          $_SESSION[$item['id']] = htmlspecialchars($item['updated']);
          $outstr0 =  "<b>[#]</b>";
        }
        else
        {
          $flag="old";
          $outstr0 = "<b>[ ]</b>";
        }
      }
      #Is it new, updated or unfiltered?
      if ( (($flag=="new") || ($flag=="upd")) && (ltrim(rtrim($_POST['friend']))!='') )
      {
        if ($alertchecked!==null)
        {
          if ($loggedin!==false)
          {
            $result = notifyNow($client,(string) $item['friend'],$item['action'],$item['location']);
            if ($result!==false)
            {
              $html .= "Notification of this item has been sent by SMS via GCal.<br/>";
            }
            else
            {
              $html .= "Notification of this item failed.<br/>";
            }
          }
          else
          {
            $html .= "Notification not sent - you are not logged in to the Google Calendar service. Please check your username and password above.<br/>";
          }
        }
      }
      echo $checked;
      if (($flag=="new") || ($flag=="upd") || ($checked!=='checked="on"'))
      {
        $html .= $outstr0.$outstr1."<br/>";
        recurse($item['content']);
        $html .= "<hr>";
      }
    }
  }
  $html .= "</body></html>";
  #All finished, so finally we output the html...
  echo $html;
?>

Č
ċ
ď
filterfeed.php
(15k)
Slippy Lane,
10 Oct 2009 09:46