public
Description: Tumblebot that feeds ni.hili.st. Plays well with Autumn Leaves.
Homepage: http://ni.hili.st/post/20314520
Clone URL: git://github.com/flogic/nihilist_bot.git
README.autumn-leaves
= Autumn Leaves
<b>Version 1.0.1 (Sep 21, 2007)</b>

Author::    Tim Morgan (mailto:riscfuture@gmail.com)
Copyright:: Copyright (c)2007 Tim Morgan
License::   Distributed under the same terms as Ruby. Portions of this code are
            copyright (c)2004 David Heinemeier Hansson; please see
      libs/inheritable_attributes.rb for more information. Portions of
      this code were written by Dominiek ter Heide; please see
      libs/EyeAreSee.rb for more information.

Autumn Leaves is a full-featured framework on top of which IRC bots (called
"leaves") can be quickly and easily built. It features a very Ruby-like approach
to bot-writing, a complete framework for loading and daemonizing your bots,
multiple environment contexts, and painless logging support.

== Requirements

Autumn Leaves requires RubyGems (http://www.rubygems.org) and the Daemons gem
(http://daemons.rubyforge.org). Install RubyGems then run <tt>sudo gem install
daemons</tt> in a command line in order to run Autumn Leaves.

== Directory Structure

An Autumn Leaves  installation is like a Ruby on Rails installation: There is a
certain directory structure where your files go. For Autumn Leaves, a base
install looks like:

* big-switch.rb - The daemon controller; starts and stops Autumn Leaves
* genesis.rb - The Autumn Leaves bootloader; big-switch.rb launches this code as a daemon
* <b>leaves/</b> - Your bots are placed in this direcotry
  * scorekeeper.rb - Example bot
* <b>config/</b> - Configuration files and
  * global.yml - Universal settings that apply to every season
  * <b>seasons/</b> - Contains directories for each season (see "Seasons")
    * <b>testing/</b> - Example season
      * leaves.yml - Example bot configuration file
      * season.yml - Season configuration
* <b>data/</b> - Directory where bot-specific data is stored
* <b>doc/</b> - HTML documentation generated by RDoc
* <b>libs/</b> - Additional Autumn Leaves helper objects
  * EyeAreSee.rb - The EyeAreSee IRC library, from which Autumn Leaves is derived
  * inheritable_attributes.rb - Adds support for class-level inheritable attributes
  * loader.rb - The Foliater class, which loads bots into their own threads
  * logger.rb - The ObservantSquirrel class, which logs bot events to a file
  * leaf.rb - The core bot superclass
* <b>log/</b> - Directory where (most) Autumn Leaves logs are written (see the "Logs" section)
* README - This file
* <b>script/</b> - Helper scripts you can use when developing or maintaining your bots
  * check-errors.sh - Displays a summary of any errors appearing in the logs.
  * make-docs.sh - Generates the RDoc documentation into the doc/ directory.
  * zap-logs.sh - Removes all log files
* <b>support/</b> - Additional code your bots need

=== Additional Files You Might See

* autumn-leaves.pid
* autumn-leaves_monitor.pid

These files are created by big-switch.rb for keeping track of the daemon
process and its monitor.

* autumn-leaves.output
* autumn-leaves.log

These are log files that are generated by the daemon library. Generally the
kinds of things you will see in these log files, if they appear, are exceptions
in bad places (such as before the logger could be completely loaded) or output
to standard out when running as a daemon.

== Configuring Autumn Leaves for Your First Launch

Before you can run Autumn Leaves and try out the example leaf, Scorekeeper,
you'll need to set up a few things. Here are the steps:

=== Configure Your Testing Season

Edit the config/season/testing/leaves.yml file. The season directory is where
you set up different contexts for the leaves to run in. For instance, in a
testing season, your leaves might join a different channel, so you don't spam up
a busy channel with debug messages.

The layout of the leaves.yml file is fairly obvious. Configure it to run the
leaf on your favorite IRC server and a private channel that won't disturb
others. If you have a particular nick and/or NickServ password you'd like the
leaf to use, add a +nick+ and +password+ field, like so:

 - type: Scorekeeper
   server: irc.yourircserver.com
   channels:
   - yourchannel
   nick: Mybot
   password: mypassword

The leaf will take the name Mybot and send a "/msg NickServ IDENTIFY mypassword"
command upon starting up.

=== Start the Daemon

Run the shell command <tt>big-switch.rb start</tt> to start the daemon. After a
short while, your leaf should appear in the channel you specified. You can type
"!points Coolguy +5" and then "!points" to get started using it. Have some fun,
and when you're satisfied, stop the daemon using <tt>big-switch.rb stop</tt> in
your shell.

== Making Your Own Leaf

Making your own leaf using Autumn Leaves is easy. In this tutorial, I'll show
you how to make a simple Fortune bot that responds to a few basic commands.

=== Step 1: Subclass AutumnLeaf

Create a new file in the leaves/ directory called <b>fortune.rb</b>. In this
file you'll create a +FortuneBot+ class that subclasses AutumnLeaf:

 class FortuneBot < AutumnLeaf
   FORTUNES = [
     "You will make someone happy today.",
     "Someone you don't expect will be important to you today.",
     "Today will bring unexpected hardships."
   ]
 end

As you can see, our 3 meager fortunes are stored in the +FORTUNES+ class
constant.

Of course, any self-respecting fortune bot announces its presence when it starts
up, so, in your +FortuneBot+ class, override the AutumnLeaf#did_start_up method
to display a cheerful greeting:

 def did_start_up
   message 'FortuneBot at your service! Type "!fortune" to get your fortune!'
 end

Now, we'll want it to actually respond to the "!fortune" command, and all you
have to do is create a method called +fortune_command+ to make it work:

 def fortune_command(sender, channel, msg)
   fortune = FORTUNES[rand(FORTUNES.size)]
   message fortune, channel
 end

...and that's it! You now have a fully functional fortune bot featuring -- not
two -- but <i>three</i> unique and exciting fortunes!

=== Step 2: Add the Leaf to Your Season

If you want, you can add the fortune bot to your leaves.yml file to try it out.
Adding a leaf is easy; simply duplicate the structure used for Scorekeeper's
entry and change the values as appropriate. A typical two-leaf configuration
will look like:

 - type: Scorekeeper
   server: irc.someircserver.org
   channels:
   - bottesting
   password: mypassword
 - type: FortuneBot
   server: irc.someircserver.org
   channels:
   - bottesting

Note the tricksy little dash to indicate a new leaf. Also note that we didn't
specify the leaf's nick -- in that case, it gets its nick from the type. Your
leaf will be named "FortuneBot" when it comes online.

=== Step 3: Restart Autumn Leaves

Simply use the command <tt>big-switch.rb restart</tt> to take your leaves
offline and back on. This time, your new leaf will be loaded along with
Scorekeeper.

== Autumn Leaves's Many Features

Autumn Leaves has some tools to help you write your leaves. These include things
like multiple environments, logging, and an easy to use IRC library. The class
docs are the most thorough way of learning about each of these features, but
I'll walk you through the basics here.

=== The Tools in the Autumn Leaves Class

By subclassing AutumnLeaf, you gain access to a number of neat utilities.
These generally come in four classes: IRC actions your bot can perform, IRC
commands that have already been written for you, helper methods you can call,
and invoked methods you can override. Helper methods do things like colorize
your IRC text. Invoked methods are called when certain events happen, like when
your leaf starts up or when a private message is received. You override them in
your leaf to customize how it responds to these events.

<b>IRC actions</b>:: AutumnLeaf#message, AutumnLeaf#join_channel,
                     AutumnLeaf#grant_user_privilege, etc.
<b>Invoked methods</b>:: AutumnLeaf#will_start_up, AutumnLeaf#did_start_up,
                         AutumnLeaf#did_receive_channel_message, etc.
<b>Invoked error methods</b>:: AutumnLeaf#banned_from_channel,
                               AutumnLeaf#nickname_in_use, etc.
<b>Helper methods</b>:: AutumnLeaf#color, AutumnLeaf#record, etc.
<b>IRC commands</b>:: AutumnLeaf#quit_command, AutumnLeaf#reload_command,
                      AutumnLeaf#alabout_command, etc.

=== Seasons

Each time you start Autumn Leaves with the big-switch.rb file, the process
launches in a certain season (a.k.a. environment context). This season is
defined in the config/global.yml file.

It's important to realize that an season is just a name, nothing more. You can
have as many seasons as you like, and name them anything that you like. Autumn
Leaves will load the leaves.yml file for the season you've indicated as active.
Autumn Leaves doesn't really care if it's named "production" or "live" or
"testing-on-jeffs-machine"; it's all the same to Autumn Leaves.

Your season's configuration is stored in the season.yml file within your season
directory. Currently it supports one directive, +logging+. When set to "debug",
the Observant Squirrel will log all messages, and will parrot output to the
console. When set to "production", the Observant Squirrel will not log debug
messages, and will not parrot to the console. (See the next section.)

=== Autumn Leaves's Logging

Autumn Leaves uses the ObservantSquirrel class to log. The Observant Squirrel
logs information to a file named after the current season. If your process is
running in the "testing" season, log entries will go to the log/testing.log
file. The Observant Squirrel can also send log output to standard out for
debugging (see "Debugging Your Leaf"). For more information, see the
ObservantSquirrel class docs.

=== Your Own Configuration

==== System-wide: The global.yml File

You can add your own system-wide configuration by simply editing the
config/global.yml file. Anything you add to that file will be available in the
<tt>$AL_ENV</tt> global variable. For instance, if you add the following YAML
code to your global.yml file:

 awesome_users:
 - Sparky
 - crushx
 - Zaph_zomg

You could then write this command into your leaf, for example:

 def awesome_command(sender, channel, msg)
   if $AL_ENV['awesome_users'].include? sender then
     message "#{sender} is totally awesome!", channel
   else
     message "Sorry #{sender}, but you need to be more awesome.", channel
   end
 end

==== Season-local: The season.yml File

If you needed season-specific configuration, you could place it the season.yml
file within your season's directory. This information can be accessed from the
<tt>$YAB_ENV['season_config']</tt> global variable. For instance, consider that
you have a testing and production season. In your testing season, your
season.yml file contains:

 dont_http: true

and in production, it contains:

 dont_http: false

Now, in your code, you might have a method like:

 def scores_command(sender, channel, msg)
   if $AL_ENV['season_config']['dont_http'] then
     message "Some fake sports scores."
   else
     # go on the web and find real sports scores
   end
 end

==== Leaf-specific: The leaves.yml File

The leaves.yml file includes a few standard directives:

<tt>type</tt>:: The leaf's class
<tt>server</tt>:: The IRC server the leaf will log into
<tt>port</tt>:: The port that the IRC server runs on
<tt>channel</tt>:: A single channel to join
<tt>channels</tt>:: A list of channels to join
<tt>nick</tt>:: A nickname to use (by default, the leaf's class name)
<tt>password</tt>:: A password to send to NickServ to authenticate the leaf's
                    nick
<tt>rejoin</tt>:: If true, the leaf will rejoin its channel when kicked from it
<tt>respond_to_private_messages</tt>:: If true, the leaf will respond to known commands that it receives through private 
message. In this situation, the +channel+ parameter for your <tt>*_command</tt> method would be equal to your bot's 
nick.

The <tt>channel</tt> and <tt>channels</tt> directives can also be used to
specify a password for a password protected channel, like so:

 channel:
   channelname: channelpassword

or

 channels:
 - channel1: password1
 - channel2: password2

Any information you add to the current season's leaves.yml file is stored in
the leaf's <tt>@options</tt> instance variable. If your leaf's YAML file is:

 - type: Scorekeeper
   server: irc.someserver.org
   channels:
   - somechannel
   botmaster: Sanchosample

then you could retrieve that information in your leaf's code like so:

 @options[:botmaster]

Note that you use a Symbol, not a String, to reference this hash. Only the
nonstandard options are stored in <tt>@options</tt>; in other words,
<tt>@options[:server]</tt> will return +nil+. (You can use the +server+ method
to determine your leaf's server; see the AutumnLeaf attributes.)

=== Using Support Modules

Code placed in the support/ directory will automatically be loaded when your
leaves are started. You can place first-class methods and constants here that
will be available to all your leaves. If you'd like more compartmentalization,
you can create specialized helper modules that include only the code each leaf
needs.

For instance, if your leaf is named FortuneBot, and you needed some additional
support code, you could place it in a module called FortuneBotHelper. Code in
this module is automatically added to FortuneBot and is available to each
FortuneBot instance.

== Debugging Your Leaf

If you make a simple code change to your leaf, you can reload it without having
to restart the whole process.  See the AutumnLeaf#reload_command documentation
for more information on when and how you can reload your leaf's code.

If an error occurs on a live production instance, it will be logged to the
log file for your season and the leaf will exit. You can inspect the log file
to determine what went wrong.

If the error happens before the The Observant Squirrel is available, oftentimes
it will appear in the autumn-leaves.output or autumn-leaves.log files. These
files are generated by the daemon library and note any uncaught exceptions or
standard outs.

The most tricky of errors can happen before the process is daemonized. If your
process is quitting prematurely, and you don't see anything in either log file,
consider running the command <tt>ruby genesis.rb</tt>. This bypasses the
daemon library and runs the program directly from the shell, allowing you to see
any exceptions for yourself.

Unfortunately, it's still possible that the bug might not appear when you do
this, but only appear when the process is daemonized. In this situation, I'd
recommend installing rdebug (<tt>sudo gem install rdebug</tt>) and stepping
through the code to figure out what's going wrong. In particular, make sure you
step into the Foliater's load_leaf method, when it creates the new thread. It's
possible your exception will rear its head once you step into that line of code.

== Getting Ready for Deployment

There's really only two things you need to do once your leaf is ready to greet
the Real World:

1. In config/global.yml, set the season to your production season.
2. If desired, in big-switch.rb, set the <tt>:monitor</tt> option to true. This
   will spawn a monitor process that will relaunch your leaves if they crash.

== Other Information

=== Known Bugs

* The monitor process cannot restart a single leaf that crashes; all the leaves
  must crash before they are restarted.

=== Version History

<b>1.0.1</b> (Sep 21, 2007)
* Added AutumnLeaf#uses_command_prefix method
* CPU usage no longer excessive.
* Bug fixes for problems that could happen when the leaf joins passworded
  channels.
* Portions of EyeAreSee rewritten to be more modern.

<b>1.0</b> (Sep 12, 2007)
* Initial release.