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