How to Build a Twitter Agent

on February 15, 2008

Note, while working on this project this ReadWriteWeb article was released, illustrating the future potential of the Jabber/XMPP protocol.

In this article we will build an actual useful Twitter Service that will allow us to track the Blogosphere. In the process we will get hands on programming experience with Ruby, DRb, Twitter and Jabber. This will sharpen our developer skill-set to get ready for the upcoming (Folk)Semantic Web. Also we evaluate the problems seen and opportunities ahead.

Background

Whether you want to call it Web 2.0, Web 3.0, the Semantic Web or the Web of Data – change is happening. The past years we’ve seen the tremendous power of Folkosonomies and now this Social Web is colliding with the emergence of the Semantic Web, resulting in the first semantic services. For us developers and creative entrepreneurs it’s important to get ready for this new wave of business opportunities. I find the whole notion of Intelligent Agents very interesting. For our little project however, we will create a Stupid Agent :]

Technologies like Jabber/XMPP and DRb will enable us to move from a reactive web to a proactive web. Right now this proactive realtime push of data is important for the more liquid content creation services. Micro/nano blogging platforms as Twitter and Tumblr are good examples of this. This is one of the reasons that they already have a Jabber service set up.

I’ve had used Jabber before to communicate with my geek friends. For this project, I had to set up a Jabber client and Jabber account. Call me stupid, but I actually had to spend 30 minutes figuring out how the hell I had to create an account and choose which server to use (turns out you can do that in the client). Now of course XMPP/Jabber is just a standard for enabling IM communication, but apart from Google’s GTalk there hasn’t been much widespread use by ordinary users. In my view, these uses of XMPP for machine to machine (to human) are much more interesting.

Case: The Observatory Bot

I’ve been programming for quite some time now. When learning a new language I really hate doing little examples that produce zero user/business value. That’s why I think the best way to learn new technologies is to solve real world problems right away. Of course tutorials are valuable to just get a general idea of what’s going on, but don’t waste too much time on them – implement straight away.

Our Twitter Service will also need to create some value for the user and must be production ready. However, don’t get your hopes up too much since these are experimental technologies with dependence on external services like Jabber and Twitter.

Remember my last little project? Wigitize.com is actually generating a lot of data. It’s tracking about 5000 6000 feeds every hour! Let’s do something with that data :]

The Observatory:
  • is a Twitter-only service
  • will allow you to ‘track’ the Blogosphere
  • will send you a direct message when something happens in the Blogosphere

Basically this is like Twitter’s IM functionality to track the Twittersphere. So in a sense The Observatory will be a proof of concept portal between the Twittersphere and the Blogosphere.

The Architecture

Right now Wigitize.com uses BackgrounDRb to perform background tasks and also to update all RSS feeds periodically. Everytime the feed aggregation process finds a new feed entry it will create a FeedEntry instance. The creation of these objects serve as events for the Observatory Bot. These events have to be pushed to the Observatory Bot in some way.

What can Twitter’s IM service do for us?

To play around with Twitter’s agent you need to set up a Jabber account and a Twitter account. For debugging I’ve found the MacOS tool JabberFox very helpful.

Basically, these commands are available:

However, I think there are a lot more hidden commands which can be used. After emailing with the Twitter developers they told me there is a command called “d”. This can be used to send direct messages (d username message). Very useful!

Coding the Bot

Our implementation choice for today will be Ruby. If you’ve programmed intensively in other languages before you’ve probably come to the conclusion that Ruby is quite different from most other. Ruby’s flexible object models allow for great extension of the language itself (eg 3.minutes.ago). It is therefore no surprise that interesting Semantic Web projects like ActiveRDF are choosing Ruby as their language.

To communicate with Twitter we can use this cool Ruby Twitter API. Unfortunately all of these API’s are HTTP/REST driven and really limit what we can do in terms of realtime response. Also, if you want to build a serious production ready service, constantly polling Twitter will kill both parties.

So we need to interface with their Jabber Service. Jabber is a friendly name for Instant Messaging (IM) using the open XMPP protocol. Luckily, there is a Ruby library called XMPP4R which does most of the XMPP work for us. This blog post provides some simple examples and this German wiki entry provides sample code how to use callbacks (very important for a bot).

