comboy-autumn 3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.textile +1192 -0
- data/autumn.gemspec +25 -0
- data/bin/autumn +27 -0
- data/lib/autumn.rb +2 -0
- data/lib/autumn/authentication.rb +290 -0
- data/lib/autumn/channel_leaf.rb +107 -0
- data/lib/autumn/coder.rb +166 -0
- data/lib/autumn/console_boot.rb +9 -0
- data/lib/autumn/ctcp.rb +250 -0
- data/lib/autumn/daemon.rb +207 -0
- data/lib/autumn/datamapper_hacks.rb +290 -0
- data/lib/autumn/foliater.rb +231 -0
- data/lib/autumn/formatting.rb +236 -0
- data/lib/autumn/generator.rb +231 -0
- data/lib/autumn/genesis.rb +191 -0
- data/lib/autumn/inheritable_attributes.rb +162 -0
- data/lib/autumn/leaf.rb +738 -0
- data/lib/autumn/log_facade.rb +49 -0
- data/lib/autumn/misc.rb +87 -0
- data/lib/autumn/script.rb +74 -0
- data/lib/autumn/speciator.rb +165 -0
- data/lib/autumn/stem.rb +919 -0
- data/lib/autumn/stem_facade.rb +176 -0
- data/resources/daemons/Anothernet.yml +3 -0
- data/resources/daemons/AustHex.yml +29 -0
- data/resources/daemons/Bahamut.yml +67 -0
- data/resources/daemons/Dancer.yml +3 -0
- data/resources/daemons/GameSurge.yml +3 -0
- data/resources/daemons/IRCnet.yml +3 -0
- data/resources/daemons/Ithildin.yml +7 -0
- data/resources/daemons/KineIRCd.yml +56 -0
- data/resources/daemons/PTlink.yml +6 -0
- data/resources/daemons/QuakeNet.yml +20 -0
- data/resources/daemons/RFC1459.yml +158 -0
- data/resources/daemons/RFC2811.yml +16 -0
- data/resources/daemons/RFC2812.yml +36 -0
- data/resources/daemons/RatBox.yml +25 -0
- data/resources/daemons/Ultimate.yml +24 -0
- data/resources/daemons/Undernet.yml +6 -0
- data/resources/daemons/Unreal.yml +110 -0
- data/resources/daemons/_Other.yml +7 -0
- data/resources/daemons/aircd.yml +33 -0
- data/resources/daemons/bdq-ircd.yml +3 -0
- data/resources/daemons/hybrid.yml +38 -0
- data/resources/daemons/ircu.yml +67 -0
- data/resources/daemons/tr-ircd.yml +8 -0
- data/skel/Rakefile +135 -0
- data/skel/config/global.yml +2 -0
- data/skel/config/seasons/testing/database.yml +7 -0
- data/skel/config/seasons/testing/leaves.yml +7 -0
- data/skel/config/seasons/testing/season.yml +2 -0
- data/skel/config/seasons/testing/stems.yml +9 -0
- data/skel/leaves/administrator/README +20 -0
- data/skel/leaves/administrator/controller.rb +67 -0
- data/skel/leaves/administrator/views/autumn.txt.erb +1 -0
- data/skel/leaves/administrator/views/reload.txt.erb +11 -0
- data/skel/leaves/insulter/README +17 -0
- data/skel/leaves/insulter/controller.rb +65 -0
- data/skel/leaves/insulter/views/about.txt.erb +1 -0
- data/skel/leaves/insulter/views/help.txt.erb +1 -0
- data/skel/leaves/insulter/views/insult.txt.erb +1 -0
- data/skel/leaves/scorekeeper/README +34 -0
- data/skel/leaves/scorekeeper/config.yml +2 -0
- data/skel/leaves/scorekeeper/controller.rb +104 -0
- data/skel/leaves/scorekeeper/helpers/general.rb +64 -0
- data/skel/leaves/scorekeeper/models/channel.rb +12 -0
- data/skel/leaves/scorekeeper/models/person.rb +14 -0
- data/skel/leaves/scorekeeper/models/pseudonym.rb +11 -0
- data/skel/leaves/scorekeeper/models/score.rb +14 -0
- data/skel/leaves/scorekeeper/tasks/stats.rake +17 -0
- data/skel/leaves/scorekeeper/views/about.txt.erb +1 -0
- data/skel/leaves/scorekeeper/views/change.txt.erb +5 -0
- data/skel/leaves/scorekeeper/views/history.txt.erb +11 -0
- data/skel/leaves/scorekeeper/views/points.txt.erb +5 -0
- data/skel/leaves/scorekeeper/views/usage.txt.erb +1 -0
- data/skel/script/console +34 -0
- data/skel/script/daemon +29 -0
- data/skel/script/destroy +48 -0
- data/skel/script/generate +48 -0
- data/skel/script/server +15 -0
- metadata +170 -0
data/README.textile
ADDED
@@ -0,0 +1,1192 @@
|
|
1
|
+
h1. Autumn: A Ruby IRC Bot Framework
|
2
|
+
|
3
|
+
*Version 3.1 (Apr 4, 2008)*
|
4
|
+
|
5
|
+
| Author | Tim Morgan (riscfuture@gmail.com) with small mods by Kacper Cieśa (kacper.ciesla@gmail.com) |
|
6
|
+
| Copyright | Copyright (c)2007-2008 Tim Morgan |
|
7
|
+
| License | Distributed under the same terms as Ruby. Portions of this code are copyright (c)2004 David Heinemeier Hansson; please see <tt>libs/inheritable_attributes.rb</tt> for more information. |
|
8
|
+
|
9
|
+
Autumn is a full-featured framework on top of which IRC bots (called "leaves")
|
10
|
+
can be quickly and easily built. It features a very Ruby-like approach to
|
11
|
+
bot-writing, a complete framework for loading and daemonizing your bots,
|
12
|
+
multiple environment contexts, a database-backed model, and painless logging
|
13
|
+
support.
|
14
|
+
|
15
|
+
h2. Requirements
|
16
|
+
|
17
|
+
Autumn requires "RubyGems":http://www.rubygems.org and the Daemons and Facets*
|
18
|
+
gems, as well as some of the gems spun off from Facets. Install RubyGems then
|
19
|
+
run @sudo gem install daemons facets anise english@ in a command line in order
|
20
|
+
to run Autumn.
|
21
|
+
|
22
|
+
If you wish to use a database backend for your bot, you will need the DataMapper
|
23
|
+
gem. To install, see the "DataMapper website":http://www.datamapper.org.
|
24
|
+
|
25
|
+
The included example bot Scorekeeper requires the DataMapper gem. It can
|
26
|
+
optionally use the Chronic gem to enhance its textual date parsing. The other
|
27
|
+
example bot, Insulter, is much simpler and can run under any Autumn
|
28
|
+
configuration.
|
29
|
+
|
30
|
+
h2. Installation
|
31
|
+
|
32
|
+
<tt>gem sources -a http://gems.github.com
|
33
|
+
sudo gem install comboy-autumn</tt>
|
34
|
+
|
35
|
+
And you're ready to go.
|
36
|
+
|
37
|
+
h2. Generating Application Skeleton
|
38
|
+
|
39
|
+
To create your first Autumn IRC Bot simply type:
|
40
|
+
<tt>autumn mybot</tt>
|
41
|
+
|
42
|
+
This will generate directory structure described below in directory called mybot.
|
43
|
+
|
44
|
+
h2. Directory Structure
|
45
|
+
|
46
|
+
An Autumn installation is like a Ruby on Rails installation: There is a
|
47
|
+
certain directory structure where your files go. A lot of files and folders will
|
48
|
+
seem confusing to people who have never used Autumn before, but bear with me. In
|
49
|
+
a bit I will explain in detail what all of this stuff is. For now, here is an
|
50
|
+
overview you can consult for future reference:
|
51
|
+
|
52
|
+
* *config/* - Configuration files and season definitions
|
53
|
+
** global.yml - Universal settings that apply to every season
|
54
|
+
*** *seasons/* - Contains directories for each season (see *Seasons*)
|
55
|
+
**** *testing/* - Example season
|
56
|
+
***** database.yml - Example database configuration file
|
57
|
+
***** leaves.yml - Example bot configuration file
|
58
|
+
***** season.yml - Season configuration
|
59
|
+
***** stems.yml - Example IRC configuration file
|
60
|
+
* *leaves/* - Autumn leaves. Each subdirectory contains all the code and
|
61
|
+
data for a leaf.
|
62
|
+
** *insulter/* - Very simple example leaf
|
63
|
+
*** _See the *scorekeeper* directory_
|
64
|
+
** *scorekeeper/* - Database-backed, full-featured example leaf
|
65
|
+
*** config.yml - Optional leaf-global configuration options
|
66
|
+
*** controller.rb - The leaf's controller object
|
67
|
+
*** *data/* - Optional directory for data storage (not used by Autumn)
|
68
|
+
*** *helpers/* - Modules that extend the controller and views
|
69
|
+
*** *models/* - Active record-type database objects
|
70
|
+
*** *tasks/* - Additional rake tasks for this leaf (see *Custom leaf tasks*)
|
71
|
+
*** *views/* - ERb views for each of the leaf's commands
|
72
|
+
* *log/* - Directory where (most) Autumn logs are written (see the *Logs*
|
73
|
+
section)
|
74
|
+
* Rakefile - Contains the rake tasks used to control Autumn (see the *Tasks*
|
75
|
+
section)
|
76
|
+
* README - RDoc-formatted readme
|
77
|
+
* README.textile - This file
|
78
|
+
* *script/* - Helper scripts for controlling Autumn
|
79
|
+
** daemon - Runs Autumn as a daemon
|
80
|
+
** destroy - Destroys Autumn objects
|
81
|
+
** generate - Creates Autumn objects
|
82
|
+
** server - Starts Autumn
|
83
|
+
* *tmp/* - Temporary files, such as PID files
|
84
|
+
|
85
|
+
h3. For developers
|
86
|
+
|
87
|
+
If you want to have somem fun with Autumn source code here is short description of where to find what:
|
88
|
+
|
89
|
+
* *libs/* - Autumn core code
|
90
|
+
** channel_leaf.rb - A leaf subclass that can ignore messages from certain
|
91
|
+
channels its in
|
92
|
+
** coder.rb - Used by script/generate to write out Ruby code
|
93
|
+
** ctcp.rb - CTCP support library
|
94
|
+
** daemon.rb - Provides support for different kinds of IRC servers
|
95
|
+
** datamapper_hacks.rb - Some hacks to help DataMapper work with Autumn
|
96
|
+
** foliater.rb - Instantiates and manages stems and leaves
|
97
|
+
** formatting.rb - Provides support for different kinds of IRC client text
|
98
|
+
formatting and colorization
|
99
|
+
** generator.rb - Library used by script/generate
|
100
|
+
** genesis.rb - Boots the Autumn environment
|
101
|
+
** inheritable_attributes.rb - Adds support for class-level inheritable
|
102
|
+
attributes
|
103
|
+
** leaf.rb - The core bot superclass
|
104
|
+
** log_facade.rb - Simplifies logging for stems and leaves
|
105
|
+
** misc.rb - RubyCore class additions and other knick-knacks
|
106
|
+
** script.rb - Library used by script/generate and script/destroy
|
107
|
+
** speciator.rb - Manages global, season, stem, and leaf configurations
|
108
|
+
** stem.rb - IRC client library
|
109
|
+
** stem_facade.rb - Additional methods to simplify the Stem class
|
110
|
+
* *resources/* - Data files used by Autumn
|
111
|
+
** *daemons/* - Data files describing different IRC server types
|
112
|
+
* *skel/* - application skeleton, exactly what you get after typing "autumn mybot"
|
113
|
+
* *shared/* - Shared code libraries available to all leaves
|
114
|
+
|
115
|
+
h2. Configuring Autumn for Your First Launch
|
116
|
+
|
117
|
+
Before you can run Autumn and try out the example leaves, you'll need to set up
|
118
|
+
a few things. Here are the steps:
|
119
|
+
|
120
|
+
h3. Configure Your Testing Season
|
121
|
+
|
122
|
+
In Autumn, your leaves run in an environment, called a "season." Each season has
|
123
|
+
different leaves and different settings for those leaves. By default, Autumn
|
124
|
+
comes with a season called "testing" already set up for you. You can edit that
|
125
|
+
season or create a new one with @script/generate season [season name]@. The
|
126
|
+
files for your season are stored in the <tt>config/seasons</tt> directory.
|
127
|
+
|
128
|
+
First, edit the <tt>stems.yml</tt> file. This file stores information about your
|
129
|
+
IRC connection. Edit it to connect to an IRC server of your choosing. For more
|
130
|
+
information, see *Stems* below.
|
131
|
+
|
132
|
+
Next, edit the <tt>database.yml</tt> file. As mentioned previously, Scorekeeper
|
133
|
+
requires the DataMapper gem because it uses a persistent store. By default it's
|
134
|
+
set up to use a MySQL database, but you can use PostgreSQL or SQLite 3 if you'd
|
135
|
+
like. If you'd prefer not to install any of these database solutions, delete the
|
136
|
+
<tt>database.yml</tt> file and remove the Scorekeeper leaf from the
|
137
|
+
<tt>leaves.yml</tt> and <tt>stems.yml</tt> files.
|
138
|
+
|
139
|
+
If you do choose to set up a database, you will have to run @rake db:migrate@
|
140
|
+
after your <tt>database.yml</tt> file is configured and your database is
|
141
|
+
created.
|
142
|
+
|
143
|
+
Lastly, view the <tt>leaves.yml</tt> file. You shouldn't have to make any
|
144
|
+
changes to this file, but it's a good idea to look at it to see how leaves are
|
145
|
+
configured. You can do the same with the <tt>season.yml</tt> file. See *Seasons*
|
146
|
+
and *Leaves* below for more.
|
147
|
+
|
148
|
+
h3. Starting the Server
|
149
|
+
|
150
|
+
Run the shell command @script/server@ to start the server. After a short
|
151
|
+
while, your leaf should appear in the channel you specified. You can type
|
152
|
+
"!points Coolguy +5" and then "!points" to get started using Scorekeeper, or
|
153
|
+
"!insult" to play with Insulter. Have some fun, and when you're satisfied, stop
|
154
|
+
the server by typing "!quit".
|
155
|
+
|
156
|
+
If you'd like to daemonize your server, you can use the shell commands
|
157
|
+
@rake app:start@ and @rake app:stop@. For more information, see *Tasks* below.
|
158
|
+
|
159
|
+
h2. Making Your Own Leaf
|
160
|
+
|
161
|
+
Making your own leaf using Autumn is easy. In this tutorial, I'll show you how
|
162
|
+
to make a simple Fortune bot that responds to a few basic commands.
|
163
|
+
|
164
|
+
h3. Step 1: Subclass Leaf
|
165
|
+
|
166
|
+
Create a new leaf by typing @script/generate leaf fortune@. This will create a
|
167
|
+
<tt>fortune</tt> directory in the <tt>leaves</tt> directory, along with the bare
|
168
|
+
bones of files needed within that directory. Edit the <tt>controller.rb</tt>
|
169
|
+
file. First we'll create an array to hold our fortunes:
|
170
|
+
|
171
|
+
<pre><code>
|
172
|
+
FORTUNES = [
|
173
|
+
"You will make someone happy today.",
|
174
|
+
"Someone you don't expect will be important to you today.",
|
175
|
+
"Today will bring unexpected hardships."
|
176
|
+
]
|
177
|
+
</code></pre>
|
178
|
+
|
179
|
+
As you can see, our 3 meager fortunes are stored in the @FORTUNES@ class
|
180
|
+
constant. Now, we'll want it to respond to the "!fortune" command, and all you
|
181
|
+
have to do is create a method called @fortune_command@ to make it work:
|
182
|
+
|
183
|
+
<pre><code>
|
184
|
+
def fortune_command(stem, sender, reply_to, msg)
|
185
|
+
FORTUNES.pick
|
186
|
+
end
|
187
|
+
</code></pre>
|
188
|
+
|
189
|
+
The @pick@ method is provided by Facets, so you may need to add a <code>require
|
190
|
+
'facets/random'</code> line at the top of your file. Our method returns a
|
191
|
+
fortune at random, which is automatically transmitted to the channel or nick
|
192
|
+
where the command was received.
|
193
|
+
|
194
|
+
Of course, any self-respecting fortune bot announces its presence when it starts
|
195
|
+
up, so, in your @Controller@ class, override the @Autumn::Leaf#did_start_up@
|
196
|
+
method to display a cheerful greeting:
|
197
|
+
|
198
|
+
<pre><code>
|
199
|
+
def did_start_up
|
200
|
+
stems.message 'FortuneBot at your service! Type "!fortune" to get your fortune!'
|
201
|
+
end
|
202
|
+
</code></pre>
|
203
|
+
|
204
|
+
...and that's it! You now have a fully functional fortune bot featuring -- not
|
205
|
+
two -- but _three_ unique and exciting fortunes!
|
206
|
+
|
207
|
+
(For more on that @stems.message@ bit, see *Stems*.)
|
208
|
+
|
209
|
+
h3. Step 2: Add the Leaf to Your Season
|
210
|
+
|
211
|
+
If you want, you can add the fortune bot to your <tt>leaves.yml</tt> and
|
212
|
+
<tt>stems.yml</tt> files to try it out. Adding a leaf is easy; simply duplicate
|
213
|
+
the structure used for another leaf's entry and change the values as
|
214
|
+
appropriate. A typical two-leaf configuration will look like:
|
215
|
+
|
216
|
+
<pre><code>
|
217
|
+
Scorekeeper:
|
218
|
+
class: Scorekeeper
|
219
|
+
respond_to_private_messages: false
|
220
|
+
Fortune:
|
221
|
+
class: Fortune
|
222
|
+
respond_to_private_messages: true
|
223
|
+
</code></pre>
|
224
|
+
|
225
|
+
As you notice, each leaf instance is given a name. In this example the name
|
226
|
+
happens to be the same as the leaf's type name, but you could run two copies of
|
227
|
+
a leaf like so:
|
228
|
+
|
229
|
+
<pre><code>
|
230
|
+
Fortune1:
|
231
|
+
class: Fortune
|
232
|
+
Fortune2:
|
233
|
+
class: Fortune
|
234
|
+
</code></pre>
|
235
|
+
|
236
|
+
This doesn't make a whole lot of sense for our fortune bot, but for more
|
237
|
+
complicated bots it can be useful.
|
238
|
+
|
239
|
+
We've created the leaf, but we have to add it to the stem for it to work.
|
240
|
+
(Remember, a stem is an IRC connection and a leaf is a bot.) So, in your
|
241
|
+
<tt>stems.yml</tt> file, add an entry for this leaf. Your new config will appear
|
242
|
+
something like:
|
243
|
+
|
244
|
+
<pre><code>
|
245
|
+
Example:
|
246
|
+
nick: Scorekeeper
|
247
|
+
leaves:
|
248
|
+
- Scorekeeper
|
249
|
+
- Insulter
|
250
|
+
- Fortune
|
251
|
+
rejoin: true
|
252
|
+
channel: somechannel
|
253
|
+
server: irc.someserver.com
|
254
|
+
</code></pre>
|
255
|
+
|
256
|
+
When you restart the server, the bot will come back online and will now also
|
257
|
+
respond to the "!fortune" command. This is a helpful tutorial on how stems and
|
258
|
+
leaves are separate. One leaf can have many stems, and one stem can have many
|
259
|
+
leaves. You can combine these two entities however you need.
|
260
|
+
|
261
|
+
h3. Step 3: Upgrade to ERb Views
|
262
|
+
|
263
|
+
You've already learned that for your @[word]_command@-type methods, the bot
|
264
|
+
responds with whatever string your method returns. For more complicated
|
265
|
+
commands, however, you may want to upgrade to full view abstraction, a la Ruby
|
266
|
+
on Rails. This is what the <tt>views</tt> directory is for.
|
267
|
+
|
268
|
+
If you place a <tt>.txt.erb</tt> file in the <tt>views</tt> directory named
|
269
|
+
after your command, it will be parsed by ERb and rendered as the result. You can
|
270
|
+
pass variables to the ERb parser by using the @Autumn::Leaf#var@ method. Let's
|
271
|
+
upgrade our @fortune_command@ method for that:
|
272
|
+
|
273
|
+
<pre><code>
|
274
|
+
def fortune_command(stem, sender, reply_to, msg)
|
275
|
+
var :fortune => FORTUNES.pick
|
276
|
+
end
|
277
|
+
</code></pre>
|
278
|
+
|
279
|
+
We can then write a view, <tt>fortune.txt.erb</tt>, which will render the
|
280
|
+
fortune:
|
281
|
+
|
282
|
+
<code><%= var :fortune %></code>
|
283
|
+
|
284
|
+
OK, so admittedly, this doesn't really get us anywhere, but for more complicated
|
285
|
+
bots, this well help separate view and controller concerns.
|
286
|
+
|
287
|
+
For more information on view rendering, see the @Autumn::Leaf#render@ method.
|
288
|
+
|
289
|
+
h2. Seasons
|
290
|
+
|
291
|
+
Each time you start Autumn, the process launches in a certain season (a.k.a.
|
292
|
+
environment context). This season is defined in the <tt>config/global.yml</tt>
|
293
|
+
file. You can temporarily override it by setting the @SEASON@ environment
|
294
|
+
variable (e.g., @SEASON=production script/server@).
|
295
|
+
|
296
|
+
It's important to realize that an season is just a name, nothing more. You can
|
297
|
+
have as many seasons as you like, and name them anything that you like. Autumn
|
298
|
+
will load the config files for the season you've indicated as active. Autumn
|
299
|
+
doesn't really care if it's named "production" or "live" or
|
300
|
+
"testing-on-jeffs-machine"; it's all the same to Autumn.
|
301
|
+
|
302
|
+
Your season's configuration is stored in the <tt>season.yml</tt> file within
|
303
|
+
your season directory. Currently it supports one directive, @logging@. This sets
|
304
|
+
the minimum log level (such as @debug@ or @warn@). If the log level is set to
|
305
|
+
@debug@, it also enables console output parroting. (See the *Logging* section.)
|
306
|
+
|
307
|
+
The power of seasons comes in custom configuration options. For instance,
|
308
|
+
consider that you have a testing and production season. In your testing season,
|
309
|
+
your <tt>season.yml</tt> file contains:
|
310
|
+
|
311
|
+
<code>dont_http: true</code>
|
312
|
+
|
313
|
+
and in production, it contains:
|
314
|
+
|
315
|
+
<code>dont_http: false</code>
|
316
|
+
|
317
|
+
Now, in your code, you might have a method like:
|
318
|
+
|
319
|
+
<pre><code>
|
320
|
+
def scores_command(stem, sender, reply_to, msg)
|
321
|
+
if options[:dont_http] then
|
322
|
+
return "Some fake sports scores."
|
323
|
+
else
|
324
|
+
# go on the web and find real sports scores
|
325
|
+
end
|
326
|
+
end
|
327
|
+
</code></pre>
|
328
|
+
|
329
|
+
h3. Standard Configuration Options
|
330
|
+
|
331
|
+
h4. Global
|
332
|
+
|
333
|
+
System-wide configuration is done in the <tt>config/global.yml</tt> file. It
|
334
|
+
supports by default the following directives:
|
335
|
+
|
336
|
+
| @season@ | The season to launch in. |
|
337
|
+
| @log_history@ | The number of historical logfiles to keep (default 10). |
|
338
|
+
|
339
|
+
In addition, the following options are available (but cannot be set in the yml
|
340
|
+
file):
|
341
|
+
|
342
|
+
| @root@ | The root directory of the Autumn installation. |
|
343
|
+
| @system_logger@ | The @Autumn::LogFacade@ instance that records system messages. |
|
344
|
+
|
345
|
+
h4. Season
|
346
|
+
|
347
|
+
Season-specific configuration is done in the
|
348
|
+
<tt>config/seasons/[season]/season.yml</tt> file. Currently it only supports one
|
349
|
+
directive, @logging@, which takes log levels like @debug@ or @warn@.
|
350
|
+
|
351
|
+
h4. Stem
|
352
|
+
|
353
|
+
Stem-specific configuration is done in the
|
354
|
+
<tt>config/seasons/[season]/stems.yml</tt> file. It's important to note that
|
355
|
+
stem and leaf configurations are completely independent of each other. (In other
|
356
|
+
words, stem options do not override leaf options, nor vice versa.) Therefore,
|
357
|
+
you generally won't add custom directives to the <tt>stems.yml</tt> file,
|
358
|
+
because you generally won't be working with stems directly. The standard options
|
359
|
+
are:
|
360
|
+
|
361
|
+
| @server@ | The address of the IRC server. |
|
362
|
+
| @port@ | The IRC server port (default 6667). |
|
363
|
+
| @local_ip@ | The IP address to connect on (for virtual hosting). |
|
364
|
+
| @nick@ | The nick to request. |
|
365
|
+
| @password@ | The nick's password, if it is registered. |
|
366
|
+
| @channel@ | A channel to join. |
|
367
|
+
| @channels@ | A list of channels to join. |
|
368
|
+
| @leaf@ | The name of a leaf to run. |
|
369
|
+
| @leaves@ | A list of leaves to run. (These are the names of leaf configurations in <tt>leaves.yml</tt>, not leaf subclasses.) |
|
370
|
+
| @rejoin@ | If true, the stem will rejoin any channels it is kicked from. |
|
371
|
+
| @server_password@ | The password for the IRC server, if necessary. |
|
372
|
+
| @ssl@ | If true, the connection to the IRC server will be made over SSL. |
|
373
|
+
| @server_type@ | The IRC server type. See <tt>resources/daemons</tt> for a list of valid server types. If you do not manually set this value, it will be guessed automatically. |
|
374
|
+
| @case_sensitive_channel_names@ | If true, channel names will be compared with case sensitivity. |
|
375
|
+
| @dont_ghost@ | If true, the stem will not try to GHOST a registered nick if it's taken. |
|
376
|
+
| @ghost_without_password@ | If true, the stem will use the GHOST command without a password. Set this for servers that use some other form of nick authentication, such as hostname-based. |
|
377
|
+
| @user@ | The username to send (optional). |
|
378
|
+
| @name@ | The user's real name (optional). |
|
379
|
+
| @throttle@ | If enabled, the stem will throttle large amounts of simultaneous messages. |
|
380
|
+
| @throttle_rate@ | Sets the number of seconds that pass between consecutive PRIVMSG's when the leaf's output is throttled. |
|
381
|
+
| @throttle_threshold@ | Sets the number of simultaneous messages that must be queued before the leaf begins throttling output. |
|
382
|
+
| @nick_regex@ | The regular expression used to match nicknames in server messages. By default, it conforms to the RFC-1459 definition. |
|
383
|
+
|
384
|
+
The @channel@ and @channels@ directives can also be used to specify a password
|
385
|
+
for a password protected channel, like so:
|
386
|
+
|
387
|
+
<pre><code>
|
388
|
+
channel:
|
389
|
+
channelname: channelpassword
|
390
|
+
</code></pre>
|
391
|
+
|
392
|
+
or
|
393
|
+
|
394
|
+
<pre><code>
|
395
|
+
channels:
|
396
|
+
- channel1: password1
|
397
|
+
- channel2: password2
|
398
|
+
</code></pre>
|
399
|
+
|
400
|
+
The @port@, @server_type@, and @channel@/@channels@ options are set in the
|
401
|
+
config file but not available in the @options@ hash. They are accessed directly
|
402
|
+
from attributes in the @Autumn::Stem@ instance, such as the @channels@
|
403
|
+
attribute.
|
404
|
+
|
405
|
+
h4. Leaf
|
406
|
+
|
407
|
+
Leaf-specific configuration is done in the
|
408
|
+
<tt>config/seasons/[season]/leaves.yml</tt> file and the
|
409
|
+
<tt>leaves/[leaf]/config.yml</tt> file, with the former taking precedence over
|
410
|
+
the latter. As mentioned above, leaf and stem configurations are completely
|
411
|
+
separate, so one does not override the other. The standard options are:
|
412
|
+
|
413
|
+
| @class@ | The type of the leaf. It must be a subdirectory in the <tt>leaves</tt> directory. |
|
414
|
+
| @command_prefix@ | The text that must precede each command. Defaults to "!". |
|
415
|
+
| @respond_to_private_messages@ | If true, the leaf will parse commands in whispers, and respond over whispers to those commands. |
|
416
|
+
| @database@ | A database connection to use (as defined in <tt>database.yml</tt>). By default Autumn will choose a connection named after your leaf. |
|
417
|
+
| @formatter@ | The name of a module in @Autumn::Formatting@ that will handle output formatting and colorization. This defaults to mIRC-style formatting. |
|
418
|
+
|
419
|
+
In addition, the following options are available (but cannot be set in the yml
|
420
|
+
file):
|
421
|
+
|
422
|
+
| @root@ | The root directory of the leaf installation. |
|
423
|
+
|
424
|
+
The <tt>leaves.yml</tt> file is optional. When not included, each leaf in the
|
425
|
+
<tt>leaves</tt> directory will be automatically instantiated once.
|
426
|
+
|
427
|
+
h3. Custom Configuration Options
|
428
|
+
|
429
|
+
All configuration files support user-generated directives. You can set options
|
430
|
+
at any level. Options at a more narrow level override those at a broader level.
|
431
|
+
|
432
|
+
Options are maintained and cataloged by the @Autumn::Speciator@ singleton. You
|
433
|
+
could access the singleton directly, but most objects have an @options@
|
434
|
+
attribute providing simpler access to the Speciator.
|
435
|
+
|
436
|
+
For example, to access options in a leaf, all you do is call, for example,
|
437
|
+
@options[:my_custom_option]@. @my_custom_option@ can be set at the
|
438
|
+
global, season, or leaf level.
|
439
|
+
|
440
|
+
h2. Leaves
|
441
|
+
|
442
|
+
The @Autumn::Leaf@ class has many tools to help you write your leaves. These
|
443
|
+
include things like filters, helpers, loggers, and an easy to use IRC library.
|
444
|
+
The @Autumn::Leaf@ and @Autumn::Stem@ class docs are the most thorough way of
|
445
|
+
learning about each of these features, but I'll walk you through the basics
|
446
|
+
here.
|
447
|
+
|
448
|
+
h3. The Many Methods of Leaf
|
449
|
+
|
450
|
+
By subclassing @Autumn::Leaf@, you gain access to a number of neat utilities.
|
451
|
+
These generally come in three classes: IRC commands that have already been
|
452
|
+
written for you, utility methods you can call, and invoked methods you can
|
453
|
+
override. Utility methods do things like add filters. Invoked methods are called
|
454
|
+
when certain events happen, like when your leaf starts up or when a private
|
455
|
+
message is received. You override them in your leaf to customize how it responds
|
456
|
+
to these events.
|
457
|
+
|
458
|
+
| *Invoked methods* | @will_start_up@, @did_start_up@, @did_receive_channel_message@, etc. |
|
459
|
+
| *Utility methods* | @before_filter@, @database@, etc. |
|
460
|
+
| *IRC commands* | @quit_command@, @reload_command@, @autumn_command@, etc. |
|
461
|
+
|
462
|
+
See the class docs for more information on these methods.
|
463
|
+
|
464
|
+
In addition, your leaf is designated as a listener for its @Autumn::Stem@
|
465
|
+
instances. In short, this means if you want even finer control over the IRC
|
466
|
+
connection, you can implement listener methods. See the
|
467
|
+
@Autumn::Stem#add_listener@ method for examples of such methods.
|
468
|
+
|
469
|
+
Finally, your leaf can implement methods that are broadcast by listener plugins.
|
470
|
+
An example of such a plugin is the @Autumn::CTCP@ class, which is included in
|
471
|
+
all stems by default. Visit its class docs to learn more about how to send and
|
472
|
+
receive CTCP requests.
|
473
|
+
|
474
|
+
h3. Filters
|
475
|
+
|
476
|
+
Filters are methods that are run either before or after a command is executed.
|
477
|
+
In the former case, they can also prevent the command from being run. This is
|
478
|
+
useful for authentication, for instance: A filter could determine if someone is
|
479
|
+
authorized to run a command, and prevent the command from being run if not.
|
480
|
+
|
481
|
+
Use filters to save yourself the effort of rewriting code that will run before
|
482
|
+
or after a command is executed. Filter methods are named @[word]_filter@ and
|
483
|
+
they are added to the filter chain using the @before_filter@ and @after_filter@
|
484
|
+
methods (like in Ruby on Rails). As an example, imagine you wanted your bot to
|
485
|
+
say something after each command:
|
486
|
+
|
487
|
+
<pre><code>
|
488
|
+
class Controller > Autumn::Leaf
|
489
|
+
after_filter :outro
|
490
|
+
|
491
|
+
private
|
492
|
+
|
493
|
+
def outro_filter(stem, channel, sender, command, msg, opts)
|
494
|
+
stem.message "This has been a production of OutroBot!", channel
|
495
|
+
end
|
496
|
+
end
|
497
|
+
</code></pre>
|
498
|
+
|
499
|
+
The result of this is that after each command, the leaf will make a dramatic
|
500
|
+
exit. (Why did I use @after_filter@ and not @before_filter@? Because as I said
|
501
|
+
earlier, a @before_filter@ can stop the command from being executed; the only
|
502
|
+
way we know for sure that the command was executed -- and therefore should be
|
503
|
+
outroed -- is to use an @after_filter@.)
|
504
|
+
|
505
|
+
I made the @outro_filter@ method private because I felt it shouldn't be exposed
|
506
|
+
to other classes; this is not a requirement of the filter framework, though.
|
507
|
+
|
508
|
+
Now let's say you wanted to prevent the command from being run in some cases.
|
509
|
+
The most obvious application of this feature is authentication. Autumn already
|
510
|
+
includes a robust authentication module, but for the sake of example, let's
|
511
|
+
pretend you wanted to do your own authentication in your leaf. So, you write a
|
512
|
+
@before_filter@ to determine if the user is authenticated. @before_filter@
|
513
|
+
methods have return values; if they return false, the filter chain is halted and
|
514
|
+
the command is suppressed. If you want to have your leaf display some sort of
|
515
|
+
message (like "Nice try!"), you need to include that in your filter.
|
516
|
+
|
517
|
+
As an example, here's a simple form of authentication that just checks a
|
518
|
+
person's nick:
|
519
|
+
|
520
|
+
<pre><code>
|
521
|
+
class Controller < Autumn::Leaf
|
522
|
+
before_filter :authenticate, :only => :quit, :admin => 'Yournick'
|
523
|
+
|
524
|
+
def authenticate_filter(stem, channel, sender, command, msg, opts)
|
525
|
+
sender == opts[:admin]
|
526
|
+
end
|
527
|
+
end
|
528
|
+
</code></pre>
|
529
|
+
|
530
|
+
I'm introducing you to three new features with this sample:
|
531
|
+
|
532
|
+
* You can use the @:only@ option to limit your filter to certain commands. Note
|
533
|
+
that you specify the _command_ name as a symbol, _not_ the method name (which
|
534
|
+
would be @quit_command@ in this case).
|
535
|
+
* You can pass your own options to @before_filter@ and @after_filter@; they are
|
536
|
+
passed through to your method via the last parameter, @opts@.
|
537
|
+
* The return value of a @before_filter@ is used to determine if the command
|
538
|
+
should be run. So be careful that your method does not return @nil@ or @false@
|
539
|
+
unless you really mean for the command to be suppressed.
|
540
|
+
|
541
|
+
Both of these examples use the parameters sent to your filter method. They are,
|
542
|
+
in order:
|
543
|
+
|
544
|
+
# the @Autumn::Stem@ instance that received the command,
|
545
|
+
# the name of the channel to which the command was sent (or @nil@ if it was a
|
546
|
+
private message),
|
547
|
+
# the sender hash,
|
548
|
+
# the name of the command that was typed, as a symbol,
|
549
|
+
# any additional parameters after the command (same as the @msg@ parameter in
|
550
|
+
the <tt>[word]_command</tt> methods),
|
551
|
+
# the custom options that were given to @before_filter@ or @after_filter@.
|
552
|
+
|
553
|
+
There are two built-in options that you can specify for @before_filter@ and
|
554
|
+
@after_filter@, and those are @only@ and @except@. They work just like in Rails:
|
555
|
+
The @only@ option limits the filter to running only on the given command or list
|
556
|
+
of commands, and the @except@ option prevents the filter from being run on the
|
557
|
+
given command or list. All other options are passed to the filter for you to
|
558
|
+
use.
|
559
|
+
|
560
|
+
Filters are run in the order they are added to the filter chain. Therefore, a
|
561
|
+
superclass's filters will run before a subclass's filters, and filters added
|
562
|
+
later in a class definition will be run after those added earlier.
|
563
|
+
|
564
|
+
If you subclass one of your leaves, it inherits your superclass's filters. The
|
565
|
+
@Autumn::Leaf@ superclass does not have any filters by default, though by
|
566
|
+
default new leaves come with a simple authentication filter that checks the
|
567
|
+
user's privilege level.
|
568
|
+
|
569
|
+
h3. Authentication
|
570
|
+
|
571
|
+
You don't need to write a @before_filter@ as shown above, because Autumn already
|
572
|
+
includes a robust authentication module. The @Autumn::Authentication@ module
|
573
|
+
includes the @Base@ class and four different subclasses of it. Each of these
|
574
|
+
subclasses handles a different type of authentication. You can choose the
|
575
|
+
authentication strategy you want on a leaf-by-leaf basis or for a whole season.
|
576
|
+
|
577
|
+
To specify the kind of authentication you want, you must add an @authentication@
|
578
|
+
directive to your config. If you want to set it for an individual leaf, add it
|
579
|
+
to the <tt>leaves.yml</tt> file. If you want all leaves to have the same
|
580
|
+
authentication strategy, add it to the <tt>season.yml</tt> or
|
581
|
+
<tt>global.yml</tt> file.
|
582
|
+
|
583
|
+
The @authentication@ directive should be a hash that, at a minimum, includes a
|
584
|
+
key called @type@. This is the snake_cased name of subclass in
|
585
|
+
@Autumn::Authentication@ that you wish to use. As an example, here is an entry
|
586
|
+
for an Administrator bot in a <tt>leaves.yml</tt> file, with ops-based
|
587
|
+
authentication.
|
588
|
+
|
589
|
+
<pre><code>
|
590
|
+
Administrator:
|
591
|
+
class: Administrator
|
592
|
+
authentication:
|
593
|
+
type: op
|
594
|
+
</code></pre>
|
595
|
+
|
596
|
+
This will instantiate the @Autumn::Authentication::Op@ class for use with the
|
597
|
+
Administrator bot.
|
598
|
+
|
599
|
+
Other authentication strategies may require additional information. For
|
600
|
+
instance, if you want to used nick-based authentication, your
|
601
|
+
<tt>leaves.yml</tt> file might look like:
|
602
|
+
|
603
|
+
<pre><code>
|
604
|
+
Administrator:
|
605
|
+
class: Administrator
|
606
|
+
authentication:
|
607
|
+
type: nick
|
608
|
+
nick: MyNick
|
609
|
+
</code></pre>
|
610
|
+
|
611
|
+
See the class docs for each subclass in @Autumn::Authentication@ for more info
|
612
|
+
on how you should set up your configs.
|
613
|
+
|
614
|
+
h3. Persistent Stores
|
615
|
+
|
616
|
+
If you would like to use a persistent store for your leaf, you should install
|
617
|
+
the DataMapper gem and a DataObjects gem for your database of choice (MySQL,
|
618
|
+
PostgreSQL, or SQLite). DataMapper works almost identically to ActiveRecord, so
|
619
|
+
if you have any Rails programming experience, you should be able to dive right
|
620
|
+
in.
|
621
|
+
|
622
|
+
Once you've got DataMapper installed, you should create one or more database
|
623
|
+
connections in your <tt>config/seasons/[season]/database.yml</tt> file. A sample
|
624
|
+
database connection looks like:
|
625
|
+
|
626
|
+
<pre><code>
|
627
|
+
connection_name:
|
628
|
+
adapter: mysql
|
629
|
+
host: localhost
|
630
|
+
username: root
|
631
|
+
password: pass
|
632
|
+
database: database_name
|
633
|
+
</code></pre>
|
634
|
+
|
635
|
+
or, in a smaller syntax:
|
636
|
+
|
637
|
+
<code>connection_name: mysql://root@pass:localhost/database_name</code>
|
638
|
+
|
639
|
+
If you are using the "sqlite3" adapter, the @database@ option is the path to the
|
640
|
+
file where the data should be written (example:
|
641
|
+
@leaves/fortune/data/my_database.db@). You can name your connection however you
|
642
|
+
want, but you _should_ name it after either your leaf or your leaf subclass.
|
643
|
+
(More on this below.)
|
644
|
+
|
645
|
+
You should also create DataMapper model classes for each of your model objects.
|
646
|
+
You can place them within your leaf's <tt>models</tt> directory. This works
|
647
|
+
almost exactly the same as the <tt>app/models</tt> directory in Rails.
|
648
|
+
|
649
|
+
Once your database, data models, and leaves have been configured, you can use
|
650
|
+
the @rake db:migrate@ task to automatically populate your database.
|
651
|
+
|
652
|
+
Now, unlike Rails, Autumn supports multiple database connections. Two leaves can
|
653
|
+
use two different database connections, or share the same database connection.
|
654
|
+
Because of this, it's important to understand how to manage your connections.
|
655
|
+
Autumn tries to do this for you by guessing which connection belongs to which
|
656
|
+
leaf, based on their names.
|
657
|
+
|
658
|
+
For example, imagine you have a leaf named "Fortune" and an instance of that
|
659
|
+
leaf in <tt>leaves.yml</tt> named "MyFortune". If you name your database
|
660
|
+
connection either "Fortune" or "MyFortune" (or "fortune" or "my_fortune"), it
|
661
|
+
will automatically be associated with that leaf. What this means is that for the
|
662
|
+
leaf's command methods (such as @about_command@) and invoked methods (such as
|
663
|
+
@did_receive_private_message@), the database connection will already be set for
|
664
|
+
you, and you can start using your DataMapper objects just like ActiveRecord
|
665
|
+
objects.
|
666
|
+
|
667
|
+
If, on the other hand, you either *named your database connection differently
|
668
|
+
from your leaf or subclass name* or you are *writing a method outside of the
|
669
|
+
normal flow of leaf methods* (for instance, one that is directly called by a
|
670
|
+
@Stem@, or a different listener), you will need to call the @database@ method
|
671
|
+
and pass it a block containing your code.
|
672
|
+
|
673
|
+
This is terribly confusing, so let me give you an example. Let's assume you've
|
674
|
+
got a fortune bot running a leaf named "FortuneLeaf", so your
|
675
|
+
<tt>leaves.yml</tt> configuration is:
|
676
|
+
|
677
|
+
<pre><code>
|
678
|
+
FortuneBot:
|
679
|
+
class: FortuneLeaf
|
680
|
+
</code></pre>
|
681
|
+
|
682
|
+
And you have a database connection for that leaf, named after the leaf's class:
|
683
|
+
|
684
|
+
<pre><code>
|
685
|
+
fortune_leaf:
|
686
|
+
adapter: sqlite3
|
687
|
+
database: leaves/fortune_leaf/data/development.db
|
688
|
+
</code></pre>
|
689
|
+
|
690
|
+
Let's further assume you have a simple DataMapper object:
|
691
|
+
|
692
|
+
<pre><code>
|
693
|
+
class Fortune
|
694
|
+
include DataMapper::Resource
|
695
|
+
property :id, Integer, :serial => true
|
696
|
+
property :text, String
|
697
|
+
end
|
698
|
+
</code></pre>
|
699
|
+
|
700
|
+
Now, if we wanted to write a "!fortune" command, it would appear something like
|
701
|
+
this:
|
702
|
+
|
703
|
+
<pre><code>
|
704
|
+
def fortune_command(stem, sender, reply_to, msg)
|
705
|
+
fortunes = Fortune.all
|
706
|
+
fortunes[rand(fortunes.size)].text
|
707
|
+
end
|
708
|
+
</code></pre>
|
709
|
+
|
710
|
+
Autumn automatically knows to execute this DataMapper code in the correct
|
711
|
+
database context. It knows this because your leaf's name is @FortuneLeaf@, and
|
712
|
+
your database context is named the same.
|
713
|
+
|
714
|
+
But what if you wanted to use that connection for other leaves too, so you named
|
715
|
+
it something like "local_database"? Now, Autumn won't be able to guess that you
|
716
|
+
want to use that DB context, so you have to specify it manually:
|
717
|
+
|
718
|
+
<pre><code>
|
719
|
+
def fortune_command(stem, sender, reply_to, msg)
|
720
|
+
database(:local_database) do
|
721
|
+
fortunes = Fortune.all
|
722
|
+
return fortunes[rand(fortunes.size)].text
|
723
|
+
end
|
724
|
+
end
|
725
|
+
</code></pre>
|
726
|
+
|
727
|
+
If that is too tedious, you can specify the database connection manually in the
|
728
|
+
<tt>leaves.yml</tt> file:
|
729
|
+
|
730
|
+
<pre><code>
|
731
|
+
FortuneBot:
|
732
|
+
class: FortuneLeaf
|
733
|
+
database: local_database
|
734
|
+
</code></pre>
|
735
|
+
|
736
|
+
OK, now onto the second special case. Imagine you want your fortune bot to also
|
737
|
+
send a fortune in response to a CTCP VERSION request. So, you'd implement a
|
738
|
+
method like so:
|
739
|
+
|
740
|
+
<pre><code>
|
741
|
+
def ctcp_version_request(handler, stem, sender, arguments)
|
742
|
+
fortune = random_fortune # Loads a random fortune
|
743
|
+
send_ctcp_reply stem, sender[:nick], 'VERSION', fortune.text
|
744
|
+
end
|
745
|
+
</code></pre>
|
746
|
+
|
747
|
+
This will break -- why? Because the @ctcp_version_request@ method is in the
|
748
|
+
realm of the @Autumn::CTCP@ class, _not_ the @Autumn::Leaf@ class. (You can see
|
749
|
+
this by investigating the CTCP class docs; it shows you what methods you can
|
750
|
+
implement for CTCP support.) Basically, the @CTCP@ class calls your method
|
751
|
+
directly, giving the @Autumn::Leaf@ class no chance to set up the database
|
752
|
+
first. So to fix it, make a call to @database@ first:
|
753
|
+
|
754
|
+
<pre><code>
|
755
|
+
def ctcp_version_request(handler, stem, sender, arguments)
|
756
|
+
fortune = database { random_fortune }
|
757
|
+
send_ctcp_reply stem, sender[:nick], 'VERSION', fortune.text
|
758
|
+
end
|
759
|
+
</code></pre>
|
760
|
+
|
761
|
+
This will execute those methods in the scope of the database connection guessed
|
762
|
+
by @Autumn::Leaf@. Of course, you can manually pass in a connection name if
|
763
|
+
necessary.
|
764
|
+
|
765
|
+
*Another important note:* You will need to make a call to @database@ in any
|
766
|
+
child threads your leaf creates. The database context is not automatically
|
767
|
+
carried over to such threads.
|
768
|
+
|
769
|
+
h3. Your Leaf's Module; or, "What Do I Do About Namespace Conflicts?"
|
770
|
+
|
771
|
+
So, if you have two database-backed leaves, it's entirely likely that both of
|
772
|
+
them will use some sort of DataMapper resource named @Channel@, or something
|
773
|
+
similar. You can't define the class @Channel@ twice in two different ways, so
|
774
|
+
how do you deal with this?
|
775
|
+
|
776
|
+
The answer is: It's already dealt with for you. Go ahead and define the class
|
777
|
+
twice. Or three times.
|
778
|
+
|
779
|
+
The longer explanation is: Secretly, behind the scenes, *all your leaf code is
|
780
|
+
being cleverly loaded into a module named after your leaf*. So, when, in your
|
781
|
+
<tt>controller.rb</tt> code, it says @class Controller < Autumn::Leaf@, you
|
782
|
+
should read it as @class MyLeafName::Controller < Autumn::Leaf@. When you define
|
783
|
+
your model with @class Channel@, it's really read as <code>class
|
784
|
+
MyLeafName::Channel</code>.
|
785
|
+
|
786
|
+
Don't worry about table names or associations or anything, either. Just go ahead
|
787
|
+
and use it as if it weren't in a module. The <tt>libs/datamapper_hacks.rb</tt>
|
788
|
+
file has all the necessary code changes to make this bit of trickery work.
|
789
|
+
|
790
|
+
h3. Using Support Modules
|
791
|
+
|
792
|
+
Helper modules placed in your leaf's <tt>helpers</tt> directory will
|
793
|
+
automatically be loaded and included in your leaf controller and views. To
|
794
|
+
create a helper module, place Ruby files to be loaded into the <tt>helpers</tt>
|
795
|
+
directory. Make sure your helper modules' names end with the word "Helper".
|
796
|
+
|
797
|
+
For instance, if your leaf's name is "Fortune", and you needed two helpers, a
|
798
|
+
database helper and a network helper, you could create two modules named
|
799
|
+
@DatabaseHelper@ and @NetworkHelper@. Any modules named in this fashion and
|
800
|
+
placed in the <tt>helpers</tt> subdirectory will be loaded and appended to the
|
801
|
+
controller and its views automatically.
|
802
|
+
|
803
|
+
h3. Debugging Your Leaf
|
804
|
+
|
805
|
+
If you make a simple code change to your leaf, you can reload it without having
|
806
|
+
to restart the whole process. See the @Autumn::Leaf#reload_command@
|
807
|
+
documentation for more information on when and how you can reload your leaf's
|
808
|
+
code.
|
809
|
+
|
810
|
+
If an error occurs on a live production instance, it will be logged to the log
|
811
|
+
file for your season. You can inspect the log file to determine what went wrong.
|
812
|
+
|
813
|
+
If the error happens before the logger is available, oftentimes it will appear
|
814
|
+
in the <tt>autumn.output</tt> or <tt>autumn.log</tt> files. These files are
|
815
|
+
generated by the daemon library and note any uncaught exceptions or standard
|
816
|
+
outs. They are in the <tt>tmp</tt> directory.
|
817
|
+
|
818
|
+
The most tricky of errors can happen before the process is daemonized. If your
|
819
|
+
process is quitting prematurely, and you don't see anything in either log file,
|
820
|
+
consider running @script/server@, allowing you to see any exceptions for
|
821
|
+
yourself.
|
822
|
+
|
823
|
+
Unfortunately, it's still possible that the bug might not appear when you do
|
824
|
+
this, but only appear when the process is daemonized. In this situation, I'd
|
825
|
+
recommend installing rdebug (@sudo gem install rdebug@) and stepping through the
|
826
|
+
code to figure out what's going wrong. In particular, make sure you step into
|
827
|
+
the @Foliater@'s @start_stems@ method, when it creates the new threads. It's
|
828
|
+
possible your exception will rear its head once you step into that line of code.
|
829
|
+
|
830
|
+
h2. Stems
|
831
|
+
|
832
|
+
@Autumn::Stem@ is a full-featured IRC client library, written from the ground up
|
833
|
+
for Autumn. It makes extensive use of implicit protocols, meaning that most
|
834
|
+
features are accessed by implementing the methods you feel are necessary.
|
835
|
+
|
836
|
+
Most of the time, you will only work with stems indirectly via leaves. For
|
837
|
+
instance, if you want an "!opped" command that returns true if the sender is an
|
838
|
+
operator, it would look like this:
|
839
|
+
|
840
|
+
<pre><code>
|
841
|
+
def opped_command(stem, sender, reply_to, msg)
|
842
|
+
stem.channel_members[reply_to][sender[:nick]] == :operator ? "You are opped." : "You are not opped."
|
843
|
+
end
|
844
|
+
</code></pre>
|
845
|
+
|
846
|
+
Let's break this down. In order to figure out if someone is opped or not, we
|
847
|
+
need three pieces of information: their nick, the channel they are in, and the
|
848
|
+
IRC server they are connected to.
|
849
|
+
|
850
|
+
The @stem@ parameter contains the @Autumn::Stem@ instance that received this
|
851
|
+
message. It is our link to that server. Through it we can perform IRC actions
|
852
|
+
and make requests.
|
853
|
+
|
854
|
+
@Autumn::Stem@ includes an attribute @channel_members@, a hash of channels
|
855
|
+
mapped to their members. The channel that received the message is passed via the
|
856
|
+
@reply_to@ parameter. So we call @channel_members[reply_to]@ and we receive a
|
857
|
+
hash of member names to their privilege levels. The @sender@ parameter contains
|
858
|
+
information about the person who sent the command, including their nick. So we
|
859
|
+
use their nick to resolve their privilege level.
|
860
|
+
|
861
|
+
Complicated? Sure it is. That's the price we pay for separating stems from
|
862
|
+
leaves. But what if you, like probably 90% of the people out there who use
|
863
|
+
Autumn, only have one stem? Why should you have to call the same damn stem each
|
864
|
+
and every time?
|
865
|
+
|
866
|
+
Fortunately, your pleas are not in vain. For leaves that run off only one stem,
|
867
|
+
the stem's methods are rolled right into the leaf. So, that "!opped" command
|
868
|
+
method becomes:
|
869
|
+
|
870
|
+
<pre><code>
|
871
|
+
def opped_command(stem, sender, reply_to, msg)
|
872
|
+
channel_members[reply_to][sender[:nick]] == :operator ? "You are opped." : "You are not opped."
|
873
|
+
end
|
874
|
+
</code></pre>
|
875
|
+
|
876
|
+
OK, so it's not like a world-class improvement, but it helps.
|
877
|
+
|
878
|
+
The primary thing your leaf will probably do with a @Stem@ instance is use it to
|
879
|
+
send messages, like so:
|
880
|
+
|
881
|
+
<pre><code>
|
882
|
+
def about_command(stem, sender, reply_to, msg)
|
883
|
+
stem.message "I am a pretty awesome bot!", reply_to
|
884
|
+
end
|
885
|
+
</code></pre>
|
886
|
+
|
887
|
+
Fortunately, if you just return a string, @Autumn::Leaf@ will automatically send
|
888
|
+
it for you, simplifying our method:
|
889
|
+
|
890
|
+
<pre><code>
|
891
|
+
def about_command(stem, sender, reply_to, msg)
|
892
|
+
"I am a pretty awesome bot!"
|
893
|
+
end
|
894
|
+
</code></pre>
|
895
|
+
|
896
|
+
You would still interact with the stem directly if you wanted to do something
|
897
|
+
like announce your leaf's presence to everyone. To do this, you'd have to send
|
898
|
+
a message to every channel of every stem the leaf is a listener for:
|
899
|
+
|
900
|
+
<code>stems.each { |stem| stem.channels.each { |channel| stem.message "Hello!", channel } }</code>
|
901
|
+
|
902
|
+
But! @Autumn::Stem#message@ will automatically send a message to every channel
|
903
|
+
if you don't specify any channels, simplifying our code to:
|
904
|
+
|
905
|
+
<code>stems.each { |stem| stem.message "Hello!" }</code>
|
906
|
+
|
907
|
+
It gets even better. *You can call methods on the @stems@ array as if it were a
|
908
|
+
stem itself!* This simplifies the line significantly:
|
909
|
+
|
910
|
+
<code>stems.message "Hello!"</code>
|
911
|
+
|
912
|
+
Pretty nifty, huh? This also works for functions as well as methods; for
|
913
|
+
instance, the @Autumn::Stem#ready?@ function, which returns true if a stem is
|
914
|
+
ready:
|
915
|
+
|
916
|
+
<code>stems.ready? #=> [ true, true, false, true ]</code> (for example)
|
917
|
+
|
918
|
+
h3. The nitty-gritty of stems
|
919
|
+
|
920
|
+
The section above dealt with stems as they relate to leaves. But when would you
|
921
|
+
need to deal with a stem directly? Generally, never. However, if you find that
|
922
|
+
@Autumn::Leaf@ doesn't have what you need, you may have to turn to
|
923
|
+
@Autumn::Stem@ to get the functionality you are looking for. So let's take a
|
924
|
+
look at how Stem works.
|
925
|
+
|
926
|
+
A stem interacts with interested parties via the listener protocol. Your leaf
|
927
|
+
signals its interest to a stem by calling @Autumn::Stem#add_listener@. When a
|
928
|
+
leaf or any other object becomes a stem's listener, that stem then invokes
|
929
|
+
methods on the listener whenever an IRC event occurs.
|
930
|
+
|
931
|
+
Let's take a simple example. Assume you wanted to build a basic textual IRC
|
932
|
+
client using Stem. You'd first want to indicate that your client is a listener:
|
933
|
+
|
934
|
+
<pre><code>
|
935
|
+
class MyClient
|
936
|
+
def initialize(stem)
|
937
|
+
@stem = stem
|
938
|
+
@stem.add_listener self
|
939
|
+
end
|
940
|
+
end
|
941
|
+
</code></pre>
|
942
|
+
|
943
|
+
Now the stem will send method calls to your @MyClient@ instance every time an
|
944
|
+
IRC event occurs. None of these methods are required -- you can implement as few
|
945
|
+
or as many as you want. The different methods that @Stem@ will send are
|
946
|
+
documented in the @Autumn::Stem#add_listener@ method docs. One very important
|
947
|
+
method is the @irc_privmsg_event@ method. Let's implement it:
|
948
|
+
|
949
|
+
<pre><code>
|
950
|
+
def irc_privmsg_event(stem, sender, arguments)
|
951
|
+
puts "#{arguments[:channel]} <#{sender[:nick]}> #{arguments[:message]}"
|
952
|
+
end
|
953
|
+
</code></pre>
|
954
|
+
|
955
|
+
Now we've got the most important part of our IRC client done -- receiving
|
956
|
+
messages.
|
957
|
+
|
958
|
+
You can also send IRC events using stem. It's simple: Every IRC command (such as
|
959
|
+
JOIN and PRIVMSG and MODE) has a corresponding method in @Stem@ (such as @join@
|
960
|
+
and @privmsg@ and @mode@). These methods aren't in the API docs because they're
|
961
|
+
implemented using @method_missing@. Their arguments are exactly the same as the
|
962
|
+
arguments the IRC command expects, and in the same order.
|
963
|
+
|
964
|
+
So how do we send a message? Well according to RFC-1459, the basic IRC spec, the
|
965
|
+
PRIVMSG command takes two arguments: a list of receivers, and the text to be
|
966
|
+
sent. So, we know our method call should look something like this:
|
967
|
+
|
968
|
+
@stem.privmsg recipient, message
|
969
|
+
|
970
|
+
Astute readers will note that the spec shows a _list_ of recipients, and indeed,
|
971
|
+
you can call the method like so:
|
972
|
+
|
973
|
+
@stem.privmsg [ recipient1, recipient2 ], message
|
974
|
+
|
975
|
+
That's the basics of how @Autumn::Stem@ works, but there's one other thing worth
|
976
|
+
mentioning, and that's listener plugins. The details are in the
|
977
|
+
@Autumn::Stem#add_listener@ method docs, but the short of it is that these are
|
978
|
+
special listeners that bestow their powers onto other listeners.
|
979
|
+
|
980
|
+
The best example of this is the @Autumn::CTCP@ class. This class is indeed a
|
981
|
+
@Stem@ listener: It listens to PRIVMSG events from the stem, and checks them to
|
982
|
+
see if they are CTCP requests. However, it _also_ gives you, the author of
|
983
|
+
another listener (such as your leaf) the ability to implement methods according
|
984
|
+
to _its_ protocol.
|
985
|
+
|
986
|
+
For example, say you wanted to respond to CTCP VERSION requests with your own
|
987
|
+
version information. You do it like so:
|
988
|
+
|
989
|
+
<pre><code>
|
990
|
+
def ctcp_version_request(handler, stem, sender, arguments)
|
991
|
+
send_ctcp_reply stem, sender[:nick], 'VERSION', "AwesomeBot 2.0 by Sancho Sample"
|
992
|
+
end
|
993
|
+
</code></pre>
|
994
|
+
|
995
|
+
What's going on here? Because the @Autumn::CTCP@ class is a listener plugin, it
|
996
|
+
is sending its own method calls as well as implementing @Stem@'s method calls.
|
997
|
+
One such call is the @ctcp_version_request@ method, which you can see in the
|
998
|
+
@CTCP@ class docs. Somewhere deep in the annals of @Autumn::Foliater@, there is
|
999
|
+
some code similar to the following:
|
1000
|
+
|
1001
|
+
<pre><code>
|
1002
|
+
ctcp = Autumn::CTCP.new
|
1003
|
+
stem.add_listener ctcp
|
1004
|
+
</code></pre>
|
1005
|
+
|
1006
|
+
Thus, every stem comes pre-fab with a CTCP listener plugin. That plugin is
|
1007
|
+
intercepting PRIVMSG events and checking if they're CTCP requests. If they are,
|
1008
|
+
it is invoking methods, such as @ctcp_version_request@, in all of the stem's
|
1009
|
+
other listeners, among which is your leaf. Hopefully you understand how this all
|
1010
|
+
fits together.
|
1011
|
+
|
1012
|
+
The lesson to take home here is two-fold: Firstly, if you'd like CTCP support in
|
1013
|
+
your leaf, know that it's the @Autumn::CTCP@ class that is providing the method
|
1014
|
+
calls to your leaf, not the @Autumn::Stem@ class. Secondly, this should
|
1015
|
+
hopefully give you some ideas should you want to write your own listener plugin
|
1016
|
+
to enhance @Stem@'s functionality.
|
1017
|
+
|
1018
|
+
h2. Autumn's Logging
|
1019
|
+
|
1020
|
+
Autumn uses Ruby's @Logger@ class to log; however, it uses @Autumn::LogFacade@
|
1021
|
+
to prepend additional information to each log entry. The @LogFacade@ class has
|
1022
|
+
the exact same external API as @Logger@, so you can use it like a typical Ruby
|
1023
|
+
or Ruby on Rails logger. Many objects (such as @Leaf@ and @Stem@) include a
|
1024
|
+
@logger@ attribute:
|
1025
|
+
|
1026
|
+
<pre><code>
|
1027
|
+
logger.debug "Debug statement"
|
1028
|
+
logger.fatal $!
|
1029
|
+
</code></pre>
|
1030
|
+
|
1031
|
+
See the @LogFacade@ class docs for details.
|
1032
|
+
|
1033
|
+
h2. Tasks
|
1034
|
+
|
1035
|
+
The included Rakefile contains a number of useful tasks to help you develop and
|
1036
|
+
deploy your leaves. You can always get a list of tasks by typing @rake --tasks@.
|
1037
|
+
The various commands you can run are:
|
1038
|
+
|
1039
|
+
Application tasks:
|
1040
|
+
|
1041
|
+
| @rake app:start@ | Starts the Autumn daemon in the background. |
|
1042
|
+
| @rake app:stop@ | Stops the Autumn daemon. |
|
1043
|
+
| @rake app:restart@ | Reloads the Autumn daemons. |
|
1044
|
+
| @rake app:run@ | Starts the Autumn daemon in the foreground. |
|
1045
|
+
| @rake app:zap@ | Forces the daemon to a stopped state. Use this command if your daemon is not running but <tt>script/daemon</tt> thinks it still is. |
|
1046
|
+
|
1047
|
+
Database tasks:
|
1048
|
+
|
1049
|
+
| @LEAF=[leaf name] rake db:migrate@ | Creates all the tables for a leaf, as specified by the leaf's model objects |
|
1050
|
+
|
1051
|
+
Documentation tasks:
|
1052
|
+
|
1053
|
+
| @rake doc:api@ | Generates HTML documentation for Autumn, found in the <tt>doc/api</tt> directory. |
|
1054
|
+
| @rake doc:leaves@ | Generates HTML documentation for your leaves, found in the <tt>doc/leaves</tt> directory. |
|
1055
|
+
| @rake doc:clear@ | Removes all HTML documentation. |
|
1056
|
+
|
1057
|
+
Logging tasks:
|
1058
|
+
|
1059
|
+
| @rake log:clear@ | Clears the log files for all seasons. |
|
1060
|
+
| @rake log:errors@ | Prints a list of error-level log messages for the current season, and uncaught exceptions in all seasons. |
|
1061
|
+
|
1062
|
+
h3. Custom leaf tasks
|
1063
|
+
|
1064
|
+
You can define your own leaf-specific tasks in the <tt>tasks</tt> subdirectory
|
1065
|
+
within your leaf's directory. Any <tt>.rake</tt> files there will be loaded by
|
1066
|
+
rake. The tasks will be added within a task-group named after your leaf. Use
|
1067
|
+
Scorekeeper as an example: If you type @rake --tasks@, you'll see one other
|
1068
|
+
task, @rake scorekeeper:scores@. The "scores" task is defined in the
|
1069
|
+
<tt>leaves/scorekeeper/tasks/stats.rake</tt> file, and placed in the
|
1070
|
+
"scorekeeper" task group by Autumn.
|
1071
|
+
|
1072
|
+
Also, if you open that file up, you'll notice that you have to refer to your
|
1073
|
+
leaf's classes by their _full_ names, including the leaf module. (See *Your
|
1074
|
+
Leaf's Module* if you're confused.)
|
1075
|
+
|
1076
|
+
h2. Scripts
|
1077
|
+
|
1078
|
+
Autumn includes some scripts to help you control it.
|
1079
|
+
|
1080
|
+
h3. <tt>script/console</tt>
|
1081
|
+
|
1082
|
+
Bootstraps an IRb console with the Autumn environment configured. Stems and
|
1083
|
+
leaves are accessile from the Foliater instance. DataMapper models can be used.
|
1084
|
+
Does not start any stems (in other words, no actual server login occurs).
|
1085
|
+
|
1086
|
+
Usage: @script/console [options]@
|
1087
|
+
|
1088
|
+
where [options] may contain:
|
1089
|
+
|
1090
|
+
| @--irb@ | Invoke a different Ruby terminal. |
|
1091
|
+
|
1092
|
+
You can alter the season by setting the @SEASON@ environment variable.
|
1093
|
+
|
1094
|
+
h3. <tt>script/daemon</tt>
|
1095
|
+
|
1096
|
+
Controller for the Autumn daemon. Starts, stops, and manages the daemon. Must be
|
1097
|
+
run from the Autumn root directory.
|
1098
|
+
|
1099
|
+
Usage: @script/daemon [command] [options] -- [application options]@
|
1100
|
+
|
1101
|
+
where [command] is one of:
|
1102
|
+
|
1103
|
+
| @start@ | start an instance of the application |
|
1104
|
+
| @stop@ | stop all instances of the application |
|
1105
|
+
| @restart@ | stop all instances and restart them afterwards |
|
1106
|
+
| @run@ | start the application and stay on top |
|
1107
|
+
| @zap@ | set the application to a stopped state |
|
1108
|
+
|
1109
|
+
and where [options] may contain several of the following:
|
1110
|
+
|
1111
|
+
| @-t, --ontop@ | Stay on top (does not daemonize) |
|
1112
|
+
| @-f, --force@ | Force operation |
|
1113
|
+
|
1114
|
+
Common options:
|
1115
|
+
|
1116
|
+
| @-h, --help@ | Show this message |
|
1117
|
+
| @--version@ | Show version |
|
1118
|
+
|
1119
|
+
h3. <tt>script/destroy</tt>
|
1120
|
+
|
1121
|
+
Destroys the files for leaves, seasons, and other objects of the Autumn
|
1122
|
+
framework.
|
1123
|
+
|
1124
|
+
Usage: @script/destroy [options] [object] [name]@
|
1125
|
+
|
1126
|
+
| [object] | The object type to destroy. Valid types are "leaf" and "season". |
|
1127
|
+
| [name] | The name of the object to destroy. For example, you can call @script/destroy leaf Scorekeeper@ to remove a leaf named Scorekeeper. |
|
1128
|
+
|
1129
|
+
| @--help, -h@ | Displays this usage information. |
|
1130
|
+
| @--vcs, -c@ | Remove any created files or directories from the project's version control system. (Autodetects CVS, Git, and Subversion.) |
|
1131
|
+
|
1132
|
+
h3. <tt>script/generate</tt>
|
1133
|
+
|
1134
|
+
Generates template files for leaves, seasons, and other Autumn objects.
|
1135
|
+
|
1136
|
+
Usage: @script/generate [options] [template] [name]@
|
1137
|
+
|
1138
|
+
| [template] | The template to create. Valid templates are "leaf" and "season". |
|
1139
|
+
| [name] | The name to give the created template. For example, you can call @script/generate leaf Scorekeeper@ to create a leaf named Scorekeeper. |
|
1140
|
+
|
1141
|
+
| @--help, -h@ | Displays this usage information. |
|
1142
|
+
| @--vcs, -c@ | Add any created files or directories to the project's version control system. (Autodetects CVS, Git, and Subversion.) |
|
1143
|
+
|
1144
|
+
h3. <tt>script/server</tt>
|
1145
|
+
|
1146
|
+
Runs Autumn from the command line. This script will not exit until all leaves
|
1147
|
+
have exited. You can set the @SEASON@ environment variable to override the
|
1148
|
+
season.
|
1149
|
+
|
1150
|
+
h2. Thread Safety
|
1151
|
+
|
1152
|
+
Autumn is a multi-threaded IRC client. When a message is received, a new thread
|
1153
|
+
is spawned to process the message. In this thread, the message will be parsed,
|
1154
|
+
and all listener hooks will be invoked, including your leaf's methods. The
|
1155
|
+
thread will terminate once the message has been fully processed and all methods
|
1156
|
+
invoked.
|
1157
|
+
|
1158
|
+
I have made every effort to ensure that @Autumn::Stem@ and @Autumn::Leaf@ are
|
1159
|
+
thread-safe, as well as other relevant support classes such as @Autumn::CTCP@.
|
1160
|
+
It is now in your hands to ensure your leaves are thread-safe! This basically
|
1161
|
+
means recognizing that, while your leaf is churning away at whatever command it
|
1162
|
+
received, things can and will change in the background. If your command requires
|
1163
|
+
your leaf to have operator privileges, write your code under the assumption that
|
1164
|
+
operator could be taken from your leaf in the middle of executing the command.
|
1165
|
+
Write data in critical blocks, use transactions in your database calls ... you
|
1166
|
+
know the deal. Don't assume things will be the same between one line of code and
|
1167
|
+
the next.
|
1168
|
+
|
1169
|
+
h2. Getting Ready for Deployment
|
1170
|
+
|
1171
|
+
There's only a few things you need to do once your leaf is ready to greet
|
1172
|
+
the Real World:
|
1173
|
+
|
1174
|
+
# Create a new production season. Configure your stems, leaves, and database
|
1175
|
+
as necessary for your production environment.
|
1176
|
+
# In <tt>config/global.yml</tt>, set the season to your production season.
|
1177
|
+
# If desired, in <tt>script/daemon</tt>, set the @:monitor@ option to true.
|
1178
|
+
This will spawn a monitor process that will relaunch Autumn if it crashes.
|
1179
|
+
|
1180
|
+
h2. Other Information
|
1181
|
+
|
1182
|
+
Please consult the "list of known bugs":http://github.com/RISCfuture/autumn/wikis/known-bugs
|
1183
|
+
and "version history":http://github.com/RISCfuture/autumn/wikis/version-history
|
1184
|
+
for more information.
|
1185
|
+
|
1186
|
+
*_Why do you require Facets?_, I hear you ask. Facets doesn't add any super
|
1187
|
+
awesome new features to Ruby like Daemons or DataMapper does. It does, however,
|
1188
|
+
improve code reuse, and I'm a big fan of that. Why should a million different
|
1189
|
+
Ruby projects all write the same @Symbol#to_proc@ method or the same
|
1190
|
+
@Hash#symbolize_keys@ method? I use Facets because that job has already been
|
1191
|
+
done, and staying DRY means staying DRY _between_ codebases, not just within
|
1192
|
+
them.
|