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/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
|