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.
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.