I’ve wrapped all of this in a simple JabberBot class jabber_bot.rb that can be used like this:

1
2
3
4
5
6
7
8
  class MyJabberBot < JabberBot
    def on_message(from, body)
      say(from, "You said: #{body}")
    end
  end
  my_jabber_bot = MyJabberBot.new('observatory@jabber.org', 'password')
  my_jabber_bot.connect_and_authenticate
  my_jabber_bot.run

As you can see in the diagram, I’ve build a TwitterBot on top of this JabberBot. Unfortunatly it’s not possible to do all communication with Twitter through Jabber yet. For example: there are no events for when users start following other users or ways to retrieve information. This is why twitter_bot.rb is essentially a hybrid using both the Twitter API and Twitter’s Jabber service. Feel free to use all sourcecode provided here, I know it will be useful to some of you out there. This is how to use this TwitterBot:

1
2
3
4
5
6
7
8
9
10
11
12
  twitter_bot = TwitterBot.new('observatory', 'password', 'observatory@jabber.org', 'password')
  twitter_bot.track_phrases = ['observatory.topoints.com']
  twitter_bot.on_directed_tweet do |username, message|
    puts("directed tweet: #{username} says #{message}")
  end
  twitter_bot.on_tweet do |username, message|
    puts("something from #{username}: #{message}")
  end
  twitter_bot.on_track do |username, message, phrase|
    puts("track: #{username} says #{message} (keyword: #{phrase}")
  end
  twitter_bot.runn(:follow_all_followers => true)

Now that we have the basic building blocks to build our service, let’s build our core business logic (observatory_twitter_bot.rb):

This means that we will send a greeting when people start following us:

1
2
3
4
5
6
7
8
  on_follow do |username|
    logger.info("#{username} is following us, will follow #{username} too and send welcome message")
    follow(username)
    direct_message(username, "the Observatory is now ready to serve you, use '@observatory track [keyword]' to get blogosphere updates.")
  end
  on_unfollow do |username|
    logger.info("#{username} stopped following us")
  end

Note: in order to get the on_follow event, we have to poll the Twitter HTTP API . Since Twitter limits the rate to 70 requests per hour, I poll every two minutes to be on the safe side.

And that we will start tracking the Blogosphere for them when they say the magic word:

1
2
3
4
5
6
7
8
9
10
11
12
  on_directed_tweet do |username, message|
    logger.info("directed tweet: #{username} says #{message}")
    if (phrase = track_phrase(message))
      logger.info("tracking '#{phrase}' for user #{username}")
      begin
        direct_message(username, "Will send a direct message anytime something happens in the Blogosphere regarding '#{phrase}'")
        Tracker.for(username, phrase)
      rescue => e
        logger.error("tracking failure: #{e.to_s}")
      end
    end
  end

Now when a new FeedEntry is created, we need to make sure that these Twitter users get notified when their tracked phrase matches the FeedEntry. Since this might take up some time, I’ve created a background worker task for it:

As you might see, Distributed Ruby (DRb) makes it extremely easy to control our bot remotely. In the ObservatoryBot we say:

1
  DRb.start_service("druby://:8997", self)

