autumn 3.1.8

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