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