And all bot functionality can be accessed by calling: observatory_bot = DRbObject.new(nil, ‘druby://:8997’)

Now that we have our autonomous agent it would be nice if we could easily start and stop it in a production environment. I found the Ruby Gem called Daemons extremely useful to wrap these things up.

First, set up a file that runs the never ending process (eg script/observatory_twitter_bot.rb):

1
2
3
4
5
6
7
8
require 'logger'
require File.dirname(__FILE__) + '/../config/boot'
require File.dirname(__FILE__) + '/../config/environment'
require File.dirname(__FILE__) + '/../lib/observatory_twitter_bot'

logger = Logger.new(File.join(RAILS_ROOT, 'log/observatory_twitter_bot.log'))
observatory_twitter_bot = ObservatoryTwitterBot.new(logger)
observatory_twitter_bot.runn

Next, wrap this up in a daemon script (eg script/observatory_twitter_bot):

1
2
3
4
5
6
7

#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot'
require 'rubygems'
require 'daemons'

Daemons.run('script/observatory_twitter_bot.rb')


The Demo

Right now if all communication lines with Twitter are working fine, the service is up and running. I’ve made a little bot homepage at observatory.topoints.com

@observatory track ‘Twitter’:

Problems and Opportunities

In the development of the Observatory I had one big obstacle: Twitter is often down and it can cripple your service and development time. I understand that Twitter is a small team under enormous pressure but there have been a lot of complaints about this.

Nevertheless Twitter and it’s developers really kick ass. I emailed Alex Payne and he was excited about what I’m doing (and also the Twitter things happening the iKnow! project in Japan). He responded fairly quickly and immediately whitelisted my Twitter account to up the rate limits.

While working on this project I realized that Twitter in it’s current state isn’t really suitable for system-to-human notifications. Twitter could expand their system to be a true notification framework, but I’m not sure if they will. If they don’t, there is a tremendous business opportunity here. Imagine an open API that mashes up with technologies like XMPP and Growl. A service like that could become THE notification-bus of the web! (Already Growl is pretty big in Mac land). A full blog post about this startup idea coming up!

Geek Food for Thought

What about RubyOnRails, Jabber, DRb, Daemons and BackgrounDRb? I think we are seeing a new framework here! In this interesting article by Danny Ayers he talks about a toolset for agents. On Java there is already “an Agent Framework” that I haven’t checked out yet. I can imagine that these frameworks ease doing development like this and facilitate better system autonomy. Of course it’s desirable to give such a framework a pragmatic paintjob by using Rails paradigms.

Above here I’ve illustrated Rails’ missing brother. I think it’s also good to take into account interesting technologies like Juggernaut and Comet. These are basically Javascript Push techniques to make a synchronous interaction on the asynchronous web possible.

When you combine all these micro- asynchronous communication lines you get one big synchronous connection line between machine agents and user agents.

Wigitize.com, Post-Digg

on January 09, 2008

Yesterday I hit the Digg front page with my Building a .com in 24 hours article. Apart from that I was also number one and number nine in the delicious rankings. Very exciting!

What is also interesting is that my rapidly build website – wigitize.com – got a lot of hits. My blog got about 25000 uniques, Wigitize about 10000. All these hits caused an incredible load on my small 256MB memory server so I quickly bought a second one and scaled up. Yesterday, I could finally stabilize the backgrounding on the website and everything is running fine ever since. Someone offered me a free slicehost slice too last night!

It’s also funny that people commented on how I must have too much time on my hands. I even got offered participation in startups and companies haha. In fact, the opposite is true, but I do love hacking away in my free time. Besides, it was xmas, that means coding, drinking and eating right?!

While combating the performance issues I had yesterday, I learned these technical lessons:

Make sure you’re running the right version of tools. Apparently I was running a very old backgroundrb version. So I now upgraded to Ezra’s 0.2.1 version

Debug things in production-mode on your local machine! Some code – especially funky code like backgroundrb – will behave differently on production as opposed to dev. Often people like me are strictly developing in ‘development’ mode and than publishing to staging/production environments. I found a mayor issue that was screwing up everything in backgroundrb by debugging things in production mode on my MacBook.

Regular expressions can be dangerous too! Sometimes Feed detection workers started eating up 100% CPU. After debugging things for a long time, I found out that my regular expression was going berserk on sites like msn.com. So I changed expressions in the feed detecting algorithm from:
1
/<link.*href=['"]*([^\s'"]+)['"]*.*application\/rss\+xml.*>/.match(html)
to:
1
/<link.*href=['"]+([^'"]+)['"]+.*application\/rss\+xml.*>/.match(html) 

Note about BackgrounDRB

on January 03, 2008

BackgroundRB was a nice project that allowed you to easily do background jobs in Ruby and Rails.

Now, it is maintained by some PRICK who is rewriting everything making it IMPOSSIBLE to understand. (Maybe Zed Shaw has a Point?)

In the current SNS - Social Networking Site - boom it is becoming increasingly important to deal with usability. People have accounts for many different websites and it's getting more and more tiring to register for a new account. This is one of the main reasons why Confabio.com doesn't require you to signup and login. And it's also one of the main reasons why websites like Wakoopa.com make their registration as painless as possible. A colleague of mine was even experimenting with the idea of omitting the username/email requirement at all. Also, OpenID is yet too young and Sun's Liberty Alliance is just too corporate and slow.

But for most social networking sites it's pretty simple: they just need people to enter information. So let's make that as easy for the user as possible.

Entering Syndication Feeds

For one of my projects I have to let users enter information about themselves. This is so they can build up their own profile. What I really like about some of the new sites is that they aggregate your blog's contents and your FlickR pictures.

One of such websites is the Tokyo based Social Networking Site Asooboo.com. After signing up you can enter your blog feed and FlickR username and it will keep track of all your stories and pictures. I think that's really cool and it's one of the first steps in making the web more ubiquitous. You can later change your Feed URL in your 'edit my profile':

Entering Links instead of Feeds

Entering feeds is nice, but to users that are not tech-savy 'Feed, RSS and Atom' might raise question marks. Therefore I think it would be nice if the users wouldn't have to worry about feeds, but instead can just enter their links like:

My Websites and Profiles:

  • http://blog.dominiek.com/
  • http://www.flickr.com/photos/dominiekterheide/
  • http://del.icio.us/dominiekth

It would then show a fancy spinner and convert it to 'My Blog', 'My Pictures' and 'My Links'. All content will be automatically aggregated if it can detect any RSS feeds on those pages.

Detecting RSS feeds

When you use a proper browser like Mozilla Firefox you will see a syndication icon every time you visit a website that has RSS feeds:

It does this by reading certain HTML tags.

After a quick search I couldn't find any code to do this in my own project, so I wrote a little piece of code for it with a RubyOnRails integration test.

You can use it like this:

 FeedDetector.fetch_feed_url('http://blog.dominiek.com/')
 => "http://blog.dominiek.com/feed/atom.xml"
 FeedDetector.fetch_feed_url('http://blog.dominiek.com/feed/atom.xml')
 => "http://blog.dominiek.com/feed/atom.xml"
 FeedDetector.fetch_feed_url('http://www.flickr.com/photos/dominiekterheide/', :rss)
 => "http://api.flickr.com/services/feeds/photos_public.gne?id=71386598@N00&amp;lang=en-us&format=rss_200"
 # alternatively you can parse HTML with FeedDetector.get_feed_path(html_data)
 # see integration test for more examples

FeedDetector + Test

Excuse my quick mash code. The FeedDetector (lib/feed_detector.rb):


require 'net/http'

class FeedDetector

  ##
  # return the feed url for a url
  # for example: http://blog.dominiek.com/ => http://blog.dominiek.com/feed/atom.xml
  # only_detect can force detection of :rss or :atom
  def self.fetch_feed_url(page_url, only_detect=nil)
    url = URI.parse(page_url)
    host_with_port = url.host
    host_with_port << ":#{url.port}" unless url.port == 80
    req = Net::HTTP::Get.new(url.path)
    # something fishy going on with URI.host
    res = Net::HTTP.start(url.host.gsub(/:[0-9]+/, ''), url.port) {|http|
      http.request(req)
    }
    feed_url = self.get_feed_path(res.body, only_detect)
    feed_url = "http://#{host_with_port}/#{feed_url.gsub(/^\//, '')}" unless !feed_url || feed_url =~ /^http:\/\// 
    feed_url || page_url
  end

  ##
  # get the feed href from an HTML document
  # for example:
  # ...
  # <link href="/feed/atom.xml" rel="alternate" type="application/atom+xml" />
  # ...
  # => /feed/atom.xml
  # only_detect can force detection of :rss or :atom
  def self.get_feed_path(html, only_detect=nil)
    unless only_detect && only_detect != :atom
      md ||= /<link.*href=['"]*([^\s'"]+)['"]*.*application\/atom\+xml.*>/.match(html) 
      md ||= /<link.*application\/atom\+xml.*href=['"]*([^\s'"]+)['"]*.*>/.match(html) 
    end
    unless only_detect && only_detect != :rss
      md ||= /<link.*href=['"]*([^\s'"]+)['"]*.*application\/rss\+xml.*>/.match(html) 
      md ||= /<link.*application\/rss\+xml.*href=['"]*([^\s'"]+)['"]*.*>/.match(html) 
    end
    md && md[1]
  end

end

The integration test (test/integration/feed detector test.rb:


require "#{File.dirname(__FILE__)}/../test_helper"


class FeedDetectorTest < ActionController::IntegrationTest

  def test_fetch_feed_url
    return # uncomment me to test HTTP fetching

    # test mephisto
    feed_url = FeedDetector.fetch_feed_url('http://blog.dominiek.com/')
    assert_equal('http://blog.dominiek.com/feed/atom.xml', feed_url)
    # test wordpress
    feed_url = FeedDetector.fetch_feed_url('http://digigen.nl/')
    assert_equal('http://digigen.nl/feed/', feed_url)

    # test non conventional port
    feed_url = FeedDetector.fetch_feed_url('http://blog.dominiek.com:8000/')
    assert_equal('http://blog.dominiek.com:8000/feed/atom.xml', feed_url)

    # test only_detect rss/atom on flickr
    feed_url = FeedDetector.fetch_feed_url('http://www.flickr.com/photos/dominiekterheide/', :atom)
    assert_equal('http://api.flickr.com/services/feeds/photos_public.gne?id=71386598@N00&amp;lang=en-us&format=atom', feed_url)
    feed_url = FeedDetector.fetch_feed_url('http://www.flickr.com/photos/dominiekterheide/', :rss)
    assert_equal('http://api.flickr.com/services/feeds/photos_public.gne?id=71386598@N00&amp;lang=en-us&format=rss_200', feed_url)

    # make sure that feeds return themselves
    feed_url = FeedDetector.fetch_feed_url('http://blog.dominiek.com/feed/atom.xml')
    assert_equal('http://blog.dominiek.com/feed/atom.xml', feed_url)
    feed_url = FeedDetector.fetch_feed_url('http://digigen.nl/feed/')
    assert_equal('http://digigen.nl/feed/', feed_url)
  end

  def test_get_feed_path
    body = []
    body << ' <html>'
    body << '  <head>'
    body << '   <link href="/super.css" rel="alternate" type="text/css"/>'
    body << '   <link href="/feed/atom.xml" rel="alternate" type="application/atom+xml" />'
    body << '  </head>'
    body << ' </html>'

    # Mephisto
    feed_path = FeedDetector.get_feed_path(body.join("\n"))
    assert_equal('/feed/atom.xml', feed_path)
    body[3] = '   <link href=\'/feed/atom.xml\' rel="alternate" type="application/atom+xml" />'
    feed_path = FeedDetector.get_feed_path(body.join("\n"))
    assert_equal('/feed/atom.xml', feed_path)

    # FlickR
    body[3] = '<link rel="alternate" type="application/atom+xml" title="Flickr: Photos from dominiekth Atom feed" href="http://api.flickr.com/services/feeds/photos_public.gne?id=71386598@N00&amp;lang=en-us&format=atom">'
    feed_path = FeedDetector.get_feed_path(body.join("\n"))
    assert_equal('http://api.flickr.com/services/feeds/photos_public.gne?id=71386598@N00&amp;lang=en-us&format=atom', feed_path)
          body[4] = '<link rel="alternate"   type="application/rss+xml" title="Flickr: Photos from dominiekth RSS feed" href="http://api.flickr.com/services/feeds/photos_public.gne?id=71386598@N00&amp;lang=en-us&format=rss_200">'
    feed_path = FeedDetector.get_feed_path(body.join("\n"))
    assert_equal('http://api.flickr.com/services/feeds/photos_public.gne?id=71386598@N00&amp;lang=en-us&format=atom', feed_path)
    feed_path = FeedDetector.get_feed_path(body.join("\n"), :rss)
    assert_equal('http://api.flickr.com/services/feeds/photos_public.gne?id=71386598@N00&amp;lang=en-us&format=rss_200', feed_path)

    # Wordpress
    body[3] = '<link rel="alternate" type="application/rss+xml" title="Digigen RSS Feed" href="http://digigen.nl/feed/" />'
    body[4] = ' </head>'
    feed_path = FeedDetector.get_feed_path(body.join("\n"), :atom)
    assert_equal(nil, feed_path)
    feed_path = FeedDetector.get_feed_path(body.join("\n"), :rss)
    assert_equal('http://digigen.nl/feed/', feed_path)
  end

end

I'm sure this might be useful to some people so Enjoy!

RubyEnRails 2007

on June 12, 2007

Last Thursday was the second RubyOnRails conference in the Netherlands. Conveniently, it was four minutes walking from my apartment in Amsterdam. RubyEnRails was organized and sponsored by recruitment companies that have RubyOnRails fever. The main sponsor was Adnexus the company by Brett Dawkins, a very active Dutch RoR scener.

Conference day

The conference was opened by Dr Nic Williams who gave two nice lectures that day. I knew Dr Nic because he has a nice blog and made nice comments about my Shuriken script. It appears Dr Nic is very fond of extending the syntax of Ruby by using 'magic' tricks like methodmissing or constmissing.

There were about 200 people at the conference. There was a mixed audience of PHP developers, Java developers and Sysop gothics. Also there was a fair percentage of recruiters, some of them with no clue. What really surprised me was the low amount of people that had actual production experience with rails. I suspect there weren't more than 20. During the lunch break, Dr Nic even admitted that he codes Java for a living.

Interesting cases

There were two interesting real-life cases nonetheless:

  • Wakoopa.com, a 'what software do you use' social networking site by Robert Gaal (blueace.nl)
  • Nedap's MovesOnRails, a health-care planning application by the Dutch Devices Factory Corp (Nederlandse Apparatenfabriek).

Also, I had a fruitful conversation with the director of Nedforce, someone who really does have a clue.

Noted techniques

These are some things that I heard and are more or less new to me:

  • BackgrounDRB a distrubuted ruby job scheduler for rails
  • Asset Packager a rails plugin that compresses your CSS/JavaScript
  • multi-page (MovesOnRails) They invented this thing that allows you to navigate your layouts like a book. Still waiting for some code though.

Noted tools

  • monit server monitoring tool
  • munin server monitoring tool
  • pingdom.com server monitoring tool 2.0, with SMS notification which is nice for in Europe
  • mailroom a webapp that facilitates mail interaction with team and customers (not quite sure what it is yet, but people like it)

Nintendo Wiiii bakatachiiiiiii :-)

First some notes about IDE’s.

The Knight versus the Ninja

kletch…. kletch…. a loud noise of kletching metal combined with slight floor vibrations left the hall. It was The Knight. His scarred and angry face showed that he was a well experienced warrior, ready for another day of combat for the greater collective cause.

shhh…. shhhh…. what was that? It was probably just the wind. The next day we found the victim dead, with a shuriken-metal-star in his head. The Ninja has made another kill, fighting for the feudal ruler.

If you are the Knight, click here. If you are the agile Ninja, read on.

Using IRB and the Rails Console

A little while ago I read this great article about the Secrets of the Rails Console Ninja’s

My current Rails job in Tokyo required me to use the RadRails IDE on Windows to work on a legacy part the first weeks. The constant switch between script/console, RadRails and my browser inspired me to merge the first two. (Also, sometimes I got rails errors because of :wq symbols in wrong places ;)

I’ve named it Shuriken (the ninja death star) the tool for Rails Console Ninja’s.

You can download it as a .irbrc file which you can just place in your homedir.

download .irbrc

Shuriken

Downloading shuriken, place .irbrc in your homedir.

dodo@membrane$ wget http://www.darkwired.org/~dodo/.irbrc
Create a project or use an existing one and startup the console.
dodo@membrane$ rails webapp > /dev/null; cd webapp
dodo@membrane$ ./script/console
Loading development environment.
Loading IRB shuriken technique
rails development>>
Creating a controller from IRB, after creation we can use it right away.
rails development>> script/generate controller skinny info
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/skinny
      create  test/functional/
      create  app/controllers/skinny_controller.rb
      create  test/functional/skinny_controller_test.rb
      create  app/helpers/skinny_helper.rb
      create  app/views/skinny/info.rhtml
# => true
rails development>> SkinnyController.instance_methods.index("info")
# => 175
Display an overview of controllers:
rails development>> map controllers
  SkinnyController
# => "app/controllers" 
rails development>> map c
  SkinnyController
# => "app/controllers" 
Use vim – the editor from heaven – to open the controller
rails development>> vim skinny_controller
# => "app/controllers/skinny_controller.rb" 
Displaying views (models is also possible)
rails development>> map views # or 'map v'
  Layouts
  Skinny/Info
# => "app/views" 
Using subversion ‘svn ste’ is an alias for: “svn status | grep -v ’?
rails development>> svn ste '" 
A      .
A      README
M      app/controllers/skinny_controller.rb
# => "app/views" 
Executing commands.
rails development>> !uname
Linux
# => true
Unit testing (or selenium testing).
rails development>> map tests # or 'map t'
  Functional/SkinnyControllerTest
# => "test" 
rails development>> rake test
...
Running in production mode:
dodo@membrane$ ./script/console production
Loading production environment.
Loading IRB shuriken technique
rails production>>

TODO

  • get feedback from users
  • add these commands to IRB auto-completer (readline)
  • add more useful stuff, then cleanup code

Note to my female readers: Sorry, I’m addicted to programming

Because of my UNIX backgrounds I feel most comfortable working in the black (My girlfriends’ terminology of ‘a terminal’). Also, I’m a frequent IRC chat user. Actually, after booting up my computer, the first thing I do is re-attach my IRC session. Therefore I think it’s only adequate for me to make my personal manager available ‘in the black’.

This weekend I couldn’t resist taking a few hours to quick-and-dirty mashup these:

  • Backpack IRC interface (bot)
  • Backpack Command Line Interface (cli)

code: backpack_tt-290107.tar.gz (written in Ruby of course)

Since my domain darkwired.org is only used for IRC (chatting) I decided to write a web interface. The Web interface uses the EyeRC API and is built on RubyOnRails.

Currently, you can join our test channel on http://www.darkwired.org/ :

I have no intention to develop it any further, however, I might take some time to finish it off (sourcecode):

  • refactoring
  • unit test case

I hope this was the last IRC client I ever wrote :)

