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/log/fakeio.rb
CHANGED
data/lib/log/splitter.rb
CHANGED
@@ -4,14 +4,18 @@ module Log
|
|
4
4
|
@receivers = receivers
|
5
5
|
end
|
6
6
|
|
7
|
-
def
|
7
|
+
def __delete__(receiver)
|
8
8
|
@receivers.delete(receiver)
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
11
|
+
def __add__(receiver)
|
12
12
|
@receivers << receiver
|
13
13
|
end
|
14
14
|
|
15
|
+
def each(&block)
|
16
|
+
@receivers.each(&block)
|
17
|
+
end
|
18
|
+
|
15
19
|
def method_missing(*a, &b)
|
16
20
|
@receivers.each { |r| r.__send__(*a, &b)
|
17
21
|
end
|
data/lib/ostructfixed.rb
CHANGED
@@ -11,15 +11,20 @@ require 'ostruct'
|
|
11
11
|
|
12
12
|
|
13
13
|
class OpenStruct
|
14
|
+
# Get the field by its name, faster than #send and with
|
15
|
+
# dynamic field names more convenient.
|
14
16
|
def [](field)
|
15
17
|
@table[field.to_sym]
|
16
18
|
end
|
17
19
|
|
20
|
+
# Assign a field by its name, faster than #send and with
|
21
|
+
# dynamic fields more convenient
|
18
22
|
def []=(field,value)
|
19
23
|
new_ostruct_member(field)
|
20
24
|
@table[field.to_sym] = value
|
21
25
|
end
|
22
26
|
|
27
|
+
# Get a copy of the table with the members.
|
23
28
|
def to_hash
|
24
29
|
@table.dup
|
25
30
|
end
|
@@ -10,15 +10,15 @@ class Exception
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def prepend(string)
|
13
|
-
@prepend.unshift string
|
13
|
+
(@prepend ||= []).unshift string
|
14
14
|
end
|
15
15
|
|
16
16
|
def append(string)
|
17
|
-
@append.unshift string
|
17
|
+
(@append ||= []).unshift string
|
18
18
|
end
|
19
19
|
|
20
20
|
def to_s
|
21
|
-
(@prepend+[super]
|
21
|
+
((@prepend||[])+[super]+(@append||[])).join(' ')
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
data/lib/scheduler.rb
CHANGED
@@ -13,6 +13,15 @@ require 'log/comfort'
|
|
13
13
|
|
14
14
|
|
15
15
|
|
16
|
+
# == Indexing
|
17
|
+
# Author: Stefan Rusterholz
|
18
|
+
# Contact: apeiros@gmx.net>
|
19
|
+
# Version: 1.0.0
|
20
|
+
# Date: 2007-10-12
|
21
|
+
#
|
22
|
+
# == About
|
23
|
+
# Provides methods to execute scheduled Events
|
24
|
+
#
|
16
25
|
# == Synopsis
|
17
26
|
# scheduler = Dispatcher::Scheduled.new(*events)
|
18
27
|
# scheduler.every 5.minutes do puts "Another 5 minutes wasted!" end
|
@@ -22,12 +31,13 @@ require 'log/comfort'
|
|
22
31
|
# scheduler.resume
|
23
32
|
# scheduler.terminate # important, else you'll have a forever sleeping thread left
|
24
33
|
#
|
25
|
-
# == Description
|
26
|
-
# Provides methods to execute scheduled Events
|
27
|
-
#
|
28
34
|
class Scheduler
|
29
35
|
include Log::Comfort
|
36
|
+
include Enumerable
|
30
37
|
|
38
|
+
# ==== Arguments
|
39
|
+
# * delete_finished: if true, it will remove elements from #events once they're done
|
40
|
+
# * *events: events to schedule
|
31
41
|
def initialize(delete_finished=true, *events)
|
32
42
|
events.each { |event| event.scheduler = self }
|
33
43
|
@terminated = false
|
@@ -42,7 +52,8 @@ class Scheduler
|
|
42
52
|
add(*events)
|
43
53
|
end
|
44
54
|
|
45
|
-
|
55
|
+
# Handles the events and invokes them when due.
|
56
|
+
def sleeper # :nodoc:
|
46
57
|
while instr = @instructions.shift
|
47
58
|
@alarm.kill # just overkill...
|
48
59
|
|
@@ -86,9 +97,13 @@ class Scheduler
|
|
86
97
|
def restart
|
87
98
|
end
|
88
99
|
|
100
|
+
# not yet implemented
|
89
101
|
def terminate
|
90
102
|
end
|
91
103
|
|
104
|
+
# Tell the Scheduler to update its scheduling regardings this event
|
105
|
+
# This should not be necessary if your event is properly linked to the
|
106
|
+
# scheduler (the event is supposed to do that by itself).
|
92
107
|
def reschedule(event)
|
93
108
|
@mutex.synchronize {
|
94
109
|
#first = @events.first
|
data/lib/scriptfile.rb
CHANGED
@@ -35,10 +35,12 @@ class ScriptFile < File
|
|
35
35
|
(IO.instance_methods(false)+File.instance_methods(false) - %w[see tell pos truncate]).each { |m| private m }
|
36
36
|
|
37
37
|
class <<self
|
38
|
+
# Like File::read, slurp the whole files DATA at once.
|
38
39
|
def read(file)
|
39
40
|
open(file) { |fh| fh.read }
|
40
41
|
end
|
41
42
|
|
43
|
+
# Like File::read, slurp the whole files DATA at once.
|
42
44
|
def open(file, mode="r")
|
43
45
|
fh = new(file, mode)
|
44
46
|
block_given? ? yield(fh) : fh
|
@@ -46,7 +48,8 @@ class ScriptFile < File
|
|
46
48
|
fh.close if fh and block_given?
|
47
49
|
end
|
48
50
|
end
|
49
|
-
|
51
|
+
|
52
|
+
# Same as File::new
|
50
53
|
def initialize(file, mode="r")
|
51
54
|
super(file, "r+") # "r#{'+' unless mode=='r'}"
|
52
55
|
@offset = 0
|
@@ -76,23 +79,27 @@ class ScriptFile < File
|
|
76
79
|
|
77
80
|
alias file_seek seek unless method_defined?(:file_seek)
|
78
81
|
private :file_seek
|
79
|
-
|
82
|
+
|
83
|
+
# See File#seek
|
80
84
|
def seek(offset=0, type=IO::SEEK_SET)
|
81
85
|
raise ArgumentError, "negative seeks currently not supported" if offset < 0
|
82
86
|
raise ArgumentError, "no type but IO::SEEK_SET supported at the moment" if type != IO::SEEK_SET
|
83
87
|
super(offset+@offset)
|
84
88
|
end
|
85
89
|
|
90
|
+
# See File#truncate
|
86
91
|
def truncate(bytes=0)
|
87
92
|
super(bytes+@offset)
|
88
93
|
seek(bytes)
|
89
94
|
0
|
90
95
|
end
|
91
96
|
|
97
|
+
# See File#tell
|
92
98
|
def tell
|
93
99
|
super-@offset
|
94
100
|
end
|
95
101
|
|
102
|
+
# See File#pos
|
96
103
|
def pos
|
97
104
|
super-@offset
|
98
105
|
end
|
data/lib/string.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
$KCODE ||= 'utf8'
|
2
|
+
|
3
|
+
# Extend String class to integrate nicely
|
4
|
+
require 'string/ascii'
|
5
|
+
|
6
|
+
# SBEncodedString handles singlebyte encoded strings with collation
|
7
|
+
require 'string/sbencoded'
|
8
|
+
|
9
|
+
# MBEncodedString handles multibyte encoded strings with collation
|
10
|
+
require 'string/mbencoded'
|
11
|
+
|
12
|
+
# UTF8String is slightly faster than EncodedString
|
13
|
+
require 'string/utf8'
|
14
|
+
|
15
|
+
# Iconv to allow charset conversions
|
16
|
+
begin
|
17
|
+
require 'iconv'
|
18
|
+
rescue LoadError
|
19
|
+
warn "Iconv is not installed on this machinge, charset translation of Strings won't work."
|
20
|
+
class Iconv
|
21
|
+
def initialize(*args)
|
22
|
+
end
|
23
|
+
def iconv(text)
|
24
|
+
text
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Unicode to have universal uppercase/lowercase/capitalize methods
|
30
|
+
begin
|
31
|
+
require 'unicode'
|
32
|
+
rescue LoadError
|
33
|
+
warn "Unicode is not installed on this machine, universal upper-, lowercase, capitalize and length method won't work."
|
34
|
+
module Unicode
|
35
|
+
class <<self
|
36
|
+
def upcase(string)
|
37
|
+
string.ascii_upcase
|
38
|
+
end
|
39
|
+
def downcase(string)
|
40
|
+
string.ascii_downcase
|
41
|
+
end
|
42
|
+
def capitalize(string)
|
43
|
+
string.ascii_capitalize
|
44
|
+
end
|
45
|
+
def strcmp(string1, string2)
|
46
|
+
string1.ascii_cmp(string2)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module Kernel
|
53
|
+
# Short for creating a utf8 string
|
54
|
+
# u"Stringy"
|
55
|
+
def u(string, collation=nil)
|
56
|
+
String::UTF8.new(string, String::UTF8::UTF8, collation)
|
57
|
+
end
|
58
|
+
private :u
|
59
|
+
end
|
60
|
+
|
61
|
+
def Iconv.charsets
|
62
|
+
`Iconv -l`.split("\n").map { |l| l.split(" ") }
|
63
|
+
end
|
64
|
+
|
65
|
+
class String
|
66
|
+
def self.encodings
|
67
|
+
Encodings.keys
|
68
|
+
end
|
69
|
+
|
70
|
+
# encoding -> singlebyte
|
71
|
+
Encodings = {
|
72
|
+
'binary' => ASCII, # only with collation, else it's ::String
|
73
|
+
'ascii' => ASCII,
|
74
|
+
'utf-8' => UTF8,
|
75
|
+
|
76
|
+
'iso-8859-1' => SBEncoded,
|
77
|
+
'iso-8859-2' => SBEncoded,
|
78
|
+
'iso-8859-3' => SBEncoded,
|
79
|
+
'iso-8859-4' => SBEncoded,
|
80
|
+
'iso-8859-5' => SBEncoded,
|
81
|
+
'iso-8859-6' => SBEncoded,
|
82
|
+
'iso-8859-7' => SBEncoded,
|
83
|
+
'iso-8859-8' => SBEncoded,
|
84
|
+
'iso-8859-9' => SBEncoded,
|
85
|
+
'iso-8859-10' => SBEncoded,
|
86
|
+
'iso-8859-11' => SBEncoded,
|
87
|
+
'iso-8859-12' => SBEncoded,
|
88
|
+
'iso-8859-13' => SBEncoded,
|
89
|
+
'iso-8859-14' => SBEncoded,
|
90
|
+
'iso-8859-15' => SBEncoded,
|
91
|
+
'macroman' => SBEncoded,
|
92
|
+
'macroman' => SBEncoded,
|
93
|
+
|
94
|
+
'utf-8-mac' => MBEncoded,
|
95
|
+
'utf-16' => MBEncoded,
|
96
|
+
'utf-16be' => MBEncoded,
|
97
|
+
'utf-16le' => MBEncoded,
|
98
|
+
'utf-32' => MBEncoded,
|
99
|
+
'utf-32be' => MBEncoded,
|
100
|
+
'utf-32le' => MBEncoded,
|
101
|
+
'ucs-2be' => MBEncoded,
|
102
|
+
'ucs-2le' => MBEncoded,
|
103
|
+
'ucs-4be' => MBEncoded,
|
104
|
+
'ucs-4le' => MBEncoded,
|
105
|
+
'ucs-2-internal' => MBEncoded,
|
106
|
+
'ucs-2-swapped' => MBEncoded,
|
107
|
+
'ucs-4-internal' => MBEncoded,
|
108
|
+
'ucs-4-swapped' => MBEncoded,
|
109
|
+
}
|
110
|
+
|
111
|
+
# UNICODE_ is used for strip/strip!
|
112
|
+
UNICODE_WHITESPACE = [
|
113
|
+
(0x0009..0x000D).to_a, # White_Space # Cc [5] <control-0009>..<control-000D>
|
114
|
+
0x0020, # White_Space # Zs SPACE
|
115
|
+
0x0085, # White_Space # Cc <control-0085>
|
116
|
+
0x00A0, # White_Space # Zs NO-BREAK SPACE
|
117
|
+
0x1680, # White_Space # Zs OGHAM SPACE MARK
|
118
|
+
0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
|
119
|
+
(0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
|
120
|
+
0x2028, # White_Space # Zl LINE SEPARATOR
|
121
|
+
0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
|
122
|
+
0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
|
123
|
+
0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
|
124
|
+
0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
|
125
|
+
].flatten #:nodoc:
|
126
|
+
UNICODE_LEADERS_AND_TRAILERS = UNICODE_WHITESPACE + [65279] # ZERO-WIDTH NO-BREAK SPACE aka BOM #:nodoc:
|
127
|
+
UNICODE_LT_PAT = /#{UNICODE_LEADERS_AND_TRAILERS.pack("U*").gsub(/.(?=.)/u, '\0|')}/u
|
128
|
+
UNICODE_T_PAT = /#{UNICODE_LT_PAT}+$/
|
129
|
+
UNICODE_L_PAT = /^#{UNICODE_LT_PAT}+/
|
130
|
+
|
131
|
+
Binary = 'binary'.freeze
|
132
|
+
|
133
|
+
alias ascii_upcase upcase
|
134
|
+
alias ascii_downcase downcase
|
135
|
+
alias ascii_capitalize capitalize
|
136
|
+
alias byte_cmp <=>
|
137
|
+
alias byte_insert insert
|
138
|
+
alias byte_reverse reverse
|
139
|
+
alias byte_slice slice
|
140
|
+
alias byte_index index
|
141
|
+
alias byte_rindex rindex
|
142
|
+
alias jlength length
|
143
|
+
alias jcount count
|
144
|
+
|
145
|
+
def encoding
|
146
|
+
Binary
|
147
|
+
end
|
148
|
+
|
149
|
+
def collation
|
150
|
+
Binary
|
151
|
+
end
|
152
|
+
|
153
|
+
def to_s(encoding=nil, collation=nil)
|
154
|
+
encoding = nil if encoding == Binary
|
155
|
+
collation = nil if collation == Binary
|
156
|
+
return String.new(self) unless (encoding || collation)
|
157
|
+
data = if encoding == self.encoding || self.encoding == Binary || self.encoding == ASCII::ASCII then
|
158
|
+
self
|
159
|
+
else
|
160
|
+
Iconv.iconv(encoding, self.encoding, self).first
|
161
|
+
end
|
162
|
+
Encodings[encoding].new(data, encoding, collation)
|
163
|
+
end
|
164
|
+
|
165
|
+
def ascii(collation=nil)
|
166
|
+
String::ASCII.new(self, String::ASCII::ASCII, collation)
|
167
|
+
end
|
168
|
+
|
169
|
+
def utf8(collation=nil)
|
170
|
+
String::UTF8.new(self, String::UTF8::UTF8, collation)
|
171
|
+
end
|
172
|
+
|
173
|
+
def binary
|
174
|
+
String.new(self)
|
175
|
+
end
|
176
|
+
end
|
data/lib/string/ascii.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'string'
|
2
|
+
|
3
|
+
# deal with ascii-strings
|
4
|
+
class String::ASCII < String
|
5
|
+
ASCII = 'ascii'.freeze
|
6
|
+
Collation = 'ascii'.freeze
|
7
|
+
|
8
|
+
def self.new(string, encoding, collation=nil)
|
9
|
+
unless encoding == String::Binary or encoding == String::ASCII::ASCII then
|
10
|
+
raise "Encoding must be 'ascii' or 'binary'"
|
11
|
+
end
|
12
|
+
unless collation.nil? or collation == Collation then
|
13
|
+
raise "Collation must be 'ascii' or nil"
|
14
|
+
end
|
15
|
+
obj = allocate
|
16
|
+
obj.send(:initialize, string)
|
17
|
+
obj
|
18
|
+
end
|
19
|
+
|
20
|
+
def encoding
|
21
|
+
String::ASCII::ASCII
|
22
|
+
end
|
23
|
+
|
24
|
+
def collation
|
25
|
+
Collation
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
"#{encoding}(#{collation||'none'}):#{super}"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'string'
|
2
|
+
|
3
|
+
class String::MBEncoded < String
|
4
|
+
def self.new(string, encoding, collation=nil)
|
5
|
+
raise "No encoding given" unless encoding
|
6
|
+
if encoding == 'utf-8' then
|
7
|
+
String::UTF8.new(string, collation)
|
8
|
+
else
|
9
|
+
obj = allocate
|
10
|
+
obj.send(:initialize, string, encoding, collation)
|
11
|
+
obj
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :encoding
|
16
|
+
|
17
|
+
attr_reader :collation
|
18
|
+
|
19
|
+
def initialize(string, encoding, collation=nil)
|
20
|
+
super(string)
|
21
|
+
@encoding = encoding.freeze
|
22
|
+
@collation = collation.freeze
|
23
|
+
end
|
24
|
+
|
25
|
+
def <=>(other)
|
26
|
+
unless other.collation == @collation
|
27
|
+
raise ArgumentError, "Collations don't match: #{@collation} <=> #{other.collation}"
|
28
|
+
end
|
29
|
+
unless other.encoding == @encoding
|
30
|
+
raise ArgumentError.new("Encodings don't match: #{@encoding} <=> #{other.encoding}")
|
31
|
+
end
|
32
|
+
self.utf8(@collation) <=> other.utf8(@collation)
|
33
|
+
end
|
34
|
+
|
35
|
+
# all methods delegated to utf8-ified strings (and if necessary it's reconversion)
|
36
|
+
def length; utf8.length; end
|
37
|
+
def upcase; utf8.upcase.to_s(@encoding, @collation); end
|
38
|
+
def downcase; utf8.downcase.to_s(@encoding, @collation); end
|
39
|
+
def capitalize; utf8.capitalize.to_s(@encoding, @collation); end
|
40
|
+
def chop; utf8.chop.to_s(@encoding, @collation); end
|
41
|
+
def index(*a); utf8.index(*a); end
|
42
|
+
def rindex(*a); utf8.rindex(*a); end
|
43
|
+
def strip; utf8.strip.to_s(@encoding, @collation); end
|
44
|
+
def lstrip; utf8.lstrip.to_s(@encoding, @collation); end
|
45
|
+
def rstrip; utf8.rstrip.to_s(@encoding, @collation); end
|
46
|
+
def [](*a); utf8[*a].to_s(@encoding, @collation); end
|
47
|
+
alias slice []
|
48
|
+
def []=(*args)
|
49
|
+
value = args.pop
|
50
|
+
a = self.utf8
|
51
|
+
a[*args] = value
|
52
|
+
self.replace(a.to_s(@encoding, @collation))
|
53
|
+
end
|
54
|
+
|
55
|
+
# ! variants, using self.replace
|
56
|
+
def upcase!; replace(upcase); end
|
57
|
+
def downcase!; replace(downcase); end
|
58
|
+
def capitalize!; replace(capitalize); end
|
59
|
+
def lstrip!; replace(lstrip); end
|
60
|
+
def rstrip!; replace(rstrip); end
|
61
|
+
def strip!; replace(strip); end
|
62
|
+
def chop!; replace(chop); end
|
63
|
+
|
64
|
+
def dup
|
65
|
+
String::MBEncoded.new(self, @encoding, @collation)
|
66
|
+
end
|
67
|
+
|
68
|
+
def utf8(collation=nil)
|
69
|
+
String::UTF8.new(
|
70
|
+
Iconv.iconv(String::UTF8::UTF8, @encoding, self).first,
|
71
|
+
String::UTF8::UTF8,
|
72
|
+
collation || @collation
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
def inspect
|
77
|
+
"#{encoding}(#{collation||'none'}):#{super}"
|
78
|
+
end
|
79
|
+
end
|