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