30
Jan 09
A Twitter RSS Notifier

If you're a heavy user of twitter you probably already experienced this: you cannot miss a bit of what is happening on twitter so you always have your Twitter client fired up getting you the updates with a high frequency check (my Twitterific is on a 3min check), and although you have all of your RSS subscriptions safely managed by GoogleReader or your client of choice, you end up knowing the news first on Twitter, by your followers conversations than by deliberately accessing GoogleReader, which itself has an unknown frequency check. Also, if on GoogleReader you have content that's highly important to you (data thats updated by RSS, not your regular blog post) constant accesses to GoogleReader may distract you from the job your doing locally, just to end up knowing nothing knew is there.

Of course you could just forget to check GoogleReader and be notified by a simple RSS Notifier, but why would you install another program if your twitter client already does that?

So I decided to create a Twitter account that gets fed by a simple Ruby script that checks my most important feeds in 3 minutes time loops and if there is any new content, the first 140 characters of it gets posted on twitter (you may edit it to post the entire post but be careful, twitter has a 100 connections per hour limit), because in the feed I'm talking about that is enough to know what happened. So let's get to the code.

require 'net/https'
require 'rexml/document'

begin
  f = File.new("data.txt", "r")
rescue Errno::ENOENT
  #File doesn't exists, fill it with sample data
  f = File.new("data.txt", "w")
  info = {
    "so" => "",
    "eo" => "",
    "aced" => "",
    "po" => "",
    "ges" => "",
    "ppi" => ""
  }
  Marshal.dump(info,f)
  f.close
  f = File.new("data.txt", "r")
end

info = Marshal.load(f)
f.close

urls = {
  "po" => "/external/announcementsRSS.do?announcementBoardId=243496",
  "so" => "/external/announcementsRSS.do?announcementBoardId=243523",
  "aced" => "/external/announcementsRSS.do?announcementBoardId=244762",
  "ges" => "/external/announcementsRSS.do?announcementBoardId=243508",
  "ppi" => "/external/announcementsRSS.do?announcementBoardId=243511",
  "eo" => "/external/announcementsRSS.do?announcementBoardId=243514"
}

actual_info = ""
urls.each { |id,url|
  begin
    http = Net::HTTP.new('fenix.ist.utl.pt', '443')
    http.use_ssl = true
    http.start do |http|
      request = Net::HTTP::Get.new(url)
      response = http.request(request)
      response.value
      actual_info = response.body
    end

  rescue Net::HTTPExceptions
    next #do nothing if couldn't get feed, it'll in the near future
  end

  if info[id] != actual_info  
    info[id] = actual_info

    doc = REXML::Document.new(info[id])
    root = doc.root
    title = String.new(doc.elements["*/channel/item/title"].text)
    desc = String.new(doc.elements["*/channel/item/description"].text)
    desc = desc.gsub(/<[^>]*>/,'')

    begin
      http = Net::HTTP.new('twitter.com', '443')
      http.use_ssl = true
      http.start do |http|
        limit = 140 - (2 + 2 + 2 + title.length)
        request = Net::HTTP::Post.new('/statuses/update.xml')
        request.basic_auth 'username', 'password'
        request.set_form_data({"status"=>id.upcase+"::"+title+"::"+desc.slice(0,limit)})
        response = http.request(request)
        response.value
      end
    rescue Net::HTTPExceptions
      #The submission to twitter failed, forget it! (critical)
    end
  end
}

f = File.new("data.txt", "w")
Marshal.dump(info,f)
f.close

 

 The script is intended to be run by crontab, there you may specify the time interval in which you want the script to be run. The script itself stores the feeds info in a string which for itself is stored in a hashtable that contains all the feed strings indexed by an id that you must provide and be equal in both hash tables, the one for the info, and the one with the URL's of the feeds. In my script all the feeds were located under the same host, so the connection was always and opened with 'fenix.ist.utl.pt' (and SSL). If that is different with you, store the host in another hashtable indexed by the same id's. At last you may want (have) to change the items selected in the XML file returned by the call to be posted on the twitter post. In my example all the feeds started by:

<channel>
    <item>
        <tittle>
        <description>
        <...>
        <...>
    </item>
</channel>

So I select the title and split the description in a way to not fill more than 140 chars total. At last you will have to create a TwitterAccount and provide its username and password in the last connection that is done, the one to Twitter.

I hope this may be useful to you. The result of mine is at @istalert.


Go to Blog

Comments

Pedro Ganço 28 of June of 2009
That is just great! I don't even need to be that skilled to understand your code ;)
Maybe I'll do some experiments myself on holidays...
Congrats

Add your thoughts about it!