Make your own IM bot in Ruby, and interface it with your Rails app
Jun 26, 2008
In a super secret project I'm currently working on, I've been looking for ways of making it easier for people to interact with the system without the need to log in to the website. That's included the obvious things like having a RESTful API so they can put their own services and interfaces on top, but that only works for developers or 3rd party application providers. What about your average Joe on the street, how come they get forced into having to log in to the site to update their data, etc.? Why not provide a means of carrying out simple tasks in an application they probably already have? And so an Instant Messaging interface was designed to send a instruction to a robot, and it's relatively simple to get setup.
The best thing about it is that we help give more control and time back to the user, hopefully making the system more appealing to use. No more breaking your train of thought while you go log into a site to check something, just a quick message to a bot and you can forget about it. Or conversely, you wont defer the task because the overhead in writing a reminder is possibly more than the time it would take to just do it. And for those fans of the Getting Things Done (GTD) approach to task management, it means a whole range of tasks can be suddenly just 'get done' in the process phase
So how do we give such amazing power and flexibility to our users? First you're going to need to install the xmpp4r gem which at the time of writing doesn't seem to play nice with the new version of rubygems (I think it's to do with the way new rubygems preserves permissions). The problem appears to be fixed in version 0.3.2.99, but only 0.3.2 is available as a gem. So in the interim, we are going to have to download it directly from the git repository and build it ourselves:
git clone git://github.com/ln/xmpp4r.git xmpp4r
cd xmpp4r
rake gem:install
sudo gem install xmpp4r-simple
And now some ruby code. Connecting is straight-forward:
require 'rubygems'
require 'xmpp4r-simple'
messenger = Jabber::Simple.new('my-bot@gmail.com', "bot-password")
As is sending a message:
messenger.deliver("a-real-person@gmail.com", "Why hello there Mr. Person, your bot is here now!")
If you wish to have your service sit around and respond to messages, then something you'll need something like the following:
while true
messenger.received_messages do |msg|
puts msg.body
messenger.deliver("a-real-person@gmail.com", "Got your message, thanks!")
end
sleep 2
end
Now just to point out for those that aren't paying close attention, the above code does the following:
- Creates a loop that we will continually go over
- Call received_messages and pass any messages into a block, iterating over each one at a time
- Stop processing for 2 seconds so the system can take a break and we don't use up all of the resources
Of particular importance once again, I've created an infinite loop here to listen to incoming messages. DO NOT put this code in a rails app, this is the kind of process that needs to be handled outside of rails as a supporting but background task. If you try and kick it off via rails it will process indefinitely and tie up one of your mongrel instances until it dies and likely degrade the performance of your site in the process.
That's all well and good, but wasn't the whole point of this to give our users a more convenient way to interface with our site and their data? Let us have a look a a slightly more useful implementation:
require 'rubygems'
require 'xmpp4r-simple'
messenger = Jabber::Simple.new('my-bot@gmail.com', "bot-password")
while true
messenger.received_messages do |msg|
user = User.find_by_im_name(msg.from)
if user
case msg.body
when /^help /i
messenger.deliver(msg.from, "Valid commands are......")
when /^status /i
user.status = msg.body.sub(/^status /i)
user.save
messenger.deliver(msg.from, "Thanks #{user.first_name}, your status has been updated")
when /^balance\?/i
messenger.deliver(msg.from, "#{user.first_name}, your current remaining balance is #{user.remaining_balance}")
else
messenger.deliver(msg.from, "Sorry #{user.first_name}, I didn't understand that. Message me with 'help' for a list of commands")
end
else
messenger.deliver(msg.from, "Sorry, but we've not got this account registered on our system. Sign-up or update your details at http://www.mysite.com/")
end
end
sleep 2
end
The easiest way to have this run now is to call it via script/runner so that we can get access to our ActiveRecord models and interface with the underlying data. All you need to do is kick this off with an init script, setup something like god for process monitoring to ensure it stays alive and keeps within some same memory and CPU constraints.
Previously I led the Terraform product team @ HashiCorp, where we launched Terraform Cloud and set the stage for a successful IPO. Prior to that I was part of the Startup Team @ AWS, and earlier still an early employee @ Heroku. I've also invested in a couple of dozen early stage startups.