butler 1.8.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/CHANGELOG +40 -0
  2. data/README +9 -9
  3. data/Rakefile +15 -71
  4. data/bin/botcontrol +151 -146
  5. data/data/butler/dialogs/botcontrol.rb +8 -3
  6. data/data/butler/dialogs/create.rb +18 -18
  7. data/data/butler/dialogs/create_config.rb +8 -0
  8. data/data/butler/dialogs/en/create_config.yaml +2 -0
  9. data/data/butler/dialogs/en/list.yaml +2 -1
  10. data/data/butler/dialogs/info.rb +2 -2
  11. data/data/butler/dialogs/list.rb +13 -8
  12. data/data/butler/plugins/games/countdown.rb +41 -0
  13. data/data/butler/plugins/games/roll.rb +59 -0
  14. data/lib/access.rb +6 -107
  15. data/lib/access/admin.rb +3 -0
  16. data/lib/access/role.rb +37 -2
  17. data/lib/access/savable.rb +5 -0
  18. data/lib/access/user.rb +21 -2
  19. data/lib/access/yamlbase.rb +4 -0
  20. data/lib/butler.rb +4 -4
  21. data/lib/butler/bot.rb +13 -13
  22. data/lib/butler/irc/client.rb +10 -2
  23. data/lib/butler/irc/parser.rb +7 -2
  24. data/lib/butler/irc/parser/commands.rb +24 -7
  25. data/lib/butler/irc/parser/generic.rb +27 -315
  26. data/lib/butler/irc/parser/rfc2812.rb +328 -0
  27. data/lib/butler/irc/socket.rb +1 -1
  28. data/lib/butler/irc/user.rb +13 -0
  29. data/lib/butler/plugin.rb +1 -1
  30. data/lib/butler/plugin/configproxy.rb +4 -4
  31. data/lib/butler/plugins.rb +1 -1
  32. data/lib/butler/version.rb +1 -1
  33. data/lib/configuration.rb +22 -71
  34. data/lib/dialogline.rb +12 -0
  35. data/lib/event.rb +5 -2
  36. data/lib/installer.rb +52 -24
  37. data/lib/iterator.rb +17 -7
  38. data/lib/log.rb +32 -5
  39. data/lib/log/comfort.rb +33 -16
  40. data/lib/log/entry.rb +25 -5
  41. data/lib/log/fakeio.rb +1 -0
  42. data/lib/log/splitter.rb +6 -2
  43. data/lib/ostructfixed.rb +5 -0
  44. data/lib/ruby/exception/detailed.rb +3 -3
  45. data/lib/scheduler.rb +19 -4
  46. data/lib/scriptfile.rb +9 -2
  47. data/lib/string.rb +176 -0
  48. data/lib/string/ascii.rb +31 -0
  49. data/lib/string/mbencoded.rb +79 -0
  50. data/lib/string/sbencoded.rb +77 -0
  51. data/lib/string/utf8.rb +157 -0
  52. data/lib/templater.rb +68 -10
  53. data/lib/w3validator.rb +86 -0
  54. data/test/irc/serverlistings/test_rpl_hiddenhost.txt +60 -0
  55. data/test/test_access.rb +101 -0
  56. data/test/test_configuration.rb +63 -0
  57. metadata +19 -2
@@ -7,6 +7,7 @@
7
7
 
8
8
 
9
9
  module Log
10
+
10
11
  # == Description
11
12
  # Fakes most IO methods and forwards the converted data to #process
12
13
  #
@@ -4,14 +4,18 @@ module Log
4
4
  @receivers = receivers
5
5
  end
6
6
 
7
- def delete(receiver)
7
+ def __delete__(receiver)
8
8
  @receivers.delete(receiver)
9
9
  end
10
10
 
11
- def add(receiver)
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
@@ -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]+@append).join(' ')
21
+ ((@prepend||[])+[super]+(@append||[])).join(' ')
22
22
  end
23
23
  end
24
24
  end
@@ -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
- def sleeper
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
@@ -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
@@ -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
@@ -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