butler 1.8.2 → 1.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/Rakefile +1 -1
  2. data/bin/botcontrol +1 -1
  3. data/data/butler/dialogs/create_config.rb +2 -2
  4. data/data/butler/dialogs/quickcreate.rb +6 -4
  5. data/data/butler/dialogs/uninstall.rb +4 -3
  6. data/data/butler/plugins/core/access.rb +10 -10
  7. data/data/butler/plugins/dev/bleakhouse.rb +19 -8
  8. data/data/butler/plugins/operator/deop.rb +10 -1
  9. data/data/butler/plugins/operator/devoice.rb +9 -0
  10. data/data/butler/plugins/operator/limit.rb +12 -0
  11. data/data/butler/plugins/operator/op.rb +9 -0
  12. data/data/butler/plugins/operator/voice.rb +10 -0
  13. data/data/butler/plugins/util/calculator.rb +11 -0
  14. data/data/butler/services/org.rubyforge.butler/calculator/1/calculator.rb +68 -0
  15. data/data/butler/services/org.rubyforge.butler/log/1/service.rb +198 -0
  16. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/acknowledge.yaml +8 -0
  17. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/gratitude.yaml +3 -0
  18. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/hello.yaml +6 -0
  19. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/ignorance.yaml +7 -0
  20. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/ignorance_about.yaml +3 -0
  21. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/insult.yaml +3 -0
  22. data/data/butler/services/org.rubyforge.butler/strings/1/data/en/rejection.yaml +12 -0
  23. data/data/butler/services/org.rubyforge.butler/strings/1/service.rb +50 -0
  24. data/lib/access.rb +6 -3
  25. data/lib/access/privilege.rb +9 -78
  26. data/lib/access/privilegelist.rb +75 -0
  27. data/lib/access/role.rb +14 -94
  28. data/lib/access/role/base.rb +40 -0
  29. data/lib/access/rolelist.rb +99 -0
  30. data/lib/access/savable.rb +6 -3
  31. data/lib/access/user.rb +21 -19
  32. data/lib/access/version.rb +17 -0
  33. data/lib/access/yamlbase.rb +64 -48
  34. data/lib/butler.rb +1 -0
  35. data/lib/butler/bot.rb +8 -2
  36. data/lib/butler/control.rb +3 -1
  37. data/lib/butler/initialvalues.rb +1 -1
  38. data/lib/butler/irc/client.rb +6 -0
  39. data/lib/butler/irc/message.rb +14 -9
  40. data/lib/butler/irc/parser.rb +8 -5
  41. data/lib/butler/irc/parser/generic.rb +33 -1
  42. data/lib/butler/irc/parser/rfc2812.rb +5 -2
  43. data/lib/butler/plugin.rb +22 -2
  44. data/lib/butler/plugins.rb +2 -7
  45. data/lib/butler/service.rb +73 -0
  46. data/lib/butler/services.rb +65 -0
  47. data/lib/butler/version.rb +1 -1
  48. data/lib/ruby/array/random.rb +17 -0
  49. data/lib/ruby/string/camelcase.rb +14 -0
  50. data/test/test_access.rb +164 -59
  51. data/test/test_access/privilege/banners.statistics.yaml +3 -0
  52. data/test/test_access/privilege/banners.yaml +3 -0
  53. data/test/test_access/privilege/news.create.yaml +3 -0
  54. data/test/test_access/privilege/news.delete.yaml +3 -0
  55. data/test/test_access/privilege/news.edit.yaml +3 -0
  56. data/test/test_access/privilege/news.read.yaml +3 -0
  57. data/test/test_access/privilege/news.yaml +3 -0
  58. data/test/test_access/privilege/paid_content.yaml +3 -0
  59. data/test/test_access/privilege/statistics.ftp.yaml +3 -0
  60. data/test/test_access/privilege/statistics.web.yaml +3 -0
  61. data/test/test_access/privilege/statistics.yaml +3 -0
  62. data/test/test_access/role/chiefeditor.yaml +7 -0
  63. data/test/test_access/role/editor.yaml +9 -0
  64. data/test/test_access/user/test.yaml +12 -0
  65. metadata +51 -5
  66. data/data/butler/plugins/core/user.rb +0 -166
  67. data/data/butler/plugins/dev/onhandlers.rb +0 -93
  68. data/data/butler/plugins/service/log.rb +0 -183
