comboy-autumn 3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/README.textile +1192 -0
  2. data/autumn.gemspec +25 -0
  3. data/bin/autumn +27 -0
  4. data/lib/autumn.rb +2 -0
  5. data/lib/autumn/authentication.rb +290 -0
  6. data/lib/autumn/channel_leaf.rb +107 -0
  7. data/lib/autumn/coder.rb +166 -0
  8. data/lib/autumn/console_boot.rb +9 -0
  9. data/lib/autumn/ctcp.rb +250 -0
  10. data/lib/autumn/daemon.rb +207 -0
  11. data/lib/autumn/datamapper_hacks.rb +290 -0
  12. data/lib/autumn/foliater.rb +231 -0
  13. data/lib/autumn/formatting.rb +236 -0
  14. data/lib/autumn/generator.rb +231 -0
  15. data/lib/autumn/genesis.rb +191 -0
  16. data/lib/autumn/inheritable_attributes.rb +162 -0
  17. data/lib/autumn/leaf.rb +738 -0
  18. data/lib/autumn/log_facade.rb +49 -0
  19. data/lib/autumn/misc.rb +87 -0
  20. data/lib/autumn/script.rb +74 -0
  21. data/lib/autumn/speciator.rb +165 -0
  22. data/lib/autumn/stem.rb +919 -0
  23. data/lib/autumn/stem_facade.rb +176 -0
  24. data/resources/daemons/Anothernet.yml +3 -0
  25. data/resources/daemons/AustHex.yml +29 -0
  26. data/resources/daemons/Bahamut.yml +67 -0
  27. data/resources/daemons/Dancer.yml +3 -0
  28. data/resources/daemons/GameSurge.yml +3 -0
  29. data/resources/daemons/IRCnet.yml +3 -0
  30. data/resources/daemons/Ithildin.yml +7 -0
  31. data/resources/daemons/KineIRCd.yml +56 -0
  32. data/resources/daemons/PTlink.yml +6 -0
  33. data/resources/daemons/QuakeNet.yml +20 -0
  34. data/resources/daemons/RFC1459.yml +158 -0
  35. data/resources/daemons/RFC2811.yml +16 -0
  36. data/resources/daemons/RFC2812.yml +36 -0
  37. data/resources/daemons/RatBox.yml +25 -0
  38. data/resources/daemons/Ultimate.yml +24 -0
  39. data/resources/daemons/Undernet.yml +6 -0
  40. data/resources/daemons/Unreal.yml +110 -0
  41. data/resources/daemons/_Other.yml +7 -0
  42. data/resources/daemons/aircd.yml +33 -0
  43. data/resources/daemons/bdq-ircd.yml +3 -0
  44. data/resources/daemons/hybrid.yml +38 -0
  45. data/resources/daemons/ircu.yml +67 -0
  46. data/resources/daemons/tr-ircd.yml +8 -0
  47. data/skel/Rakefile +135 -0
  48. data/skel/config/global.yml +2 -0
  49. data/skel/config/seasons/testing/database.yml +7 -0
  50. data/skel/config/seasons/testing/leaves.yml +7 -0
  51. data/skel/config/seasons/testing/season.yml +2 -0
  52. data/skel/config/seasons/testing/stems.yml +9 -0
  53. data/skel/leaves/administrator/README +20 -0
  54. data/skel/leaves/administrator/controller.rb +67 -0
  55. data/skel/leaves/administrator/views/autumn.txt.erb +1 -0
  56. data/skel/leaves/administrator/views/reload.txt.erb +11 -0
  57. data/skel/leaves/insulter/README +17 -0
  58. data/skel/leaves/insulter/controller.rb +65 -0
  59. data/skel/leaves/insulter/views/about.txt.erb +1 -0
  60. data/skel/leaves/insulter/views/help.txt.erb +1 -0
  61. data/skel/leaves/insulter/views/insult.txt.erb +1 -0
  62. data/skel/leaves/scorekeeper/README +34 -0
  63. data/skel/leaves/scorekeeper/config.yml +2 -0
  64. data/skel/leaves/scorekeeper/controller.rb +104 -0
  65. data/skel/leaves/scorekeeper/helpers/general.rb +64 -0
  66. data/skel/leaves/scorekeeper/models/channel.rb +12 -0
  67. data/skel/leaves/scorekeeper/models/person.rb +14 -0
  68. data/skel/leaves/scorekeeper/models/pseudonym.rb +11 -0
  69. data/skel/leaves/scorekeeper/models/score.rb +14 -0
  70. data/skel/leaves/scorekeeper/tasks/stats.rake +17 -0
  71. data/skel/leaves/scorekeeper/views/about.txt.erb +1 -0
  72. data/skel/leaves/scorekeeper/views/change.txt.erb +5 -0
  73. data/skel/leaves/scorekeeper/views/history.txt.erb +11 -0
  74. data/skel/leaves/scorekeeper/views/points.txt.erb +5 -0
  75. data/skel/leaves/scorekeeper/views/usage.txt.erb +1 -0
  76. data/skel/script/console +34 -0
  77. data/skel/script/daemon +29 -0
  78. data/skel/script/destroy +48 -0
  79. data/skel/script/generate +48 -0
  80. data/skel/script/server +15 -0
  81. 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.