(related code: BASH IRC Client )

Code update: Ruby-EyeAreSee

on January 18, 2007

At the moment I’m working on a little web-IRC client. My intention was to use the Ruby-IRC Framework, but I’ve decided to write my own API for the following reasons:

  • It is impossible to establish multiple connections (multi-user) in the same process
  • Event-handlers can not be flexible.
  • After disconnecting it’s almost impossible to reconnect using the same instance or a new one.

Now I have a slim SSL capable IRC API that fulfills all my needs.

Click here to view examples and documentation

Click here to download the API (Beer-Ware Licensed)

example IRC client:

require 'EyeAreSee'

irc = EyeAreSee.new("EyeAreSee", "irc.darkwired.org", :ssl => true, :channel => "#test")

# catch all lines for debugging
irc.on_message { |line|
  puts "debug: "+line if $DEBUG
}

# catch all server messages, eg nickname already in use
irc.on_server_message { |event|
  next if event[:code] == 372
  puts event[:code].to_s+": "+event[:message]
}

# handle 372 messages (Message of the Day)
irc.on_server_message(372) { |event|
  puts "motd: "+event[:message]
}

# display all messages directed to the #test channel
irc.on_message("privmsg", :to => "#test") { |event|
  puts event[:from_nickname].to_s+" says: "+event[:message].to_s
}

