comboy-autumn 3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. data/README.textile +1192 -0
  2. data/autumn.gemspec +25 -0
  3. data/bin/autumn +27 -0
  4. data/lib/autumn.rb +2 -0
  5. data/lib/autumn/authentication.rb +290 -0
  6. data/lib/autumn/channel_leaf.rb +107 -0
  7. data/lib/autumn/coder.rb +166 -0
  8. data/lib/autumn/console_boot.rb +9 -0
  9. data/lib/autumn/ctcp.rb +250 -0
  10. data/lib/autumn/daemon.rb +207 -0
  11. data/lib/autumn/datamapper_hacks.rb +290 -0
  12. data/lib/autumn/foliater.rb +231 -0
  13. data/lib/autumn/formatting.rb +236 -0
  14. data/lib/autumn/generator.rb +231 -0
  15. data/lib/autumn/genesis.rb +191 -0
  16. data/lib/autumn/inheritable_attributes.rb +162 -0
  17. data/lib/autumn/leaf.rb +738 -0
  18. data/lib/autumn/log_facade.rb +49 -0
  19. data/lib/autumn/misc.rb +87 -0
  20. data/lib/autumn/script.rb +74 -0
  21. data/lib/autumn/speciator.rb +165 -0
  22. data/lib/autumn/stem.rb +919 -0
  23. data/lib/autumn/stem_facade.rb +176 -0
  24. data/resources/daemons/Anothernet.yml +3 -0
  25. data/resources/daemons/AustHex.yml +29 -0
  26. data/resources/daemons/Bahamut.yml +67 -0
  27. data/resources/daemons/Dancer.yml +3 -0
  28. data/resources/daemons/GameSurge.yml +3 -0
  29. data/resources/daemons/IRCnet.yml +3 -0
  30. data/resources/daemons/Ithildin.yml +7 -0
  31. data/resources/daemons/KineIRCd.yml +56 -0
  32. data/resources/daemons/PTlink.yml +6 -0
  33. data/resources/daemons/QuakeNet.yml +20 -0
  34. data/resources/daemons/RFC1459.yml +158 -0
  35. data/resources/daemons/RFC2811.yml +16 -0
  36. data/resources/daemons/RFC2812.yml +36 -0
  37. data/resources/daemons/RatBox.yml +25 -0
  38. data/resources/daemons/Ultimate.yml +24 -0
  39. data/resources/daemons/Undernet.yml +6 -0
  40. data/resources/daemons/Unreal.yml +110 -0
  41. data/resources/daemons/_Other.yml +7 -0
  42. data/resources/daemons/aircd.yml +33 -0
  43. data/resources/daemons/bdq-ircd.yml +3 -0
  44. data/resources/daemons/hybrid.yml +38 -0
  45. data/resources/daemons/ircu.yml +67 -0
  46. data/resources/daemons/tr-ircd.yml +8 -0
  47. data/skel/Rakefile +135 -0
  48. data/skel/config/global.yml +2 -0
  49. data/skel/config/seasons/testing/database.yml +7 -0
  50. data/skel/config/seasons/testing/leaves.yml +7 -0
  51. data/skel/config/seasons/testing/season.yml +2 -0
  52. data/skel/config/seasons/testing/stems.yml +9 -0
  53. data/skel/leaves/administrator/README +20 -0
  54. data/skel/leaves/administrator/controller.rb +67 -0
  55. data/skel/leaves/administrator/views/autumn.txt.erb +1 -0
  56. data/skel/leaves/administrator/views/reload.txt.erb +11 -0
  57. data/skel/leaves/insulter/README +17 -0
  58. data/skel/leaves/insulter/controller.rb +65 -0
  59. data/skel/leaves/insulter/views/about.txt.erb +1 -0
  60. data/skel/leaves/insulter/views/help.txt.erb +1 -0
  61. data/skel/leaves/insulter/views/insult.txt.erb +1 -0
  62. data/skel/leaves/scorekeeper/README +34 -0
  63. data/skel/leaves/scorekeeper/config.yml +2 -0
  64. data/skel/leaves/scorekeeper/controller.rb +104 -0
  65. data/skel/leaves/scorekeeper/helpers/general.rb +64 -0
  66. data/skel/leaves/scorekeeper/models/channel.rb +12 -0
  67. data/skel/leaves/scorekeeper/models/person.rb +14 -0
  68. data/skel/leaves/scorekeeper/models/pseudonym.rb +11 -0
  69. data/skel/leaves/scorekeeper/models/score.rb +14 -0
  70. data/skel/leaves/scorekeeper/tasks/stats.rake +17 -0
  71. data/skel/leaves/scorekeeper/views/about.txt.erb +1 -0
  72. data/skel/leaves/scorekeeper/views/change.txt.erb +5 -0
  73. data/skel/leaves/scorekeeper/views/history.txt.erb +11 -0
  74. data/skel/leaves/scorekeeper/views/points.txt.erb +5 -0
  75. data/skel/leaves/scorekeeper/views/usage.txt.erb +1 -0
  76. data/skel/script/console +34 -0
  77. data/skel/script/daemon +29 -0
  78. data/skel/script/destroy +48 -0
  79. data/skel/script/generate +48 -0
  80. data/skel/script/server +15 -0
  81. metadata +170 -0