data/lib/butler.rb CHANGED
@@ -164,6 +164,7 @@ class Butler
164
164
  conf[k] = v
165
165
  }
166
166
  FileUtils.cp_r(path.plugin_repository, bot_path)
167
+ FileUtils.cp_r(path.service_repository, bot_path)
167
168
  end
168
169
 
169
170
  # deletes a bot
data/lib/butler/bot.rb CHANGED
@@ -12,6 +12,7 @@ require 'butler/debuglog'
12
12
  require 'configuration'
13
13
  require 'log'
14
14
  require 'butler/plugins'
15
+ require 'butler/services'
15
16
  require 'butler/session'
16
17
  require 'ruby/string/arguments'
17
18
  require 'ruby/string/post_arguments'
@@ -33,6 +34,7 @@ class Butler
33
34
  attr_reader :path
34
35
  attr_reader :plugins
35
36
  attr_reader :scheduler
37
+ attr_reader :services
36
38
 
37
39
  # FIXME, raise if selftest fails
38
40
  def initialize(path, name, opts={})
@@ -47,6 +49,7 @@ class Butler
47
49
  :lib => @base+'/lib',
48
50
  :log => @base+'/log',
49
51
  :plugins => @base+'/plugins',
52
+ :services => @base+'/services',
50
53
  :strings => @base+'/strings'
51
54
  )
52
55
  $LOAD_PATH.unshift(@path.lib)
@@ -74,7 +77,9 @@ class Butler
74
77
 
75
78
  # { lang => { trigger => SortedSet[ *commands ] } }
76
79
  @commands = {}
77
- @plugins = Plugins.new(self, @base+'/plugins')
80
+ @services = Services.new(self, @path.services)
81
+ @services.load_all
82
+ @plugins = Plugins.new(self, @path.plugins)
78
83
 
79
84
  if $DEBUG then
80
85
  @irc.extend DebugLog
@@ -117,7 +122,7 @@ class Butler
117
122
  }
118
123
  break if command.abort_invocations?
119
124
  else
120
- info("#{message.from} (#{message.from.access.id}) had no authorization for 'plugin/#{command.plugin.base}'")
125
+ info("#{message.from} (#{message.from.access.oid}) had no authorization for 'plugin/#{command.plugin.base}'")
121
126
  end
122
127
  end
123
128
  }
@@ -150,6 +155,7 @@ class Butler
150
155
  def login
151
156
  @access.default_user = @access["default_user"]
152
157
  @access.default_user.login
158
+ p @access.default_user
153
159
 
154
160
  nick = @config["connections/main/nick"]
155
161
  pass = @config["connections/main/password"]
@@ -27,6 +27,7 @@ class Butler
27
27
  :config => Gem.datadir("butler")+'/config.yaml', # REPLACE: :config => %CONFIG_DIR%+'/config.yaml',
28
28
  :dialogs => Gem.datadir("butler")+'/dialogs', # REPLACE: :dialogs => %DIALOGS_DIR%,
29
29
  :plugins => Gem.datadir("butler")+'/plugins', # REPLACE: :plugins => %PLUGINS_DIR%,
30
+ :services => Gem.datadir("butler")+'/services', # REPLACE: :services => %SERVICES_DIR%,
30
31
  :strings => Gem.datadir("butler")+'/strings', # REPLACE: :strings => %STRINGS_DIR%,
31
32
  :man => Gem.datadir("butler")+'/man1', # DELETE
32
33
  })
@@ -78,7 +79,8 @@ class Butler
78
79
  def butler_path(user=nil)
79
80
  path = @config.users[user(user)]
80
81
  user_config = OpenStruct.new(YAML.load_file(path))
81
- user_config.plugin_repository = @path.plugins
82
+ user_config.plugin_repository = @path.plugins
83
+ user_config.service_repository = @path.services
82
84
  user_config
83
85
  end
84
86
 
@@ -15,7 +15,7 @@ class Butler
15
15
  BOTPATH/access/user
