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