data/autumn.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'autumn'
3
+ s.version = '3.1'
4
+ s.date = '2009-04-04'
5
+
6
+ s.summary = "A Ruby IRC Bot Framework"
7
+ #s.description = ""
8
+
9
+ s.authors = ['Tim Morgan','Kacper Cieśla']
10
+ s.email = ''
11
+ s.homepage = ''
12
+
13
+ s.bindir = 'bin'
14
+ s.executables << 'autumn'
15
+
16
+ s.has_rdoc = true
17
+ s.rdoc_options = ['--main', 'README.textile']
18
+ s.rdoc_options << '--inline-source' << '--charset=UTF-8'
19
+ s.extra_rdoc_files = ['README.textile']
20
+
21
+ # Does not look very cool but github need static lest to build a gem
22
+ s.files = %w{shared README.textile lib lib/autumn.rb lib/autumn lib/autumn/console_boot.rb lib/autumn/log_facade.rb lib/autumn/channel_leaf.rb lib/autumn/datamapper_hacks.rb lib/autumn/authentication.rb lib/autumn/daemon.rb lib/autumn/stem.rb lib/autumn/generator.rb lib/autumn/leaf.rb lib/autumn/coder.rb lib/autumn/misc.rb lib/autumn/ctcp.rb lib/autumn/formatting.rb lib/autumn/foliater.rb lib/autumn/stem_facade.rb lib/autumn/genesis.rb lib/autumn/script.rb lib/autumn/inheritable_attributes.rb lib/autumn/speciator.rb bin bin/autumn README skel skel/tmp skel/log skel/leaves skel/leaves/administrator skel/leaves/administrator/data skel/leaves/administrator/models skel/leaves/administrator/tasks skel/leaves/administrator/README skel/leaves/administrator/controller.rb skel/leaves/administrator/helpers skel/leaves/administrator/views skel/leaves/administrator/views/reload.txt.erb skel/leaves/administrator/views/autumn.txt.erb skel/leaves/insulter skel/leaves/insulter/data skel/leaves/insulter/models skel/leaves/insulter/tasks skel/leaves/insulter/README skel/leaves/insulter/controller.rb skel/leaves/insulter/helpers skel/leaves/insulter/views skel/leaves/insulter/views/help.txt.erb skel/leaves/insulter/views/about.txt.erb skel/leaves/insulter/views/insult.txt.erb skel/leaves/scorekeeper skel/leaves/scorekeeper/data skel/leaves/scorekeeper/models skel/leaves/scorekeeper/models/person.rb skel/leaves/scorekeeper/models/channel.rb skel/leaves/scorekeeper/models/pseudonym.rb skel/leaves/scorekeeper/models/score.rb skel/leaves/scorekeeper/tasks skel/leaves/scorekeeper/tasks/stats.rake skel/leaves/scorekeeper/README skel/leaves/scorekeeper/controller.rb skel/leaves/scorekeeper/helpers skel/leaves/scorekeeper/helpers/general.rb skel/leaves/scorekeeper/config.yml skel/leaves/scorekeeper/views skel/leaves/scorekeeper/views/about.txt.erb skel/leaves/scorekeeper/views/change.txt.erb skel/leaves/scorekeeper/views/history.txt.erb skel/leaves/scorekeeper/views/points.txt.erb skel/leaves/scorekeeper/views/usage.txt.erb skel/config skel/config/seasons skel/config/seasons/testing skel/config/seasons/testing/stems.yml skel/config/seasons/testing/database.yml skel/config/seasons/testing/leaves.yml skel/config/seasons/testing/season.yml skel/config/global.yml skel/Rakefile skel/script skel/script/server skel/script/console skel/script/destroy skel/script/generate skel/script/daemon autumn.gemspec doc resources resources/daemons resources/daemons/tr-ircd.yml resources/daemons/Dancer.yml resources/daemons/_Other.yml resources/daemons/Undernet.yml resources/daemons/QuakeNet.yml resources/daemons/aircd.yml resources/daemons/IRCnet.yml resources/daemons/KineIRCd.yml resources/daemons/RFC2811.yml resources/daemons/Ultimate.yml resources/daemons/hybrid.yml resources/daemons/GameSurge.yml resources/daemons/AustHex.yml resources/daemons/Unreal.yml resources/daemons/Ithildin.yml resources/daemons/bdq-ircd.yml resources/daemons/RFC1459.yml resources/daemons/RFC2812.yml resources/daemons/Anothernet.yml resources/daemons/Bahamut.yml resources/daemons/ircu.yml resources/daemons/RatBox.yml resources/daemons/PTlink.yml}
23
+
24
+ end
25
+
data/bin/autumn ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+
5
+ if ARGV.empty?
6
+ puts "Usage: autumn /path/to/app"
7
+ puts "\nDescription:"
8
+ puts " Creates new autumn app skeleton in the given directory."
9
+ else
10
+ begin
11
+ dir = ARGV[0]
12
+ print "Preparing application skeleton in directory #{dir}... "; STDOUT.flush
13
+ Dir.mkdir dir
14
+ my_dir = File.expand_path(File.dirname(__FILE__))
15
+ FileUtils.cp_r Dir.glob(File.join(my_dir,'..','skel','*')), dir
16
+
17
+ # There's a small problem with packaging empty dirs into a gem
18
+ FileUtils.mkdir_p File.join(dir,'tmp')
19
+ FileUtils.mkdir_p File.join(dir,'log')
20
+ puts "DONE"
21
+
22
+ rescue Errno::EEXIST => e
23
+ puts "Directory already exists."
24
+ exit 1
25
+ end
26
+
27
+ end
data/lib/autumn.rb ADDED
@@ -0,0 +1,2 @@
1
+ AUTUMN_LIB_DIR = File.expand_path(File.dirname(__FILE__))
2
+ $: << AUTUMN_LIB_DIR
@@ -0,0 +1,290 @@
1
+ # Defines the Autumn::Authentication class, which includes different
2
+ # authentication strategies available to leaves.
3
+
4
+ module Autumn
5
+
6
+ # Defines classes which each encapsulate a different strategy for
7
+ # authentication. When the +authentication+ option is specified (see the
8
+ # Autumn::Leaf class), the options given are used to choose the correct class
9
+ # within this module to serve as the authenticator for that leaf.
10
+ #
11
+ # These authentication strategies are used to ensure only authorized users
12
+ # have access to protected commands. Leaf authors can designate certain
13
+ # commands as protected.
14
+ #
15
+ # = Writing Your Own Authenticators
16
+ #
17
+ # When the Autumn::Leaf#authenticate method is called, it converts the symbol
18
+ # from snake_case to CamelCase, and looks for a class in this model. Thus, a
19
+ # call to <tt>authenticate :hostname</tt> would look for a class
20
+ # Autumn::Authentication::Hostname.
21
+ #
22
+ # To define your own authenticator, subclass Autumn::Authentication::Base as a
23
+ # new class in the Autumn::Authentication module. Implement the methods
24
+ # defined in the Autumn::Authentication::Base class docs, then adjust your
25
+ # configuration to use your new authenticator.
26
+
27
+ module Authentication
28
+
29
+ # The basic subclass for all authenticators. If you wish to write your own
30
+ # authenticator, you must subclass this class. You must at a minimum
31
+ # override the authenticate method. You should also override the initialize
32
+ # method if you need to store any options or other data for later use.
33
+ #
34
+ # The authentication module will become a stem listener, so see
35
+ # Autumn::Stem#add_listener for information on other methods you can
36
+ # implement.
37
+
38
+ class Base
39
+
40
+ # Stores the options for this authenticator and configures it for use.
41
+
42
+ def initialize(options={})
43
+ raise "You can only instantiate subclasses of this class."
44
+ end
45
+
46
+ # Returns true if the user is authorized, false if not. +sender+ is a
47
+ # sender hash as defined in the Autumn::Stem docs.
48
+
49
+ def authenticate(stem, channel, sender, leaf)
50
+ raise "Subclasses must override the Autumn::Authentication::Base#authenticate method."
51
+ end
52
+
53
+ # Returns a string to be displayed to a user who is not authorized to
54
+ # perform a command. Override this method to provide more specific hints
55
+ # to a user on what he can do to authorize himself (e.g., "Tell me your
56
+ # password").
57
+
58
+ def unauthorized
59
+ "You must be an administrator for this bot to do that."
60
+ end
61
+ end
62
+
63
+ # Authenticates users by their privilege level in the channel they ran the
64
+ # command in.
65
+ #
66
+ # This is a quick, configuration-free way of protecting your leaf, so long
67
+ # as you trust the ops in your channel.
68
+
69
+ class Op < Base
70
+
71
+ # Creates a new authenticator. Pass a list of allowed privileges (as
72
+ # symbols) for the +privileges+ option. By default this class accepts ops,
73
+ # admins, and channel owners/founders as authorized.
74
+
75
+ def initialize(options={})
76
+ @privileges = options[:privileges]
77
+ @privileges ||= [ :operator, :oper, :op, :admin, :founder, :channel_owner ]
78
+ end
79
+
80
+ def authenticate(stem, channel, sender, leaf) # :nodoc:
81
+ # Returns true if the sender has any of the privileges listed below
82
+ not (@privileges & [ stem.privilege(channel, sender) ].flatten).empty?
83
+ end
84
+
85
+ def unauthorized # :nodoc:
86
+ "You must be an op to do that."
87
+ end
88
+ end
89
+
90
+ # Authenticates by IRC nick. A list of allowed nicks is built on
91
+ # initialization, and anyone with that nick is allowed to use restricted
92
+ # commands.
93
+ #
94
+ # This is the most obvious approach to authentication, but it is hardly
95
+ # secure. Anyone can change their nick to an admin's nick once that admin
96
+ # logs out.
97
+
98
+ class Nick < Base
99
+
100
+ # Creates a new authenticator. Pass a single nick for the +nick+ option or
101
+ # an array of allowed nicks for the +nicks+ option. If neither option is
102
+ # set, an exception is raised.
103
+
104
+ def initialize(options={})
105
+ @nicks = options[:nick]
106
+ @nicks ||= options[:nicks]
107
+ raise "You must give the nick of an administrator to use nick-based authentication." if @nicks.nil?
108
+ @nicks = [ @nicks ] if @nicks.kind_of? String
109
+ end
110
+
111
+ def authenticate(stem, channel, sender, leaf) # :nodoc:
112
+ @nicks.include? sender[:nick]
113
+ end
114
+ end
115
+
116
+ # Works just like nick authentication, the only difference is that one has
117
+ # to be identified to the nickserver.
118
+ #
119
+ # This currently will only work on ircd, dancer and dalnet servers.
120
+
121
+ class RegisteredNick < Base
122
+
123
+ def initialize(options={})
124
+ puts "RegisterNick initialization"
125
+ @nicks = options[:nick]
126
+ @nicks ||= options[:nicks]
127
+ raise "You must must the nick of an administrator to use nick-based authentication." if @nicks.nil?
128
+ @nicks = [ @nicks ] if @nicks.kind_of? String
129
+ @identified = {}
130
+ @whois_lock = Mutex.new
131
+ @identification_lock = Mutex.new
132
+ end
133
+
134
+ def authenticate(stem, channel, sender, leaf) # :nodoc:
135
+ nick = sender[:nick]
136
+ @nicks.include?(nick) && is_identified?(stem,nick)
137
+ end
138
+
139
+ # Respnse code that comes at the end of whois message
140
+ def irc_rpl_endofwhois_response(stem, sender, recipient, arguments, msg)
141
+ @whois_lock.unlock
142
+ end
143
+
144
+ # Dancer and ircd response code including line with identification information
145
+ def irc_rpl_whois_hidden_response(stem, sender, recipient, arguments, msg)
146
+ @identified[arguments.first] = true if msg.strip == "is identified to services"
147
+ end
148
+
149
+ # DALnet response code including line with identification information
150
+ def irc_rpl_whoisregnick_response(stem, sender, recipient, arguments, msg)
151
+ @identified[arguments.first] = true if msg.strip == "has identified for this nick"
152
+ end
153
+
154
+ private
155
+
156
+ def is_identified?(stem,nick)
157
+ @identification_lock.synchronize do
158
+ @whois_lock.lock
159
+ @identified[nick] = false
160
+ stem.whois nick
161
+ @whois_lock.synchronize {}
162
+ @identified[nick]
163
+ end
164
+ end
165
+
166
+ end
167
+
168
+ # Authenticates by the host portion of an IRC message. A hostmask is used to
169
+ # match the relevant portion of the address with a whitelist of accepted
170
+ # host addresses.
171
+ #
172
+ # This method can be a secure way of preventing unauthorized access if you
173
+ # choose an appropriately narrow hostmask. However, you must configure in
174
+ # advance the computers you may want to administrate your leaves from.
175
+
176
+ class Hostname < Base
177
+
178
+ # Creates a new authenticator. You provide a hostmask via the +hostmask+
179
+ # option -- either a Regexp with one capture (that captures the portion of
180
+ # the hostmask you are interested in), or a Proc, which takes a host as an
181
+ # argument and returns true if the host is authorized, false if not. If
182
+ # the +hostmask+ option is not provided, a standard hostmask regexp will
183
+ # be used. This regexp strips everything left of the first period; for the
184
+ # example hostmask "wsd1.ca.widgetcom.net", it would return
185
+ # "ca.widgetcom.net" to be used for comparison.
186
+ #
187
+ # You also provide an authorized host with the +host+ option, or a list of
188
+ # such hosts with the +hosts+ option. If neither is given, an exception is
189
+ # raised.
190
+
191
+ def initialize(options={})
192
+ @hostmask = options[:hostmask]
193
+ @hostmask ||= /^.+?\.(.+)$/
194
+ @hostmask = @hostmask.to_rx(false) if @hostmask.kind_of? String
195
+ if @hostmask.kind_of? Regexp then
196
+ mask = @hostmask
197
+ @hostmask = lambda do |host|
198
+ if matches = host.match(mask) then matches[1] else nil end
199
+ end
200
+ end
201
+
202
+ @hosts = options[:host]
203
+ @hosts ||= options[:hosts]
204
+ raise "You must give the host address of an administrator to use nick-based authentication." unless @hosts
205
+ @hosts = [ @hosts ] unless @hosts.kind_of? Array
206
+ end
207
+
208
+ def authenticate(stem, channel, sender, leaf) # :nodoc:
209
+ @hosts.include? @hostmask.call(sender[:host])
210
+ end
211
+ end
212
+
213
+ # Authenticates by a password provided in secret. When a user PRIVMSG's the
214
+ # leaf the correct password, the leaf adds that user's nick to a list of
215
+ # authorized nicks. These credentials expire when the person changes his
216
+ # nick, logs out, leaves the channel, etc. They also expire if a certain
217
+ # amount of time passes without running any protected commands.
218
+
219
+ class Password < Base
220
+ # The default period of time that must occur with no use of protected
221
+ # commands after which a user's credentials expire.
222
+ DEFAULT_EXPIRE_TIME = 5*60
223
+
224
+ # Creates a new authenticator. You provide a valid password with the
225
+ # +password+ option. If that option is not provided, an exception is
226
+ # raised. You can pass a number of seconds to the +expire_time+ option;
227
+ # this is the amount of time that must pass with no protected commands for
228
+ # a nick's authorization to expire. If the +expire_time+ option is not
229
+ # given, a default value of five minutes is used.
230
+
231
+ def initialize(options={})
232
+ @password = options[:password]
233
+ @expire_time = options[:expire_time]
234
+ @expire_time ||= DEFAULT_EXPIRE_TIME
235
+ raise "You must provide a password to use password-based authentication" unless @password
236
+ @authorized_nicks = Hash.new { |hsh, key| hsh[key] = Set.new }
237
+ @last_protected_action = Hash.new { |hsh, key| hsh[key] = Hash.new(Time.at(0)) }
238
+ @an_lock = Mutex.new
239
+ end
240
+
241
+ def irc_privmsg_event(stem, sender, arguments) # :nodoc:
242
+ if arguments[:recipient] and arguments[:message] == @password then
243
+ @an_lock.synchronize do
244
+ @authorized_nicks[stem] << sender[:nick]
245
+ @last_protected_action[stem][sender[:nick]] = Time.now
246
+ #TODO values are not always deleted; this hash has the possibility to slowly grow and consume more memory
247
+ end
248
+ stem.message "Your password has been accepted, and you are now authorized.", sender[:nick]
249
+ end
250
+ end
251
+
252
+ def irc_nick_event(stem, sender, arguments) # :nodoc:
253
+ @an_lock.synchronize do
254
+ revoke stem, sender[:nick]
255
+ revoke stem, arguments[:nick]
256
+ end
257
+ end
258
+
259
+ def irc_kick_event(stem, sender, arguments) # :nodoc:
260
+ @an_lock.synchronize { revoke stem, arguments[:nick] }
261
+ end
262
+
263
+ def irc_quit_event(stem, sender, arguments) # :nodoc:
264
+ @an_lock.synchronize { revoke stem, sender[:nick] }
265
+ end
266
+
267
+ def authenticate(stem, channel, sender, leaf) # :nodoc:
268
+ @an_lock.synchronize do
269
+ if Time.now - @last_protected_action[stem][sender[:nick]] > @expire_time then
270
+ revoke stem, sender[:nick]
271
+ else
272
+ @last_protected_action[stem][sender[:nick]] = Time.now
273
+ end
274
+ @authorized_nicks[stem].include? sender[:nick]
275
+ end
276
+ end
277
+
278
+ def unauthorized # :nodoc:
279
+ "You must authenticate with an administrator password to do that."
280
+ end
281
+
282
+ private
283
+
284
+ def revoke(stem, nick)
285
+ @authorized_nicks[stem].delete nick
286
+ @last_protected_action[stem].delete nick
287
+ end
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,107 @@
1
+ # Defines the Autumn::ChannelLeaf class, a subclass of Autumn::Leaf that
2
+ # selectively ignores channels.
3
+
4
+ module Autumn
5
+
6
+ # A special kind of leaf that only responds to messages sent to certain
7
+ # channels. Leaves that subclass ChannelLeaf can, in their config, specify a
8
+ # +channels+ option that narrows down which channels the leaf listens to. The
9
+ # leaf will not invoke the hook methods nor the <tt>*_command</tt> methods for
10
+ # IRC events that are not associated with those channels. It will respond to
11
+ # global, non-channel-specific events as well.
12
+ #
13
+ # You can combine multiple ChannelLeaf subclasses in one Stem to allow you to
14
+ # run two leaves off of one nick, but have the nick running different leaves
15
+ # in different channels.
16
+ #
17
+ # The +channels+ option should be a list of stems, and for each stem, a valid
18
+ # channel. For example, if you ran your leaf on two servers, your stems.yml
19
+ # file might look like:
20
+ #
21
+ # GamingServer:
22
+ # channels: fishinggames, games, drivinggames
23
+ # [...]
24
+ # FishingServer:
25
+ # channels: fishinggames, flyfishing
26
+ # [...]
27
+ #
28
+ # Now let's say you had a trivia leaf that asked questions about fishing
29
+ # games. You'd want to run that leaf on the "#fishinggames" channel of each
30
+ # server, and the "#games" channel of the GamingServer, but not the other
31
+ # channels. (Perhaps your Stem was also running other leaves relevant to those
32
+ # channels.) You'd set up your leaves.yml file like so:
33
+ #
34
+ # FishingGamesTrivia:
35
+ # channels:
36
+ # GamingServer:
37
+ # - fishinggames
38
+ # - games
39
+ # FishingServer: fishinggames
40
+ # [...]
41
+ #
42
+ # Now your leaf will only respond to messages relevant to the specified server
43
+ # channels (as well as global messages).
44
+ #
45
+ # Interception and filtering of messages is done at the _leaf_ level, not the
46
+ # _stem_ level. Therefore, for instance, if you override
47
+ # +someone_did_join_channel+, it will only be called for the appropriate
48
+ # channels; however, if you implement +irc_join_event+, it will still be
49
+ # called for all channels the stem is in.
50
+
51
+ class ChannelLeaf < Leaf
52
+ # The IRC channels that this leaf is responding to, mapped to server names.
53
+ attr :channels
54
+
55
+ # Creates a new instance. (See the Leaf class for more information.)
56
+
57
+ def will_start_up
58
+ @channels = Hash.new
59
+ @options[:channels] ||= Hash.new
60
+ @options[:channels].each do |server, chans|
61
+ stem = Foliater.instance.stems[server]
62
+ raise "Unknown stem #{server}" unless stem
63
+ chans = [ chans ] if chans.kind_of? String
64
+ @channels[stem] = chans.map { |chan| stem.normalized_channel_name chan }
65
+ end
66
+ super
67
+ end
68
+
69
+ def irc_privmsg_event(stem, sender, arguments) # :nodoc:
70
+ super if arguments[:channel].nil? or listening?(stem, arguments[:channel])
71
+ end
72
+
73
+ def irc_join_event(stem, sender, arguments) # :nodoc:
74
+ super if listening?(stem, arguments[:channel])
75
+ end
76
+
77
+ def irc_part_event(stem, sender, arguments) # :nodoc:
78
+ super if listening?(stem, arguments[:channel])
79
+ end
80
+
81
+ def irc_mode_event(stem, sender, arguments) # :nodoc:
82
+ super if arguments[:channel].nil? or listening?(stem, arguments[:channel])
83
+ end
84
+
85
+ def irc_topic_event(stem, sender, arguments) # :nodoc:
86
+ super if listening?(stem, arguments[:channel])
87
+ end
88
+
89
+ def irc_invite_event(stem, sender, arguments) # :nodoc:
90
+ super if listening?(stem, arguments[:channel]) or not stem.channels.include? arguments[:channel]
91
+ end
92
+
93
+ def irc_kick_event(stem, sender, arguments) # :nodoc:
94
+ super if listening?(stem, arguments[:channel])
95
+ end
96
+
97
+ def irc_notice_event(stem, sender, arguments) # :nodoc:
98
+ super if arguments[:channel].nil? or listening?(stem, arguments[:channel])
99
+ end
100
+
101
+ private
102
+
103
+ def listening?(stem, channel)
104
+ @channels.include? stem and @channels[stem].include? channel
105
+ end
106
+ end
107
+ end