# auto-response to private message from specific user
irc.on_message("privmsg", :to => "EyeAreSee", :from_nickname => "Drakonen") { |event|
  sleep(rand*10)
  responses = ["hmmmz", "boeiend", "interessant"]
  irc.message event[:from_nickname], responses[(rand*3).to_i]
}

# display all messages directed to the #test channel
irc.on_message("privmsg", :to => "#test", :message => "please go away EyeAreSee") { |event|
  irc.message("#test", "OK "+event[:from_nickname]+", bye everyone!") 
  irc.quit
}

irc.start
irc.start #reconnect once after a quit

Code update: Ruby XMMS Hack

on January 13, 2007

Control XMMS (nix audio player) without any third party libraries (No Ruby-XMMS, no libxmms).

Why? This might be useful for some specific cases. My case: I needed to know the low-level protocol in order to port it to a different language. This Ruby script is a quick hack to test my understanding of the protocol.

xmms_hack.rb

Code update: Ruby-IRC SSL Hack

on January 13, 2007

I haven’t been doing much programming lately since I had to write my final thesis. Sometimes however, I cannot resist coding a little piece :]

Ruby-IRC is an IRC Framework which is still in the BETA stage (what isn’t?). I wanted to write a little piece of code to connect with my IRC server: irc.darkwired.org. Fortunatle, Darkwired IRC requires SSL connection, so I had to hack the Framework a little.

