butler 1.8.0 → 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +40 -0
- data/README +9 -9
- data/Rakefile +15 -71
- data/bin/botcontrol +151 -146
- data/data/butler/dialogs/botcontrol.rb +8 -3
- data/data/butler/dialogs/create.rb +18 -18
- data/data/butler/dialogs/create_config.rb +8 -0
- data/data/butler/dialogs/en/create_config.yaml +2 -0
- data/data/butler/dialogs/en/list.yaml +2 -1
- data/data/butler/dialogs/info.rb +2 -2
- data/data/butler/dialogs/list.rb +13 -8
- data/data/butler/plugins/games/countdown.rb +41 -0
- data/data/butler/plugins/games/roll.rb +59 -0
- data/lib/access.rb +6 -107
- data/lib/access/admin.rb +3 -0
- data/lib/access/role.rb +37 -2
- data/lib/access/savable.rb +5 -0
- data/lib/access/user.rb +21 -2
- data/lib/access/yamlbase.rb +4 -0
- data/lib/butler.rb +4 -4
- data/lib/butler/bot.rb +13 -13
- data/lib/butler/irc/client.rb +10 -2
- data/lib/butler/irc/parser.rb +7 -2
- data/lib/butler/irc/parser/commands.rb +24 -7
- data/lib/butler/irc/parser/generic.rb +27 -315
- data/lib/butler/irc/parser/rfc2812.rb +328 -0
- data/lib/butler/irc/socket.rb +1 -1
- data/lib/butler/irc/user.rb +13 -0
- data/lib/butler/plugin.rb +1 -1
- data/lib/butler/plugin/configproxy.rb +4 -4
- data/lib/butler/plugins.rb +1 -1
- data/lib/butler/version.rb +1 -1
- data/lib/configuration.rb +22 -71
- data/lib/dialogline.rb +12 -0
- data/lib/event.rb +5 -2
- data/lib/installer.rb +52 -24
- data/lib/iterator.rb +17 -7
- data/lib/log.rb +32 -5
- data/lib/log/comfort.rb +33 -16
- data/lib/log/entry.rb +25 -5
- data/lib/log/fakeio.rb +1 -0
- data/lib/log/splitter.rb +6 -2
- data/lib/ostructfixed.rb +5 -0
- data/lib/ruby/exception/detailed.rb +3 -3
- data/lib/scheduler.rb +19 -4
- data/lib/scriptfile.rb +9 -2
- data/lib/string.rb +176 -0
- data/lib/string/ascii.rb +31 -0
- data/lib/string/mbencoded.rb +79 -0
- data/lib/string/sbencoded.rb +77 -0
- data/lib/string/utf8.rb +157 -0
- data/lib/templater.rb +68 -10
- data/lib/w3validator.rb +86 -0
- data/test/irc/serverlistings/test_rpl_hiddenhost.txt +60 -0
- data/test/test_access.rb +101 -0
- data/test/test_configuration.rb +63 -0
- metadata +19 -2
data/lib/dialogline.rb
CHANGED
@@ -12,6 +12,18 @@ begin
|
|
12
12
|
require 'readline'
|
13
13
|
rescue LoadError; warn "DialogLine starts without readline support." end
|
14
14
|
|
15
|
+
# == Indexing
|
16
|
+
# Author: Stefan Rusterholz
|
17
|
+
# Contact: apeiros@gmx.net>
|
18
|
+
# Version: 1.0.0
|
19
|
+
# Date: 2007-10-12
|
20
|
+
#
|
21
|
+
# == About
|
22
|
+
# Abstract command line dialogs with multilingualization.
|
23
|
+
#
|
24
|
+
# == Synopsis
|
25
|
+
#
|
26
|
+
#
|
15
27
|
class DialogLine
|
16
28
|
module VERSION
|
17
29
|
MAJOR = 0
|
data/lib/event.rb
CHANGED
@@ -114,7 +114,8 @@ class Event
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
-
|
117
|
+
# Don't use directly, use the creation methods.
|
118
|
+
def initialize(options={}, &block) # :nodoc:
|
118
119
|
@block = block
|
119
120
|
@start = options.delete(:start) || Time.now
|
120
121
|
@stop = options.delete(:stop)
|
@@ -128,6 +129,7 @@ class Event
|
|
128
129
|
@count = 0
|
129
130
|
end
|
130
131
|
|
132
|
+
# Invoke the event
|
131
133
|
def call(*args)
|
132
134
|
@block.call(*args)
|
133
135
|
end
|
@@ -264,7 +266,8 @@ class Event
|
|
264
266
|
def to_f
|
265
267
|
seconds_left.to_f
|
266
268
|
end
|
267
|
-
|
269
|
+
|
270
|
+
# Stop current thread until this event is finished.
|
268
271
|
def join
|
269
272
|
@wakeup << Thread.current
|
270
273
|
sleep unless finished?
|
data/lib/installer.rb
CHANGED
@@ -1,5 +1,29 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
1
9
|
require 'rbconfig'
|
2
10
|
|
11
|
+
|
12
|
+
|
13
|
+
# == Indexing
|
14
|
+
# Author: Stefan Rusterholz
|
15
|
+
# Contact: apeiros@gmx.net>
|
16
|
+
# Version: 1.0.0
|
17
|
+
# Date: 2007-10-12
|
18
|
+
#
|
19
|
+
# == About
|
20
|
+
# Tools to install stuff. At the moment that means mostly suggest
|
21
|
+
# where stuff might go.
|
22
|
+
#
|
23
|
+
# == Synopsis
|
24
|
+
# x = Iterator.new { |iter| [1,2,3].each { |e| iter.yield(e) } }
|
25
|
+
# p x.map { |e| e*2 }
|
26
|
+
#
|
3
27
|
class Installer
|
4
28
|
def initialize(app_name)
|
5
29
|
@app_name = app_name
|
@@ -13,43 +37,47 @@ class Installer
|
|
13
37
|
end
|
14
38
|
end
|
15
39
|
|
16
|
-
def
|
40
|
+
def home(user=nil)
|
41
|
+
File.expand_path("~#{user}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def user_data_suggestions(user=nil)
|
17
45
|
{
|
18
|
-
:osx => ["#{
|
19
|
-
:windows => ["#{
|
20
|
-
:nix => ["#{
|
46
|
+
:osx => ["#{home(user)}/Library/Application Support/#{@app_name}", "#{home(user)}/.#{@app_name}"],
|
47
|
+
:windows => ["#{home(user)}/.#{@app_name}"],
|
48
|
+
:nix => ["#{home(user)}/.#{@app_name}"],
|
21
49
|
}[os_class]
|
22
50
|
end
|
23
51
|
|
24
|
-
def user_config_suggestions
|
52
|
+
def user_config_suggestions(user=nil)
|
25
53
|
{
|
26
|
-
:osx => ["#{
|
27
|
-
:windows => ["#{
|
28
|
-
:nix => ["#{
|
54
|
+
:osx => ["#{home(user)}/Library/Preferences/#{@app_name}", "#{home(user)}/.#{@app_name}/etc"],
|
55
|
+
:windows => ["#{home(user)}/.#{@app_name}/etc"],
|
56
|
+
:nix => ["#{home(user)}/.#{@app_name}/etc"],
|
29
57
|
}[os_class]
|
30
58
|
end
|
31
59
|
|
32
|
-
def user_documents_suggestions
|
60
|
+
def user_documents_suggestions(user=nil)
|
33
61
|
{
|
34
|
-
:osx => ["#{
|
62
|
+
:osx => ["#{home(user)}/Documents"],
|
35
63
|
:windows => [
|
36
|
-
"#{
|
37
|
-
"#{
|
38
|
-
"#{
|
64
|
+
"#{home(user)}/My Documents",
|
65
|
+
"#{home(user)}/#{ENV['USER']}'s Documents",
|
66
|
+
"#{home(user)}/#{ENV['USER'].downcase}'s documents"
|
39
67
|
].select { |path| File.directory?(path) },
|
40
|
-
:nix => ["#{
|
68
|
+
:nix => ["#{home(user)}/"],
|
41
69
|
}[os_class]
|
42
70
|
end
|
43
71
|
|
44
|
-
def user_pid_suggestions
|
72
|
+
def user_pid_suggestions(user=nil)
|
45
73
|
{
|
46
|
-
:osx => ["#{
|
47
|
-
:windows => ["#{
|
48
|
-
:nix => ["#{
|
74
|
+
:osx => ["#{home(user)}/Library/Application Support/#{@app_name}/run", "#{home(user)}/.#{@app_name}/run"],
|
75
|
+
:windows => ["#{home(user)}/.#{@app_name}/run"],
|
76
|
+
:nix => ["#{home(user)}/.#{@app_name}/run"],
|
49
77
|
}[os_class]
|
50
78
|
end
|
51
79
|
|
52
|
-
def shared_data_suggestions
|
80
|
+
def shared_data_suggestions(user=nil)
|
53
81
|
{
|
54
82
|
:osx => ["/Library/Application Support/#{@app_name}", "#{Config::CONFIG['datadir']}/#{@app_name}"],
|
55
83
|
:windows => ["#{Config::CONFIG['datadir']}/#{@app_name}"],
|
@@ -57,19 +85,19 @@ class Installer
|
|
57
85
|
}[os_class]
|
58
86
|
end
|
59
87
|
|
60
|
-
def cli_binary_suggestions
|
88
|
+
def cli_binary_suggestions(user=nil)
|
61
89
|
{
|
62
90
|
:osx => [Config::CONFIG['bindir']],
|
63
91
|
:windows => [Config::CONFIG['bindir']],
|
64
|
-
:nix => [Config::CONFIG['bindir'], "#{
|
92
|
+
:nix => [Config::CONFIG['bindir'], "#{home(user)}/bin"],
|
65
93
|
}[os_class]
|
66
94
|
end
|
67
95
|
|
68
|
-
def gui_binary_suggestions
|
96
|
+
def gui_binary_suggestions(user=nil)
|
69
97
|
{
|
70
|
-
:osx => ["/Applications",
|
98
|
+
:osx => ["/Applications", home(user)+"/Applications"],
|
71
99
|
:windows => [Config::CONFIG['bindir']],
|
72
|
-
:nix => [Config::CONFIG['bindir'], "#{
|
100
|
+
:nix => [Config::CONFIG['bindir'], "#{home(user)}/bin"],
|
73
101
|
}[os_class]
|
74
102
|
end
|
75
103
|
end
|
data/lib/iterator.rb
CHANGED
@@ -6,10 +6,23 @@
|
|
6
6
|
|
7
7
|
|
8
8
|
|
9
|
+
# == Indexing
|
10
|
+
# Author: Stefan Rusterholz
|
11
|
+
# Contact: apeiros@gmx.net>
|
12
|
+
# Version: 1.0.0
|
13
|
+
# Date: 2007-10-12
|
14
|
+
#
|
15
|
+
# == About
|
16
|
+
# Similar like Enumarator, enables to create an Enumerable.
|
17
|
+
#
|
18
|
+
# == Synopsis
|
19
|
+
# x = Iterator.new { |iter| [1,2,3].each { |e| iter.yield(e) } }
|
20
|
+
# p x.map { |e| e*2 }
|
21
|
+
#
|
9
22
|
class Iterator
|
10
23
|
include Enumerable
|
11
24
|
|
12
|
-
class Iteration
|
25
|
+
class Iteration # :nodoc:
|
13
26
|
def initialize(&block)
|
14
27
|
@block = block
|
15
28
|
end
|
@@ -18,17 +31,14 @@ class Iterator
|
|
18
31
|
end
|
19
32
|
end
|
20
33
|
|
34
|
+
# Determine how to iterate, the block most contain the code that
|
35
|
+
# is executed to iterate.
|
21
36
|
def initialize(&iterator)
|
22
37
|
@iterator = iterator
|
23
38
|
end
|
24
39
|
|
40
|
+
# Will iterate as defined in Iterator::new
|
25
41
|
def each(&block)
|
26
42
|
@iterator.call(Iteration.new(&block))
|
27
43
|
end
|
28
44
|
end
|
29
|
-
|
30
|
-
if __FILE__ == $0 then
|
31
|
-
#Iterator.new { |iter| File.open(path) { |fh| while buf = fh.read(8192); iter.yield(buf); end } }
|
32
|
-
x = Iterator.new { |iter| [1,2,3].each { |e| iter.yield(e) } }
|
33
|
-
p x.map { |e| e*2 }
|
34
|
-
end
|
data/lib/log.rb
CHANGED
@@ -18,6 +18,24 @@
|
|
18
18
|
# $stdout.puts(exception) # same as Log::File#log(exception)
|
19
19
|
# end
|
20
20
|
#
|
21
|
+
# == Use-cases
|
22
|
+
# === Daemon
|
23
|
+
# Since a daemon should not output anything at all, the advice is to create a
|
24
|
+
# logger (Log::File e.g.) and assign a Log::Forward to each, $stdout and $stderr.
|
25
|
+
# It's suggested to use :info as the default level for $stdout and :warn for
|
26
|
+
# $stderr.
|
27
|
+
#
|
28
|
+
# === Application
|
29
|
+
# If you use Log with your application, you most likely want to log to a file.
|
30
|
+
# The advice for that is to simply assign a Log::File to $stderr, anything that
|
31
|
+
# prints to $stderr is now logged as :warn.
|
32
|
+
#
|
33
|
+
# === Library
|
34
|
+
# With a library you most likely just want Log::Comfort. It adds logging methods
|
35
|
+
# and convenience methods to your class. It uses @logger if set, else $stderr to
|
36
|
+
# puts a Log::Entry. That way your library has decent logging even if the
|
37
|
+
# employing app doesn't use a logger.
|
38
|
+
#
|
21
39
|
# == Notes
|
22
40
|
# require 'log/kernel' to get convenience methods in Kernel
|
23
41
|
# it isn't required via 'log' alone to avoid accidental method name clashes.
|
@@ -50,6 +68,8 @@ module Log
|
|
50
68
|
end
|
51
69
|
end
|
52
70
|
|
71
|
+
|
72
|
+
|
53
73
|
require 'log/comfort'
|
54
74
|
require 'log/converter'
|
55
75
|
require 'log/entry'
|
@@ -59,10 +79,17 @@ require 'log/file'
|
|
59
79
|
require 'log/nolog'
|
60
80
|
|
61
81
|
|
82
|
+
|
62
83
|
if __FILE__ == $0 then
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
Log::
|
84
|
+
begin
|
85
|
+
require 'stringio'
|
86
|
+
lf = StringIO.new
|
87
|
+
log = Log::File.new(lf)
|
88
|
+
$stderr = Log::Forward.new(log, :warn)
|
89
|
+
warn "foo"
|
90
|
+
p Log::Entry.deserialize(lf.string.split("\n").first)
|
91
|
+
puts "end"
|
92
|
+
rescue => e
|
93
|
+
puts e, *e.backtrace
|
94
|
+
end
|
68
95
|
end
|
data/lib/log/comfort.rb
CHANGED
@@ -22,52 +22,69 @@ require 'log/entry'
|
|
22
22
|
module Log
|
23
23
|
module Comfort
|
24
24
|
# where data is logged to
|
25
|
-
attr_accessor :
|
25
|
+
attr_accessor :logger
|
26
|
+
|
27
|
+
# Used for the origin-field in the log
|
28
|
+
alias log_origin class
|
26
29
|
|
27
30
|
# See Log::Message.new
|
28
31
|
# Module::log(*args) is simply: $stderr.puts(Log::Message.new(*args))
|
29
|
-
def log(text, severity=:info, *
|
30
|
-
(@
|
32
|
+
def log(text, severity=:info, origin=nil, data=nil, *flags)
|
33
|
+
(@logger || $stderr).puts(
|
34
|
+
Log::Entry.new(text.to_str, severity, origin||log_origin, data=nil, *flags)
|
35
|
+
)
|
31
36
|
end
|
32
37
|
|
33
38
|
# See Log::Entry.new to see what arguments are valid.
|
34
39
|
# Module::debug(text, *args) is the same as:
|
35
40
|
# $stderr.puts(Log::Message.new(text, :debug, *args))
|
36
|
-
def debug(text, *
|
37
|
-
(@
|
41
|
+
def debug(text, origin=nil, data=nil, *flags)
|
42
|
+
(@logger || $stderr).puts(
|
43
|
+
Log::Entry.new(text.to_str, :debug, origin||log_origin, data=nil, *flags)
|
44
|
+
)
|
38
45
|
end
|
39
|
-
|
46
|
+
|
40
47
|
# See Log::Entry.new to see what arguments are valid.
|
41
48
|
# Module::info(text, *args) is the same as:
|
42
49
|
# $stderr.puts(Log::Message.new(text, :info, *args))
|
43
|
-
def info(text, *
|
44
|
-
(@
|
50
|
+
def info(text, origin=nil, data=nil, *flags)
|
51
|
+
(@logger || $stderr).puts(
|
52
|
+
Log::Entry.new(text.to_str, :info, origin||log_origin, data=nil, *flags)
|
53
|
+
)
|
45
54
|
end
|
46
55
|
|
47
56
|
# See Log::Entry.new to see what arguments are valid.
|
48
57
|
# Module::warn(text, *args) is the same as:
|
49
58
|
# $stderr.puts(Log::Message.new(text, :warn, *args))
|
50
|
-
def warn(text, *
|
51
|
-
(@
|
59
|
+
def warn(text, origin=nil, data=nil, *flags)
|
60
|
+
(@logger || $stderr).puts(
|
61
|
+
Log::Entry.new(text.to_str, :warn, origin||log_origin, data=nil, *flags)
|
62
|
+
)
|
52
63
|
end
|
53
64
|
|
54
65
|
# See Log::Entry.new to see what arguments are valid.
|
55
66
|
# Module::error(text, *args) is the same as:
|
56
67
|
# $stderr.puts(Log::Message.new(text, :error, *args))
|
57
|
-
def error(text, *
|
58
|
-
(@
|
68
|
+
def error(text, origin=nil, data=nil, *flags)
|
69
|
+
(@logger || $stderr).puts(
|
70
|
+
Log::Entry.new(text.to_str, :error, origin||log_origin, data=nil, *flags)
|
71
|
+
)
|
59
72
|
end
|
60
73
|
|
61
74
|
# See Log::Entry.new to see what arguments are valid.
|
62
75
|
# Module::fail(text, *args) is the same as:
|
63
76
|
# $stderr.puts(Log::Message.new(text, :fail, *args))
|
64
|
-
def fail(text, *
|
65
|
-
(@
|
77
|
+
def fail(text, origin=nil, data=nil, *flags)
|
78
|
+
(@logger || $stderr).puts(
|
79
|
+
Log::Entry.new(text.to_str, :fail, origin||log_origin, data=nil, *flags)
|
80
|
+
)
|
66
81
|
end
|
67
82
|
|
68
|
-
#
|
83
|
+
# Exception is special cased, if @logger || $stderr responds to 'exception',
|
84
|
+
# the exception is just forwarded, else it uses puts and prints
|
85
|
+
# the exception and the backtrace
|
69
86
|
def exception(e)
|
70
|
-
log = @
|
87
|
+
log = @logger || $stderr
|
71
88
|
if log.respond_to?(:exception) then
|
72
89
|
log.exception(e)
|
73
90
|
else
|
data/lib/log/entry.rb
CHANGED
@@ -20,20 +20,31 @@ module Log
|
|
20
20
|
"%s#{RecordSeparator}" \
|
21
21
|
"%s#{RecordSeparator}" \
|
22
22
|
"%s#{RecordSeparator}" \
|
23
|
+
"%s#{RecordSeparator}" \
|
23
24
|
"%s"
|
24
25
|
|
25
26
|
class Entry
|
27
|
+
# the value used by #to_s if no format is given
|
28
|
+
DefaultFormat = "%{time:%FT%T} [%{severity}]: %{text} in %{origin}"
|
29
|
+
|
26
30
|
class <<self
|
27
31
|
attr_accessor :time_format
|
28
32
|
|
29
33
|
def deserialize(line)
|
30
|
-
time, severity, text, flagstr, data = line.chomp(RecordTerminator).split(RecordSeparator)
|
34
|
+
time, severity, origin, text, flagstr, data = line.chomp(RecordTerminator).split(RecordSeparator)
|
31
35
|
flags = {}
|
32
36
|
flagstr.split(UnitSeparator).each_cons(2) { |key, value|
|
33
37
|
flagstr[key] = value
|
34
38
|
}
|
35
39
|
severity = Integer(severity) rescue severity
|
36
|
-
new(
|
40
|
+
new(
|
41
|
+
text,
|
42
|
+
InvSeverity[severity],
|
43
|
+
Log.unescape(origin),
|
44
|
+
Marshal.load(Log.unescape(data)),
|
45
|
+
Time.at(time.to_i),
|
46
|
+
flags
|
47
|
+
)
|
37
48
|
end
|
38
49
|
|
39
50
|
def formatter_for(entity, &formatter)
|
@@ -48,6 +59,10 @@ module Log
|
|
48
59
|
entry.time.strftime(format || @time_format)
|
49
60
|
end
|
50
61
|
|
62
|
+
def format_origin(entry)
|
63
|
+
entry.origin.to_s
|
64
|
+
end
|
65
|
+
|
51
66
|
def format_severity(entry)
|
52
67
|
entry.severity.to_s
|
53
68
|
end
|
@@ -72,6 +87,7 @@ module Log
|
|
72
87
|
@formatter = {
|
73
88
|
"time" => method(:format_time),
|
74
89
|
"severity" => method(:format_severity),
|
90
|
+
"origin" => method(:format_origin),
|
75
91
|
"flags" => method(:format_flags),
|
76
92
|
"text" => method(:format_text), #Log.method(:escape),
|
77
93
|
}
|
@@ -79,12 +95,14 @@ module Log
|
|
79
95
|
|
80
96
|
attr_reader :time
|
81
97
|
attr_reader :severity
|
98
|
+
attr_reader :origin
|
82
99
|
attr_reader :text
|
83
100
|
attr_reader :flags
|
84
101
|
attr_reader :data
|
85
|
-
def initialize(text, severity=:info, data=nil, *flags)
|
102
|
+
def initialize(text, severity=:info, origin=nil, data=nil, *flags)
|
86
103
|
@time = flags.first.kind_of?(Time) ? flags.shift : Time.now
|
87
104
|
@severity = severity
|
105
|
+
@origin = origin.to_s
|
88
106
|
@text = text
|
89
107
|
@data = data
|
90
108
|
@flags = flags.last.kind_of?(Hash) ? flags.pop : {}
|
@@ -120,6 +138,7 @@ module Log
|
|
120
138
|
Serialized % [
|
121
139
|
@time,
|
122
140
|
Severity[@severity],
|
141
|
+
Log.escape(@origin),
|
123
142
|
Log.escape(@text),
|
124
143
|
@flags.map.join(UnitSeparator),
|
125
144
|
Log.escape(Marshal.dump(@data))
|
@@ -127,7 +146,7 @@ module Log
|
|
127
146
|
end
|
128
147
|
|
129
148
|
def to_s(format=nil)
|
130
|
-
format ||=
|
149
|
+
format ||= DefaultFormat
|
131
150
|
format.gsub(/%(%|\{[^}]+\})/) { |match|
|
132
151
|
if match == "%%" then
|
133
152
|
"%"
|
@@ -139,10 +158,11 @@ module Log
|
|
139
158
|
end
|
140
159
|
|
141
160
|
def inspect
|
142
|
-
"#<%s %s %s %s flags=%s data=%s>" % [
|
161
|
+
"#<%s %s %s %s %s flags=%s data=%s>" % [
|
143
162
|
self.class,
|
144
163
|
@time.strftime("%FT%T"),
|
145
164
|
@severity,
|
165
|
+
@origin,
|
146
166
|
@text.inspect,
|
147
167
|
@flags.inspect,
|
148
168
|
@data.inspect
|