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.
- data/README.textile +1192 -0
- data/autumn.gemspec +25 -0
- data/bin/autumn +27 -0
- data/lib/autumn.rb +2 -0
- data/lib/autumn/authentication.rb +290 -0
- data/lib/autumn/channel_leaf.rb +107 -0
- data/lib/autumn/coder.rb +166 -0
- data/lib/autumn/console_boot.rb +9 -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 +191 -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/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/resources/daemons/Anothernet.yml +3 -0
- data/resources/daemons/AustHex.yml +29 -0
- data/resources/daemons/Bahamut.yml +67 -0
- data/resources/daemons/Dancer.yml +3 -0
- data/resources/daemons/GameSurge.yml +3 -0
- data/resources/daemons/IRCnet.yml +3 -0
- data/resources/daemons/Ithildin.yml +7 -0
- data/resources/daemons/KineIRCd.yml +56 -0
- data/resources/daemons/PTlink.yml +6 -0
- data/resources/daemons/QuakeNet.yml +20 -0
- data/resources/daemons/RFC1459.yml +158 -0
- data/resources/daemons/RFC2811.yml +16 -0
- data/resources/daemons/RFC2812.yml +36 -0
- data/resources/daemons/RatBox.yml +25 -0
- data/resources/daemons/Ultimate.yml +24 -0
- data/resources/daemons/Undernet.yml +6 -0
- data/resources/daemons/Unreal.yml +110 -0
- data/resources/daemons/_Other.yml +7 -0
- data/resources/daemons/aircd.yml +33 -0
- data/resources/daemons/bdq-ircd.yml +3 -0
- data/resources/daemons/hybrid.yml +38 -0
- data/resources/daemons/ircu.yml +67 -0
- data/resources/daemons/tr-ircd.yml +8 -0
- data/skel/Rakefile +135 -0
- data/skel/config/global.yml +2 -0
- data/skel/config/seasons/testing/database.yml +7 -0
- data/skel/config/seasons/testing/leaves.yml +7 -0
- data/skel/config/seasons/testing/season.yml +2 -0
- data/skel/config/seasons/testing/stems.yml +9 -0
- data/skel/leaves/administrator/README +20 -0
- data/skel/leaves/administrator/controller.rb +67 -0
- data/skel/leaves/administrator/views/autumn.txt.erb +1 -0
- data/skel/leaves/administrator/views/reload.txt.erb +11 -0
- data/skel/leaves/insulter/README +17 -0
- data/skel/leaves/insulter/controller.rb +65 -0
- data/skel/leaves/insulter/views/about.txt.erb +1 -0
- data/skel/leaves/insulter/views/help.txt.erb +1 -0
- data/skel/leaves/insulter/views/insult.txt.erb +1 -0
- data/skel/leaves/scorekeeper/README +34 -0
- data/skel/leaves/scorekeeper/config.yml +2 -0
- data/skel/leaves/scorekeeper/controller.rb +104 -0
- data/skel/leaves/scorekeeper/helpers/general.rb +64 -0
- data/skel/leaves/scorekeeper/models/channel.rb +12 -0
- data/skel/leaves/scorekeeper/models/person.rb +14 -0
- data/skel/leaves/scorekeeper/models/pseudonym.rb +11 -0
- data/skel/leaves/scorekeeper/models/score.rb +14 -0
- data/skel/leaves/scorekeeper/tasks/stats.rake +17 -0
- data/skel/leaves/scorekeeper/views/about.txt.erb +1 -0
- data/skel/leaves/scorekeeper/views/change.txt.erb +5 -0
- data/skel/leaves/scorekeeper/views/history.txt.erb +11 -0
- data/skel/leaves/scorekeeper/views/points.txt.erb +5 -0
- data/skel/leaves/scorekeeper/views/usage.txt.erb +1 -0
- data/skel/script/console +34 -0
- data/skel/script/daemon +29 -0
- data/skel/script/destroy +48 -0
- data/skel/script/generate +48 -0
- data/skel/script/server +15 -0
- 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,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
|