Example code ( ssl_irc_example.rb )

 
require 'rubygems'
require 'IRC'
require 'IRCSSLConnection'

$IRC_DEBUG = true
bot = IRC.new("irc20", "irc.darkwired.org", "9999", "irc20")
IRCEvent.add_callback('endofmotd') { 
    |event| bot.add_channel('#rtest') 
}
IRCEvent.add_callback('join') { |event|
    bot.send_message(event.channel, "Bonjour! Ik ben #{event.from}")
}

bot.connect
 

How to use?

1. download IRCSSLConnection.rb from one of my junk archives.

2. EITHER: You can replace the FrameWork’s IRCConnection.rb file with this new one (eg cp IRCSSLConnection.rb /usr/lib/ruby/gems/1.8/gems/Ruby-IRC-1.0.7/lib/IRCConnection.rb)

2. OR: Just leave it in the directory of your script and include it

Diversity VS uniformity

on September 04, 2006
There are many different programming languages out there. Each of them have profound cultural implications to their programmers. Programmers that learn new languages every once in a while find themselves travelling through different geek-culture warzones. Since one year I settled down in Ruby land. For me this language is unique, unlike any other language I have ever seen. One could view Ruby as the mechanical version of a natural language, because it allows people to extend this computer language like a natural language.

Python and Ruby programmer’s mostly oppose eachother, although they can agree on one thing: PHP is the devil. Python and Ruby branched from the same ancestral tree but have significant cultural impacts. Like Java, Python embraces the principle of uniformity, whereas Ruby embraces the principle of diversity. One could argue that the latter might be more suitable for most of the current (european) political models :)

Now, I’m not saying that Python is for communists. Actually, I’d rather not dirty my hands on politics. But I do think these principles have significant cultural and economical consequences, respectively these are the working environment and the productivity.

I’m highly convinced that Ruby has a much greater potential for both. Programmers can program in their own style, they can even look at it as art. Like a columnist loves writing articles, programmers love writing their kung-fu code. I wonder wether this same columnist can have the same artistic joy in China.

David Heinemeier Hansson, the inventor of RubyOnRails, says beauty leads to happiness, hapiness leads to productivity . I do agree that the programmer’s freedom in creating this hapiness contributes to the productivity, but I don’t think it’s enough. To achieve the full potential of Ruby’s productivity, we need to adjust our development methodologies to this awesome language. Rails makes a good start by facilitating for example unit testing.

In short, I think Ruby is THE language for the new software world and a gateway to new ways of thinking.

In my last post I released a sidebar plugin for this blog (Typo). The plugin uses an API which I coded: ruby-GalleryRemote. This is used for accessing the Gallery2 Software remotely.

Shashin is an offline gallery-admin/blogger for travelers. Since I will go backpacking this summer with some friends we realized we needed a tool like this. The project is also used to get school credits for the Design Patterns course.

http://shashin.rubyforge.org/