bougyman-autumn 3.1.1

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