autumn 3.1.8

Sign up to get free protection for your applications and to get access to all the features.
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.