butler 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/GPL.txt +340 -0
- data/LICENSE.txt +52 -0
- data/README +37 -0
- data/Rakefile +334 -0
- data/bin/botcontrol +230 -0
- data/data/butler/config_template.yaml +4 -0
- data/data/butler/dialogs/backup.rb +19 -0
- data/data/butler/dialogs/botcontrol.rb +4 -0
- data/data/butler/dialogs/config.rb +1 -0
- data/data/butler/dialogs/create.rb +53 -0
- data/data/butler/dialogs/delete.rb +3 -0
- data/data/butler/dialogs/en/backup.yaml +6 -0
- data/data/butler/dialogs/en/botcontrol.yaml +5 -0
- data/data/butler/dialogs/en/create.yaml +11 -0
- data/data/butler/dialogs/en/delete.yaml +2 -0
- data/data/butler/dialogs/en/help.yaml +17 -0
- data/data/butler/dialogs/en/info.yaml +13 -0
- data/data/butler/dialogs/en/list.yaml +4 -0
- data/data/butler/dialogs/en/notyetimplemented.yaml +2 -0
- data/data/butler/dialogs/en/rename.yaml +3 -0
- data/data/butler/dialogs/en/start.yaml +3 -0
- data/data/butler/dialogs/en/sync_plugins.yaml +3 -0
- data/data/butler/dialogs/en/uninstall.yaml +5 -0
- data/data/butler/dialogs/en/unknown_command.yaml +2 -0
- data/data/butler/dialogs/help.rb +11 -0
- data/data/butler/dialogs/info.rb +27 -0
- data/data/butler/dialogs/interactive.rb +1 -0
- data/data/butler/dialogs/list.rb +10 -0
- data/data/butler/dialogs/notyetimplemented.rb +1 -0
- data/data/butler/dialogs/rename.rb +4 -0
- data/data/butler/dialogs/selectbot.rb +2 -0
- data/data/butler/dialogs/start.rb +5 -0
- data/data/butler/dialogs/sync_plugins.rb +30 -0
- data/data/butler/dialogs/uninstall.rb +17 -0
- data/data/butler/dialogs/unknown_command.rb +1 -0
- data/data/butler/plugins/core/logout.rb +41 -0
- data/data/butler/plugins/core/plugins.rb +134 -0
- data/data/butler/plugins/core/privilege.rb +103 -0
- data/data/butler/plugins/core/user.rb +166 -0
- data/data/butler/plugins/dev/eval.rb +64 -0
- data/data/butler/plugins/dev/nometa.rb +14 -0
- data/data/butler/plugins/dev/onhandlers.rb +93 -0
- data/data/butler/plugins/dev/raw.rb +36 -0
- data/data/butler/plugins/dev/rawlog.rb +77 -0
- data/data/butler/plugins/games/eightball.rb +54 -0
- data/data/butler/plugins/games/mastermind.rb +174 -0
- data/data/butler/plugins/irc/action.rb +36 -0
- data/data/butler/plugins/irc/join.rb +38 -0
- data/data/butler/plugins/irc/notice.rb +36 -0
- data/data/butler/plugins/irc/part.rb +38 -0
- data/data/butler/plugins/irc/privmsg.rb +36 -0
- data/data/butler/plugins/irc/quit.rb +36 -0
- data/data/butler/plugins/operator/deop.rb +41 -0
- data/data/butler/plugins/operator/devoice.rb +41 -0
- data/data/butler/plugins/operator/limit.rb +47 -0
- data/data/butler/plugins/operator/op.rb +41 -0
- data/data/butler/plugins/operator/voice.rb +41 -0
- data/data/butler/plugins/public/help.rb +69 -0
- data/data/butler/plugins/public/login.rb +72 -0
- data/data/butler/plugins/public/usage.rb +49 -0
- data/data/butler/plugins/service/clones.rb +56 -0
- data/data/butler/plugins/service/define.rb +47 -0
- data/data/butler/plugins/service/log.rb +183 -0
- data/data/butler/plugins/service/svn.rb +91 -0
- data/data/butler/plugins/util/cycle.rb +98 -0
- data/data/butler/plugins/util/load.rb +41 -0
- data/data/butler/plugins/util/pong.rb +29 -0
- data/data/butler/strings/random/acknowledge.en.yaml +5 -0
- data/data/butler/strings/random/gratitude.en.yaml +3 -0
- data/data/butler/strings/random/hello.en.yaml +4 -0
- data/data/butler/strings/random/ignorance.en.yaml +7 -0
- data/data/butler/strings/random/ignorance_about.en.yaml +3 -0
- data/data/butler/strings/random/insult.en.yaml +3 -0
- data/data/butler/strings/random/rejection.en.yaml +12 -0
- data/data/man/botcontrol.1 +17 -0
- data/lib/access.rb +187 -0
- data/lib/access/admin.rb +16 -0
- data/lib/access/privilege.rb +122 -0
- data/lib/access/role.rb +102 -0
- data/lib/access/savable.rb +18 -0
- data/lib/access/user.rb +180 -0
- data/lib/access/yamlbase.rb +126 -0
- data/lib/butler.rb +188 -0
- data/lib/butler/bot.rb +247 -0
- data/lib/butler/control.rb +93 -0
- data/lib/butler/dialog.rb +64 -0
- data/lib/butler/initialvalues.rb +40 -0
- data/lib/butler/irc/channel.rb +135 -0
- data/lib/butler/irc/channels.rb +96 -0
- data/lib/butler/irc/client.rb +351 -0
- data/lib/butler/irc/hostmask.rb +53 -0
- data/lib/butler/irc/message.rb +184 -0
- data/lib/butler/irc/parser.rb +125 -0
- data/lib/butler/irc/parser/commands.rb +83 -0
- data/lib/butler/irc/parser/generic.rb +343 -0
- data/lib/butler/irc/socket.rb +378 -0
- data/lib/butler/irc/string.rb +186 -0
- data/lib/butler/irc/topic.rb +15 -0
- data/lib/butler/irc/user.rb +265 -0
- data/lib/butler/irc/users.rb +112 -0
- data/lib/butler/plugin.rb +249 -0
- data/lib/butler/plugin/configproxy.rb +35 -0
- data/lib/butler/plugin/mapper.rb +85 -0
- data/lib/butler/plugin/matcher.rb +55 -0
- data/lib/butler/plugin/onhandlers.rb +70 -0
- data/lib/butler/plugin/trigger.rb +58 -0
- data/lib/butler/plugins.rb +147 -0
- data/lib/butler/version.rb +17 -0
- data/lib/cloptions.rb +217 -0
- data/lib/cloptions/adapters.rb +24 -0
- data/lib/cloptions/switch.rb +132 -0
- data/lib/configuration.rb +223 -0
- data/lib/dialogline.rb +296 -0
- data/lib/dialogline/localizations.rb +24 -0
- data/lib/durations.rb +57 -0
- data/lib/event.rb +295 -0
- data/lib/event/at.rb +64 -0
- data/lib/event/every.rb +56 -0
- data/lib/event/timed.rb +112 -0
- data/lib/installer.rb +75 -0
- data/lib/iterator.rb +34 -0
- data/lib/log.rb +68 -0
- data/lib/log/comfort.rb +85 -0
- data/lib/log/converter.rb +23 -0
- data/lib/log/entry.rb +152 -0
- data/lib/log/fakeio.rb +55 -0
- data/lib/log/file.rb +54 -0
- data/lib/log/filereader.rb +81 -0
- data/lib/log/forward.rb +49 -0
- data/lib/log/methods.rb +39 -0
- data/lib/log/nolog.rb +18 -0
- data/lib/log/splitter.rb +26 -0
- data/lib/ostructfixed.rb +26 -0
- data/lib/ruby/array/columnize.rb +38 -0
- data/lib/ruby/dir/mktree.rb +28 -0
- data/lib/ruby/enumerable/join.rb +13 -0
- data/lib/ruby/exception/detailed.rb +24 -0
- data/lib/ruby/file/append.rb +11 -0
- data/lib/ruby/file/write.rb +11 -0
- data/lib/ruby/hash/zip.rb +15 -0
- data/lib/ruby/kernel/bench.rb +15 -0
- data/lib/ruby/kernel/daemonize.rb +42 -0
- data/lib/ruby/kernel/non_verbose.rb +17 -0
- data/lib/ruby/kernel/safe_fork.rb +18 -0
- data/lib/ruby/range/stepped.rb +11 -0
- data/lib/ruby/string/arguments.rb +72 -0
- data/lib/ruby/string/chunks.rb +15 -0
- data/lib/ruby/string/post_arguments.rb +44 -0
- data/lib/ruby/string/unescaped.rb +17 -0
- data/lib/scheduler.rb +164 -0
- data/lib/scriptfile.rb +101 -0
- data/lib/templater.rb +86 -0
- data/test/cloptions.rb +134 -0
- data/test/cv.rb +28 -0
- data/test/irc/client.rb +85 -0
- data/test/irc/client_login.txt +53 -0
- data/test/irc/client_subscribe.txt +8 -0
- data/test/irc/message.rb +30 -0
- data/test/irc/messages.txt +64 -0
- data/test/irc/parser.rb +13 -0
- data/test/irc/profile_parser.rb +12 -0
- data/test/irc/users.rb +28 -0
- metadata +256 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
class Butler
|
10
|
+
class Plugin
|
11
|
+
class ConfigProxy
|
12
|
+
def initialize(config, base="")
|
13
|
+
@config = config
|
14
|
+
@base = base
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(key, value)
|
18
|
+
@config[key.empty? ? @base : "#{@base}.#{key}"] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](key, *args)
|
22
|
+
@config[(key.empty? ? @base : "#{@base}.#{key}"), *args]
|
23
|
+
end
|
24
|
+
|
25
|
+
def exist?(key)
|
26
|
+
@config.exist?(key.empty? ? @base : "#{@base}.#{key}")
|
27
|
+
end
|
28
|
+
alias has_key? exist?
|
29
|
+
|
30
|
+
def delete(key)
|
31
|
+
@config.delete(key.empty? ? @base : "#{@base}.#{key}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'butler/plugin'
|
10
|
+
require 'ostruct'
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
class Butler
|
15
|
+
class Plugin
|
16
|
+
class Mapper
|
17
|
+
attr_reader :authorization
|
18
|
+
attr_reader :hash
|
19
|
+
attr_reader :language
|
20
|
+
attr_reader :name
|
21
|
+
attr_reader :plugin
|
22
|
+
attr_reader :trigger
|
23
|
+
attr_reader :hash
|
24
|
+
|
25
|
+
def initialize(plugin, meth, language, expression)
|
26
|
+
@plugin = plugin
|
27
|
+
@method = meth
|
28
|
+
@language = language
|
29
|
+
@expression = expression
|
30
|
+
@trigger = expression[/^\w+/].downcase
|
31
|
+
@captures = []
|
32
|
+
@authorization = "plugin/#{@plugin.base}".freeze
|
33
|
+
@name = "mapper:#{@language}:#{@expression}".freeze
|
34
|
+
@hash = @name.hash
|
35
|
+
|
36
|
+
tiles = expression.strip.scan(/(?:^|\s)[:*]\w+(?:\[[^\]]+\])?|\S+/).map { |word|
|
37
|
+
word.strip!
|
38
|
+
case word
|
39
|
+
when /:(\w+)(.*)/
|
40
|
+
@captures << [$1.to_sym, $2]
|
41
|
+
'(\S+)'
|
42
|
+
when /\*(\w+)(.*)/
|
43
|
+
@captures << [$1.to_sym, $2]
|
44
|
+
'(\S+(?:\s+\S+)*?)'
|
45
|
+
else
|
46
|
+
Regexp.escape(word)
|
47
|
+
end
|
48
|
+
}
|
49
|
+
@regexp = Regexp.new('^'+tiles.join('\s+')+'$')
|
50
|
+
end
|
51
|
+
|
52
|
+
def invoked_by?(message)
|
53
|
+
if match = @regexp.match(message.post_arguments[0]) then
|
54
|
+
params = {}
|
55
|
+
@captures.zip(match.captures) { |(name, valid), value|
|
56
|
+
params[name] = value
|
57
|
+
}
|
58
|
+
[OpenStruct.new(params)]
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def call(message, params)
|
65
|
+
@plugin.new(message).send(@method, params)
|
66
|
+
end
|
67
|
+
|
68
|
+
def priority
|
69
|
+
-10
|
70
|
+
end
|
71
|
+
|
72
|
+
def <=>(other)
|
73
|
+
-10 <=> other.priority
|
74
|
+
end
|
75
|
+
|
76
|
+
def abort_invocations?
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
def eql?(other)
|
81
|
+
other.kind_of?(Mapper) && @name.eql?(other.name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
class Butler
|
10
|
+
class Plugin
|
11
|
+
class Matcher
|
12
|
+
attr_reader :priority
|
13
|
+
attr_reader :hash
|
14
|
+
attr_reader :en_match
|
15
|
+
attr_reader :plugin
|
16
|
+
|
17
|
+
def initialize(matcher, priority=0, &callback)
|
18
|
+
@matcher = case matcher
|
19
|
+
when Hash: matcher
|
20
|
+
when Regexp: { "en" => matcher }
|
21
|
+
else raise ArgumentError, "Matcher must be either Regexp or Hash, but is #{matcher.class}"
|
22
|
+
end
|
23
|
+
raise ArgumentError, "Matcher must supply an english variant" unless @matcher["en"]
|
24
|
+
@priority = priority
|
25
|
+
@callback = callback
|
26
|
+
@en_matcher = @matcher["en"]
|
27
|
+
@hash = @en_matcher.hash
|
28
|
+
end
|
29
|
+
|
30
|
+
def name
|
31
|
+
"matcher:#{@en_matcher}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def <=>(other)
|
35
|
+
@priority <=> other.priority
|
36
|
+
end
|
37
|
+
|
38
|
+
def eql?(other)
|
39
|
+
other.kind_of?(Matcher) && @en_matcher.eql?(other.en_matcher)
|
40
|
+
end
|
41
|
+
|
42
|
+
def ===(message)
|
43
|
+
if @matcher.has_key?(message.language) then
|
44
|
+
@matcher[message.language] =~ message.text
|
45
|
+
else
|
46
|
+
@matcher["en"] =~ message.text
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def call(*args)
|
51
|
+
@callback.call(self, *args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
class Butler
|
10
|
+
class Plugin
|
11
|
+
module OnHandlers
|
12
|
+
def method_added(name, *a, &b)
|
13
|
+
subscribe_on_handler(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def subscribe_on_handler(handler, priority=0)
|
17
|
+
case handler
|
18
|
+
when :on_join: subscribe(:JOIN, priority) { |listener, message| new(message).on_join(listener, message.from, message.channel) }
|
19
|
+
when :on_privmsg: subscribe(:PRIVMSG, priority) { |listener, message| new(message).on_privmsg(listener, message.from, message.text) }
|
20
|
+
when :on_notice: subscribe(:NOTICE, priority) { |listener, message| new(message).on_notice(listener, message.from, message.text) }
|
21
|
+
when :on_nick: subscribe(:NICK, priority) { |listener, message| new(message).on_nick(listener, message.from, message.old_nick) }
|
22
|
+
when :on_topic: subscribe(:TOPIC, priority) { |listener, message| new(message).on_topic(listener, message.from, message.channel, message.text) }
|
23
|
+
when :on_part: subscribe(:PART, priority) { |listener, message| new(message).on_part(listener, message.from, message.channel) }
|
24
|
+
when :on_quit: subscribe(:QUIT, priority) { |listener, message| new(message).on_quit(listener, message.from, message.text) }
|
25
|
+
when :on_kick: subscribe(:KICK, priority) { |listener, message| new(message).on_kick(listener) }
|
26
|
+
when :on_kill: subscribe(:KILL, priority) { |listener, message| new(message).on_kill(listener) }
|
27
|
+
when :on_kline: subscribe(:KLINE, priority) { |listener, message| new(message).on_kline(listener) }
|
28
|
+
when :on_invocation
|
29
|
+
subscribe(:PRIVMSG, priority) { |listener, message|
|
30
|
+
new(message).on_invocation(listener) if message.invocation
|
31
|
+
}
|
32
|
+
subscribe(:NOTICE, priority) { |listener, message|
|
33
|
+
new(message).on_invocation(listener) if message.invocation
|
34
|
+
}
|
35
|
+
when :on_ban
|
36
|
+
subscribe(:MODE, priority) { |listener, message|
|
37
|
+
bans = message.modes.select { |dir, type, target| dir == '+' && type == 'b' }
|
38
|
+
new(message).on_ban(listener, *bans.map { |a,b,c| c }) unless bans.empty?
|
39
|
+
}
|
40
|
+
when :on_unban
|
41
|
+
subscribe(:MODE, priority) { |listener, message|
|
42
|
+
unbans = message.modes.select { |dir, type, target| dir == '-' && type == 'b' }
|
43
|
+
new(message).on_unban(listener, *unbans.map { |a,b,c| c }) unless unbans.empty?
|
44
|
+
}
|
45
|
+
when :on_op
|
46
|
+
subscribe(:MODE, priority) { |listener, message|
|
47
|
+
ops = message.modes.select { |dir, type, target| dir == '+' && type == 'o' }
|
48
|
+
new(message).on_op(listener, *ops.map { |a,b,c| c }) unless ops.empty?
|
49
|
+
}
|
50
|
+
when :on_deop
|
51
|
+
subscribe(:MODE, priority) { |listener, message|
|
52
|
+
deops = message.modes.select { |dir, type, target| dir == '-' && type == 'o' }
|
53
|
+
new(message).on_deop(listener, *deops.map { |a,b,c| c }) unless deops.empty?
|
54
|
+
}
|
55
|
+
when :on_voice
|
56
|
+
subscribe(:MODE, priority) { |listener, message|
|
57
|
+
voices = message.modes.select { |dir, type, target| dir == '+' && type == 'v' }
|
58
|
+
new(message).on_voice(listener, *voices.map { |a,b,c| c }) unless voices.empty?
|
59
|
+
}
|
60
|
+
when :on_devoice
|
61
|
+
subscribe(:MODE, priority) { |listener, message|
|
62
|
+
devoices = message.modes.select { |dir, type, target| dir == '-' && type == 'v' }
|
63
|
+
new(message).on_devoice(listener, *devoices.map { |a,b,c| c }) unless devoices.empty?
|
64
|
+
}
|
65
|
+
# :on_action => :PRIVMSG, :NOTICE
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'butler/plugin'
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
class Butler
|
14
|
+
class Plugin
|
15
|
+
class Trigger
|
16
|
+
attr_reader :authorization
|
17
|
+
attr_reader :hash
|
18
|
+
attr_reader :language
|
19
|
+
attr_reader :name
|
20
|
+
attr_reader :plugin
|
21
|
+
attr_reader :trigger
|
22
|
+
attr_reader :hash
|
23
|
+
|
24
|
+
def initialize(plugin, language, trigger)
|
25
|
+
@plugin = plugin
|
26
|
+
@language = language
|
27
|
+
@trigger = trigger.downcase
|
28
|
+
@authorization = "plugin/#{@plugin.base}".freeze
|
29
|
+
@name = "trigger:#{@language}:#{@trigger}".freeze
|
30
|
+
@hash = @name.hash
|
31
|
+
end
|
32
|
+
|
33
|
+
def invoked_by?(message)
|
34
|
+
[]
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(message)
|
38
|
+
@plugin.new(message).on_trigger
|
39
|
+
end
|
40
|
+
|
41
|
+
def priority
|
42
|
+
0
|
43
|
+
end
|
44
|
+
|
45
|
+
def <=>(other)
|
46
|
+
0 <=> other.priority
|
47
|
+
end
|
48
|
+
|
49
|
+
def abort_invocations?
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
def eql?(other)
|
54
|
+
other.kind_of?(Trigger) && @name.eql?(other.name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
require 'scriptfile'
|
10
|
+
require 'butler/plugin'
|
11
|
+
require 'butler/dialog'
|
12
|
+
require 'iterator'
|
13
|
+
require 'log/comfort'
|
14
|
+
require 'ruby/exception/detailed'
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
class String
|
19
|
+
# CamelCase a string, e.g. "foo_bar" becomes "FooBar"
|
20
|
+
def camelcase
|
21
|
+
scan(/[^_]+/).map { |s| s.capitalize }.join("")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Butler
|
26
|
+
# Plugins manages the plugins in Butler
|
27
|
+
# It uses
|
28
|
+
class Plugins
|
29
|
+
include Enumerable
|
30
|
+
include Log::Comfort
|
31
|
+
|
32
|
+
Suffix = ".rb"
|
33
|
+
|
34
|
+
attr_reader :dir
|
35
|
+
def initialize(butler, plugin_dir)
|
36
|
+
@dir = File.expand_path(plugin_dir).freeze
|
37
|
+
raise ArgumentError, "#{@dir} is not a directory" unless File.directory?(@dir)
|
38
|
+
@plugins = {}
|
39
|
+
@constants = {}
|
40
|
+
@butler = butler
|
41
|
+
end
|
42
|
+
|
43
|
+
# returns a list with first-level groups
|
44
|
+
def groups
|
45
|
+
slice = @dir.length+1..-1
|
46
|
+
Dir[@dir+"/*"].reject { |file|
|
47
|
+
File.extname(file).downcase == Suffix
|
48
|
+
}.map { |file|
|
49
|
+
file[slice]
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def instances
|
54
|
+
@plugins.values
|
55
|
+
end
|
56
|
+
|
57
|
+
# returns a list with the bases of all plugins
|
58
|
+
def all
|
59
|
+
slice = @dir.length+1..-(Suffix.length+1)
|
60
|
+
Dir["#{@dir}/**/*#{Suffix}"].select { |file| File.file?(file) }.map { |file|
|
61
|
+
file[slice]
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
# returns a list with the names of active plugins
|
66
|
+
def active
|
67
|
+
@plugins.keys
|
68
|
+
end
|
69
|
+
|
70
|
+
def inactive
|
71
|
+
all-active
|
72
|
+
end
|
73
|
+
|
74
|
+
def loaded?(base)
|
75
|
+
@plugins.has_key?(base)
|
76
|
+
end
|
77
|
+
|
78
|
+
def [](base)
|
79
|
+
@plugins[base]
|
80
|
+
end
|
81
|
+
|
82
|
+
def identify(name)
|
83
|
+
plugin = Dir["#{@dir}/**/*#{name}#{Suffix}"].first
|
84
|
+
plugin && plugin[@dir.length+1..-(Suffix.length+1)]
|
85
|
+
end
|
86
|
+
|
87
|
+
def load_all
|
88
|
+
all.each { |base|
|
89
|
+
load(base)
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def load_group(group)
|
94
|
+
slice = @dir.length+1..-(Suffix.length+1)
|
95
|
+
Dir["#{@dir}/#{group}/**/*#{Suffix}"].select { |file| File.file?(file) }.each { |file|
|
96
|
+
load(file[slice])
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
def load(base)
|
101
|
+
path = "#{@dir}/#{base.downcase}#{Suffix}"
|
102
|
+
name = File.basename(base).camelcase
|
103
|
+
codefile = path
|
104
|
+
|
105
|
+
unless File.file?(codefile) then
|
106
|
+
raise ArgumentError, "Invalid plugin '#{base}' (codefile: #{codefile})."
|
107
|
+
end
|
108
|
+
begin
|
109
|
+
unload(base) if loaded?(base) # a plugin may not be loaded twice
|
110
|
+
rescue Exception => e
|
111
|
+
exception(e)
|
112
|
+
end
|
113
|
+
begin
|
114
|
+
constant = "%s_%08X" % [name, rand(0xffffffff)]
|
115
|
+
end while Butler::Plugins.const_defined?(constant)
|
116
|
+
plugin = Butler::Plugins.const_set(constant, Class.new(Plugin))
|
117
|
+
plugin.log_device = @butler.log_device
|
118
|
+
begin
|
119
|
+
plugin.load_plugin(@butler, base, path)
|
120
|
+
plugin.class_eval(File.read(codefile), codefile)
|
121
|
+
plugin.on_load
|
122
|
+
rescue Exception => e
|
123
|
+
e.extend Exception::Detailed
|
124
|
+
e.prepend "Loading plugin #{base} failed."
|
125
|
+
exception(e)
|
126
|
+
end
|
127
|
+
@constants[base] = constant.freeze
|
128
|
+
@plugins[base] = plugin
|
129
|
+
end
|
130
|
+
alias reload load
|
131
|
+
|
132
|
+
def unload(base)
|
133
|
+
begin
|
134
|
+
@plugins[base].on_unload
|
135
|
+
@plugins[base].unload_plugin
|
136
|
+
rescue Exception => e
|
137
|
+
e.extend Exception::Detailed
|
138
|
+
e.prepend "Exception raised while unloading plugin #{base}."
|
139
|
+
exception(e)
|
140
|
+
end
|
141
|
+
Butler::Plugins.send(:remove_const, @constants[base])
|
142
|
+
@plugins.delete(base)
|
143
|
+
@constants.delete(base)
|
144
|
+
true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|