16
16
  BOTPATH/log
17
17
  BOTPATH/plugins
18
- BOTPATH/strings
18
+ BOTPATH/services
19
19
  ].map { |e| [e, 0755] }
20
20
  # first one is the base-directory
21
21
  ConfigurationStructure = %w[
@@ -120,6 +120,12 @@ class Butler
120
120
  @myself = @users.myself
121
121
 
122
122
  subscribe(:PING, 100) { |listener, message| @irc.pong(message.pong) }
123
+ subscribe(:ISUPPORT) { |listener, message|
124
+ # @parser. ...
125
+ }
126
+ subscribe(:RPL_IDENTIFY_MSG) { |listener, message|
127
+ @parser.msg_identify = true
128
+ }
123
129
  end
124
130
 
125
131
  # Load an additional command-set for @parser
@@ -46,15 +46,16 @@ class Butler
46
46
 
47
47
  #specific data
48
48
  @fields = Hash.new { |h,k| raise IndexError, "No member '#{k}' available." }.merge({
49
- :raw => @raw,
50
- :prefix => @prefix,
51
- :command => @command,
52
- :params => @params,
53
- :symbol => @symbol,
54
- :from => nil,
55
- :for => nil,
56
- :channel => nil,
57
- :text => nil,
49
+ :raw => @raw,
50
+ :prefix => @prefix,
51
+ :command => @command,
52
+ :params => @params,
53
+ :symbol => @symbol,
54
+ :from => nil,
55
+ :for => nil,
56
+ :channel => nil,
57
+ :text => nil,
58
+ :identified => nil,
58
59
  })
59
60
  end
60
61
 
@@ -77,6 +78,10 @@ class Butler
77
78
  end
78
79
  end
79
80
 
81
+ def identified?
82
+ @fields[:identified]
83
+ end
84
+
80
85
  def from
81
86
  @fields[:from]
82
87
  end
@@ -63,14 +63,17 @@ class Butler
63
63
  attr_reader :users
64
64
  attr_reader :channels
65
65
  attr_reader :commands
66
+
67
+ attr_accessor :msg_identify
66
68
 
67
69
  def initialize(client, users, channels, *command_sets)
68
- @client = client
69
- @users = users
70
- @channels = channels
71
- @commands = Commands.new(*command_sets)
70
+ @client = client
71
+ @users = users
72
+ @channels = channels
73
+ @commands = Commands.new(*command_sets)
74
+ @msg_identify = false
72
75
  end
73
-
76
+
74
77
  # parses an incomming message and returns a Message object from which you
75
78
  # can easily access parsed data.
76
79
  # Expects the newlines to be already chomped off.
@@ -9,6 +9,33 @@
9
9
  # A list with all common, but non-rfc2812 IRC-Commands and their parsing
10
10
  # instructions.
11
11
 
12
+ #
13
+ alter("005", :ISUPPORT) { |message, parser|
14
+ hash = {
15
+ "CHANNELLEN" => 50,
16
+ "NICKLEN" => 8,
17
+ }
18
+ message.params.sub(/\s+:.*?$/, '').split(/ /).each { |support|
19
+ name, value = support.split(/=/,2)
20
+ hash[name] = case value
21
+ when nil
22
+ true
23
+ when "NICKLEN"
24
+ value.to_i
25
+ when "PREFIX"
26
+ modes, prefixes = value[1..-1].split(/\)/, 2)
27
+ value = {}
28
+ modes.split(//).zip(prefixes.split(//)) { |k,v| value[k] = v }
29
+ value
30
+ when "MAXCHANNELS"
31
+ value.to_i
32
+ else value
33
+ end
34
+ }
35
+ message.create_member(:support, hash)
36
+ p hash
37
+ }
38
+
12
39
  # Seen:
13
40
  # - ConferenceRoom (irc.bluewin.ch)
14
41
  add("007", :UNK_007)
@@ -26,7 +53,12 @@ add("266", :RPL_GLOBALUSERS)
26
53
 
27
54
  # Seen:
28
55
  # - hyperion-1.0.2b (irc.freenode.net)
29
- add("290", :RPL_IDENTIFY_MSG)
56
+ # States what kind of messages will be identified
57
+ add("290", :RPL_IDENTIFY_MSG) { |message, parser|
58
+ types = {}
59
+ message.params.scan(/\S+/).each { |k| types[k] = true }
60
+ message.create_member(:types, types)
61
+ }
30
62
 
31
63
  # Seen:
32
64
  # - ConferenceRoom (irc.bluewin.ch), sent if a nick is registered
@@ -79,6 +79,9 @@ add("part", :PART, /^([^\x00\x07\x10\x0D\x20,:]+)(?: :(.*))?/, [:channel,
79
79
  add("ping", :PING, /:(.*)/, [:pong])
80
80
  add("pong", :PONG)
81
81
  add("privmsg", :PRIVMSG, /(\S+) :(.*)/, [:for, :text]) { |message, parser|
82
+ if parser.msg_identify && message.text.slice!(/^[+-]/) == '+' then
83
+ message.alter_member(:identified, true)
84
+ end
82
85
  if message.channel then
83
86
  message.create_member(:realm, :public)
84
87
  message.create_method(:private?) { false }
@@ -104,8 +107,8 @@ add("topic", :TOPIC, /(\S+) :(.*)/, [:channel, :text])
104
107
  add("001", :RPL_WELCOME)
105
108
  add("002", :RPL_YOURHOST)
106
109
  add("003", :RPL_CREATED)
107
- add("004", :RPL_MYINFO)
108
- add("005", :RPL_ISUPPORT)
110
+ add("004", :RPL_MYINFO, /(\S+) (\S+) (\S+) (\S+)/, [:servername, :version, :user_modes, :channel_modes])
111
+ add("005", :RPL_BOUNCE)
109
112
 
110
113
 
111
114
 
data/lib/butler/plugin.rb CHANGED
@@ -82,12 +82,28 @@ class Butler
82
82
  info("Loaded plugin '#{@base}' (#{self})")
83
83
  end
84
84
 
85
- def create_templates(tmpl, name)
85
+ # a class instance variable for the plugin
86
+ # defines attr_readers for it
87
+ def plugin_attribute(*names)
88
+ (class <<self; self; end).instance_eval {
89
+ attr_reader(*names)
90
+ }
91
+ end
92
+
93
+ # a class instance variable for the plugin,
94
+ # defines attr_accessors for it
95
+ def plugin_accessor(*names)
96
+ (class <<self; self; end).instance_eval {
97
+ attr_accessor(*names)
98
+ }
99
+ end
100
+
101
+ def create_templates(tmpl, name) # :nodoc:
86
102
  tmpl.each { |key,value|
87
103
  begin
88
104
  tmpl[key] = Templater.new(value)
89
105
  rescue Exception => e
90
- e.extend Exception::Detail
106
+ e.extend Exception::Detailed
91
107
  e.prepend "Could not map #{key} in #{@base} (#{name})."
92
108
  exception(e)
93
109
  end
@@ -209,6 +225,10 @@ class Butler
209
225
  end
210
226
  end
211
227
 
228
+ def service(name)
229
+ @butler.services[name]
230
+ end
231
+
212
232
  def unknown(n=-1)
213
233
  "Unknown argument #{@message.arguments[n]} for #{@message.arguments[0...n].join(' ')}"
214
234
  end
@@ -12,16 +12,10 @@ require 'butler/dialog'
12
12
  require 'iterator'
13
13
  require 'log/comfort'
14
14
  require 'ruby/exception/detailed'
15
+ require 'ruby/string/camelcase'
15
16
 
16
17
 
17
18
 
18
- class String
19
- # CamelCase a string, e.g. "foo_bar" becomes "FooBar"
20
- def camelcase
21
- scan(/[^_]+/).map { |s| s.capitalize }.join("")
22
- end
23
- end
24
-
25
19
  class Butler
26
20
  # Plugins manages the plugins in Butler
27
21
  # It uses
@@ -38,6 +32,7 @@ class Butler
38
32
  @plugins = {}
39
33
  @constants = {}
40
34
  @butler = butler
35
+ @logger = butler.logger
41
36
  end
42
37
 
43
38
  # returns a list with first-level groups
@@ -0,0 +1,73 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'log/comfort'
10
+ require 'ostructfixed'
11
+
12
+
13
+
14
+ class Butler
15
+ module Service
16
+ include Log::Comfort
17
+
18
+ attr_reader :butler
19
+ attr_reader :message
20
+ attr_reader :name
21
+ attr_reader :rdn
22
+ attr_reader :version
23
+
24
+ # this method is called to initialize the plugin-class,
25
+ # do not override
26
+ def load_service(butler, path, rdn, name, version) # :nodoc:
27
+ @butler = butler
28
+ @path = OpenStruct.new(
29
+ :base => path,
30
+ :fixtures => path+'/fixtures.rb'
31
+ )
32
+ @name = name
33
+ @rdn = rdn
34
+ @version = version
35
+ @listener = []
36
+ load(@path.fixtures) if File.exist?(@path.fixtures)
37
+ end
38
+
39
+ def register(object)
40
+ @butler.services.register(self, object)
41
+ end
42
+
43
+ def every(*args, &block)
44
+ @butler.scheduler.every(*args, &block)
45
+ end
46
+
47
+ def at(*args, &block)
48
+ @butler.scheduler.at(*args, &block)
49
+ end
50
+
51
+ def timed(*args, &block)
52
+ @butler.scheduler.timed(*args, &block)
53
+ end
54
+
55
+ def subscribe(*args, &block)
56
+ listener = @butler.subscribe(*args, &block)
57
+ @listener << listener
58
+ listener
59
+ end
60
+
61
+ def unload_plugin
62
+ info("Unloading plugin '#{@base}' (#{self})")
63
+ @commands.each { |command| @butler.delete_command(command) }
64
+ @listener.each { |listener| listener.unsubscribe }
65
+ end
66
+
67
+ def on_load(*args); end
68
+ def on_login(*args); end
69
+ def on_disconnect(*args); end
70
+ def on_quit(*args); end
71
+ def on_unload(*args); end
72
+ end
73
+ end
@@ -0,0 +1,65 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'butler/service'
10
+ require 'log/comfort'
11
+ require 'ruby/exception/detailed'
12
+ require 'ruby/string/camelcase'
13
+
14
+
15
+
16
+ class Butler
17
+ # Plugins manages the plugins in Butler
18
+ # It uses
19
+ class Services
20
+ include Log::Comfort
21
+
22
+ def initialize(butler, services_dir)
23
+ @butler = butler
24
+ @logger = butler.logger
25
+ @dir = File.expand_path(services_dir).freeze
26
+ raise ArgumentError, "#{@dir} is not a directory" unless File.directory?(@dir)
27
+ @services = {}
28
+ end
29
+
30
+ def load_all
31
+ Dir.glob("#{@dir}/*/*/*/service.rb") { |file|
32
+ load(file)
33
+ }
34
+ end
35
+
36
+ def load(file)
37
+ rdn, name, version = file.match(%r{([^/]+)/([^/]+)/([^/]+)/service\.rb$}).captures
38
+ path = File.dirname(file)
39
+
40
+ begin
41
+ constant = "%s_%08X" % [name.camelcase, rand(0xffffffff)]
42
+ end while Butler::Plugins.const_defined?(constant)
43
+ service = Butler::Services.const_set(constant, Module.new)
44
+ service.extend(Butler::Service)
45
+ service.logger = @butler.logger
46
+ begin
47
+ service.load_service(@butler, path, rdn, name, version)
48
+ service.class_eval(File.read(file), file)
49
+ service.on_load
50
+ rescue Exception => e
51
+ e.extend Exception::Detailed
52
+ e.prepend "Loading service #{rdn}.#{name} #{version} failed."
53
+ exception(e)
54
+ end
55
+ end
56
+
57
+ def register(service, obj)
58
+ @services[service.name] = obj
59
+ end
60
+
61
+ def [](name)
62
+ @services[name]
63
+ end
64
+ end
65
+ end
@@ -10,7 +10,7 @@ class Butler #:nodoc:
10
10
  module VERSION #:nodoc:
11
11
  MAJOR = 1
12
12
  MINOR = 8
13
- TINY = 2
13
+ TINY = 3
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY].join('.')
16
16
  end