Twibot: A microframework for Twitter bots in Ruby

Twibot 0.1.1 hit the RubyForge gem server today. Inside is a microframework for writing Twitter bots in Ruby, using a DSL heavily inspired by Sinatra.

Twitter bots?

Sure, why not? Twitter bots are, much like irc bots, software that interacts with users by monitoring activity and reacting to certain "commands". An example of a Twitter bot is TweetCC, an automated user that enables Twitter users to CC license their tweets.

Twibot is a microframework for creating such bots using Ruby.

Installation

Twibot is available through RubyForge. However, Twibot requires the latest Twitter4r, v0.3.1, which is currently only available through GitHub. This means you have to add the GitHub gem server to your sources in order to install Twibot:

gem sources -a http://gems.github.com
sudo gem install twibot

Usage

The easiest way to use Twibot is through it's Sinatra inspired DSL. Instead of HTTP verbs, Twibot offers three actions, message (for receiving direct messages), reply (for receiving @replies) and tweet (for receiving any public tweet in your bots timeline).

require 'twibot'

# Receive messages, and tweet them publicly
#
message do |message, params|
  post_tweet message
end

# Respond to @replies if they come from the right crowd
#
reply :from => [:cjno, :irbno] do |message, params|
  post_tweet "@#{message.user.screen_name} I agree"
end

# Listen in and log tweets
#
tweet do |message, params|
  MyApp.log_tweet(message)
end

The "verbs" take two optional parameters, a string "route" (explained later) and a hash of conditions. Currently, the only condition you can set is :from, which takes either a single string or symbol, or an array of strings/symbols denoting usernames. When using :from, messages and tweets will only be processed if they originate from either of the allowed users.

The "verb" methods also accept a block which will handle the messages/tweets. This block should accept two parameters of its own: the message (or tweet) and a params hash (mapped to "routes", explained below). The messages are Twitter::Message instances, and tweets are Twitter::Status instances.

As seen in the above example, Twibot also provides a shortcut for posting tweets:

post_tweet message

The message object may be a string, or anything which has a text property (such as Twitter::Message and Twitter::Status objects).

Running the bot


To run the bot, simply do:

ruby bot.rb

Configuration

Twibot looks for a configuration file in ./config/bot.yml. If it exists, it should contain atleast:

login: twitter_login
password: twitter_password

You can also pass configuration as command line arguments:

ruby bot.rb --login myaccount


…or configure with Ruby:

configure do |conf|
  conf.login = "my_account"
do

If you don’t specify login and/or password in any of these ways, Twibot will prompt you for those if you have the highline gem installed.

If you want to change how Twibot is configured, you can setup the bot instance manually and give it only the configuration options you want:

# Create bot only with default configuration
require 'twibot'
bot = Twibot::Bot.new(Twibot::Config.default)

# Application here...

If you want command line arguments you can do:

require 'twibot'
bot = Twibot::Bot.new(Twibot::Config.default << Twibot::CliConfig.new)

As you can see, configuration objects can be chained and so create a cascading system. In the above case, settings will be retrieved from the Twibot::CliConfig instance if it exists, else it'll be fetched from the default configuration.

"Routes&"

Like Sinatra, and other web app frameworks, Twibot supports "routes": patterns to match incoming tweets and messages:

require 'twibot'

tweet "time :country :city" do |message,params|
  time = MyTimeService.lookup(params[:country], params[:city])
  client.message :post, "Time is #{time} in #{params[:city]}, #{params[:country]}"
end

The above example will catch public tweets like time norway oslo, and reply back to the user with the time in his area with a direct message. The params hash provides easy access to values captured by your routes, so in the above example, the following would hold true:

params[:country] == "norway"
params[:city] == "oslo"

You can have several tweet blocks (or message or reply). The first one to match an incoming tweet/message will handle it.

Working with the Twitter API

The Twitter API is provided by the excellent Twitter4R. The DSL gives you access to your Twitter client instance through client (or twitter):

message do
  twitter.status :post, "Hello world" # Also: client.status :post, "Hello world"
end

Polling

Twitter pulled the plug on it’s xmpp service last year. This means that Twibot backed bots needs to poll the Twitter service to keep up. Twitter has a request limit on 70 reqs/hour, so you should configure your bot not to make more than that, else it will fail. You can ask for your bot account to be put on the white list which allows you to make 20.000 reqs/hour, and shouldn’t be a problem so long as your intentions are good (I think).

Twibot polls like this:

As long as Twibot finds any messages and/or tweets, the interval stays the same ( min_interval configuration switch). If nothing was found however, the interval to sleep is increased by interval_step configuration option. This happens until it reaches max_interval, where it will stay until Twibot finds anything (in which case the interval is rewinded to min_interval).

Upcoming features

Daemons

Twibot is at 0.1.1, and I have several plans for it. The one thing most important to myself is to add a -d switch which will run the bot as a background process. I've made some tests using the Daemons gem, but haven't landed on a solution I like yet. In the meantime you can easily daemonize your bot by creating a wrapper script:

require 'daemons'

Daemons.run("my_bot.rb")

Where my_bot.rb is your Twibot backed bot.

Ruby 1.9

I also really want Twibot to run on 1.9, which it doesn't quite yet. I'm using Context for Twibots test suite, and Context has it's own issues with 1.9, so I haven't gotten around to it yet. If you want to help, fork away!

Regex routes

It would be nice if the routes could be arbitrary regular expressions. This is a fairly simple feature to add in, and will be added soon-ish.

Get it while it's hot!

Twibot is MIT licensed (do anything you want with it, no guarantees).

Hope someone finds this useful!

Published 15. March 2009 in ruby, github, open source og twibot.

Possibly related