butler 1.8.0 → 1.8.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/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
|