zmb 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.2.0
data/bin/zmb CHANGED
@@ -6,11 +6,12 @@ require 'zmb'
6
6
  require 'optparse'
7
7
 
8
8
  class AdminUser
9
- attr_accessor :username, :userhosts
9
+ attr_accessor :username, :userhosts, :permissions
10
10
 
11
11
  def initialize
12
12
  @username = 'admin'
13
13
  @userhosts = []
14
+ @permissions = ['admin']
14
15
  end
15
16
 
16
17
  def admin?
@@ -27,10 +28,11 @@ class AdminUser
27
28
  end
28
29
 
29
30
  class Event
30
- attr_accessor :message
31
+ attr_accessor :message, :user
31
32
 
32
33
  def initialize(message)
33
34
  @message = message
35
+ @user = AdminUser.new
34
36
  end
35
37
 
36
38
  def message?
@@ -41,10 +43,6 @@ class Event
41
43
  true
42
44
  end
43
45
 
44
- def user
45
- AdminUser.new
46
- end
47
-
48
46
  def reply(msg)
49
47
  puts "> #{msg}"
50
48
  end
data/lib/zmb.rb CHANGED
@@ -14,13 +14,19 @@ require 'zmb/commands'
14
14
  require 'zmb/timer'
15
15
 
16
16
  class Zmb
17
- attr_accessor :instances, :plugin_manager, :settings
17
+ attr_accessor :instances, :plugin_manager, :settings_manager
18
+
19
+ def plugin
20
+ 'zmb'
21
+ end
18
22
 
19
23
  def initialize(config_dir)
24
+ @debug = true
25
+
20
26
  @plugin_manager = PluginManager.new
21
- @settings = Settings.new(config_dir)
27
+ @settings_manager = Settings.new(config_dir)
22
28
 
23
- @instances = {'core/zmb' => self}
29
+ @instances = {'zmb' => self}
24
30
  @sockets = Hash.new
25
31
 
26
32
  @minimum_timeout = 0.5 # Half a second
@@ -28,13 +34,13 @@ class Zmb
28
34
  @timers = Array.new
29
35
  timer_add(Timer.new(self, :save, 120.0, true)) # Save every 2 minutes
30
36
 
31
- @settings.get('core/zmb', 'plugin_sources', []).each{|source| @plugin_manager.add_plugin_source source}
37
+ @settings_manager.get('zmb', 'plugin_sources', []).each{|source| @plugin_manager.add_plugin_source source}
32
38
 
33
39
  if @plugin_manager.plugin_sources.empty? then
34
40
  @plugin_manager.add_plugin_source File.join(File.expand_path(File.dirname(File.dirname(__FILE__))), 'plugins')
35
41
  end
36
42
 
37
- @settings.get('core/zmb', 'plugin_instances', []).each{|instance| load instance}
43
+ @settings_manager.get('zmb', 'plugin_instances', []).each{|instance| load instance}
38
44
 
39
45
  @running = false
40
46
  end
@@ -43,24 +49,26 @@ class Zmb
43
49
  @running
44
50
  end
45
51
 
46
- def to_json(*a)
52
+ def settings
47
53
  {
48
54
  'plugin_sources' => @plugin_manager.plugin_sources,
49
55
  'plugin_instances' => @instances.keys,
50
- }.to_json(*a)
56
+ }
51
57
  end
52
58
 
53
59
  def save
54
- @instances.each{ |k,v| @settings.save(k, v) }
60
+ @instances.each{ |k,v| @settings_manager.save(k, v) }
55
61
  end
56
62
 
57
63
  def load(key)
58
64
  return true if @instances.has_key?(key)
59
65
 
60
- if p = @settings.get(key, 'plugin') then
66
+ if p = @settings_manager.get(key, 'plugin') then
61
67
  object = @plugin_manager.plugin(p)
62
68
  return false if not object
63
- @instances[key] = object.new(self, @settings.setting(key))
69
+ @instances[key] = object.new(self, @settings_manager.setting(key))
70
+ @instances[key].class.send(:define_method, :plugin) { p }
71
+ @instances[key].class.send(:define_method, :instance) { key }
64
72
  post! :plugin_loaded, key, @instances[key]
65
73
  true
66
74
  else
@@ -71,7 +79,7 @@ class Zmb
71
79
  def unload(key, tell=true)
72
80
  return false if not @instances.has_key?(key)
73
81
  instance = @instances.delete(key)
74
- @settings.save key, instance
82
+ @settings_manager.save key, instance
75
83
  socket_delete instance
76
84
  timer_delete instance
77
85
  instance.unloaded if instance.respond_to?('unloaded') and tell
@@ -172,38 +180,42 @@ class Zmb
172
180
  object = @plugin_manager.plugin plugin
173
181
  return false if not object
174
182
 
175
- settings = Hash.new
176
- settings['plugin'] = plugin
183
+ s = Hash.new
184
+ s['plugin'] = plugin
177
185
 
178
186
  if object.respond_to? 'wizard' then
179
187
  d = object.wizard
180
- d.each{ |k,v| settings[k] = v['default'] if v.has_key?('default') and v['default'] }
188
+ d.each{ |k,v| s[k] = v['default'] if v.has_key?('default') and v['default'] }
181
189
  end
182
190
 
183
- @settings.save instance, settings
191
+ @settings_manager.save instance, s
184
192
 
185
193
  true
186
194
  end
187
195
 
188
196
  def event(sender, e)
189
- post! :pre_event, sender, e
190
- post! :event, sender, e
197
+ puts e.line if @debug and e.respond_to?('line')
198
+
199
+ Thread.new do
200
+ post! :pre_event, sender, e
201
+ post! :event, sender, e
202
+ end
191
203
  end
192
204
 
193
205
  def commands
194
206
  {
195
- 'reload' => PermCommand.new('admin', self, :reload_command),
196
- 'unload' => PermCommand.new('admin', self, :unload_command),
197
- 'load' => PermCommand.new('admin', self, :load_command),
198
- 'save' => PermCommand.new('admin', self, :save_command, 0),
199
- 'loaded' => PermCommand.new('admin', self, :loaded_command, 0),
200
- 'setup' => PermCommand.new('admin', self, :setup_command, 2),
201
- 'set' => PermCommand.new('admin', self, :set_command, 3),
202
- 'get' => PermCommand.new('admin', self, :get_command, 2),
203
- 'clone' => PermCommand.new('admin', self, :clone_command, 2),
204
- 'reset' => PermCommand.new('admin', self, :reset_command),
205
- 'addsource' => PermCommand.new('admin', self, :addsource_command),
206
- 'refresh' => PermCommand.new('admin', self, :refresh_command),
207
+ 'reload' => [:reload_command, 1, { :permission => 'admin' }],
208
+ 'unload' => [:unload_command, 1, { :permission => 'admin' }],
209
+ 'load' => [:load_command, 1, { :permission => 'admin' }],
210
+ 'save' => [:save_command, 0, { :permission => 'admin' }],
211
+ 'loaded' => [:loaded_command, 0, { :permission => 'admin' }],
212
+ 'setup' => [:setup_command, 2, { :permission => 'admin' }],
213
+ 'set' => [:set_command, 3, { :permission => 'admin' }],
214
+ 'get' => [:get_command, 2, { :permission => 'admin' }],
215
+ 'clone' => [:clone_command, 2, { :permission => 'admin' }],
216
+ 'reset' => [:reset_command, 1, { :permission => 'admin' }],
217
+ 'addsource' => [:addource_command, 1, { :permission => 'admin' }],
218
+ 'refresh' => [:refresh_command, 1, { :permission => 'admin' }],
207
219
  }
208
220
  end
209
221
 
@@ -212,7 +224,7 @@ class Zmb
212
224
  sockets = Array.new
213
225
  @sockets.each{ |sock,delegate| sockets << sock if delegate == @instances[instance] }
214
226
  unload(instance, false)
215
- reloaded = @plugin_manager.reload_plugin(@settings.get(instance, 'plugin'))
227
+ reloaded = @plugin_manager.reload_plugin(@settings_manager.get(instance, 'plugin'))
216
228
  load(instance)
217
229
 
218
230
  sockets.each{ |socket| @sockets[socket] = @instances[instance] }
@@ -264,9 +276,9 @@ class Zmb
264
276
  end
265
277
 
266
278
  def set_command(e, instance, key, value)
267
- settings = @settings.setting(instance)
279
+ settings = @settings_manager.setting(instance)
268
280
  settings[key] = value
269
- @settings.save(instance, settings)
281
+ @settings_manager.save(instance, settings)
270
282
 
271
283
  if @instances.has_key?(instance) and @instances[instance].respond_to?('update') then
272
284
  @instances[instance].update(key, value)
@@ -276,7 +288,7 @@ class Zmb
276
288
  end
277
289
 
278
290
  def get_command(e, instance, key)
279
- if value = @settings.get(instance, key) then
291
+ if value = @settings_manager.get(instance, key) then
280
292
  "#{key} is #{value} for #{instance}"
281
293
  else
282
294
  "#{instance} or #{instance}/#{key} not found."
@@ -284,8 +296,8 @@ class Zmb
284
296
  end
285
297
 
286
298
  def clone_command(e, instance, new_instance)
287
- if (settings = @settings.setting(instance)) != {} then
288
- @settings.save(new_instance, settings)
299
+ if (settings = @settings_manager.setting(instance)) != {} then
300
+ @settings_manager.save(new_instance, settings)
289
301
  "The settings for #{instance} were copied to #{new_instance}"
290
302
  else
291
303
  "No settings for #{instance}"
@@ -293,7 +305,7 @@ class Zmb
293
305
  end
294
306
 
295
307
  def reset_command(e, instance)
296
- @settings.save(instance, {})
308
+ @settings_manager.save(instance, {})
297
309
  "Settings for #{instance} have been deleted."
298
310
  end
299
311
 
@@ -6,6 +6,8 @@ rescue LoadError
6
6
  end
7
7
 
8
8
  class Settings
9
+ attr_accessor :directory
10
+
9
11
  def initialize(directory)
10
12
  if not File.exist?(directory) then
11
13
  FileUtils.makedirs(directory)
@@ -40,9 +42,13 @@ class Settings
40
42
  end
41
43
  end
42
44
 
43
- def save(key, data)
45
+ def save(key, instance)
44
46
  f = File.open setting_path(key), 'w'
45
- f.write data.to_json
47
+ s = instance
48
+ s = {} if instance.class != Hash
49
+ s = instance.settings if instance.respond_to?('settings')
50
+ s['plugin'] = instance.plugin if instance.respond_to?('plugin')
51
+ f.write s.to_json
46
52
  f.close
47
53
  end
48
54
  end
@@ -3,17 +3,22 @@ class Timer
3
3
 
4
4
  attr_accessor :delegate
5
5
 
6
- def initialize(delegate, symbol, interval, repeat=false) # interval is in seconds (decimals accepted)
6
+ def initialize(delegate, symbol, interval, repeat=false, data=nil) # interval is in seconds (decimals accepted)
7
7
  @delegate = delegate
8
8
  @symbol = symbol
9
9
  @interval = interval
10
10
  @repeat = repeat
11
+ @data = data
11
12
 
12
13
  @fire_at = Time.now + interval
13
14
  end
14
15
 
15
16
  def fire(sender)
16
- @delegate.send @symbol
17
+ if @data then
18
+ @delegate.send @symbol, @data
19
+ else
20
+ @delegate.send @symbol
21
+ end
17
22
 
18
23
  if not @repeat
19
24
  sender.timer_delete(self) if sender.respond_to?('timer_delete')
@@ -0,0 +1,101 @@
1
+ require 'zmb/timer'
2
+
3
+ class Announce
4
+ def initialize(sender, s)
5
+ @delegate = sender
6
+ @announcements = Hash.new
7
+ @announcements = s['announcements'] if s.has_key?('announcements')
8
+ @autoindex = 1
9
+ @autoindex = s['autoindex'] if s.has_key?('autoindex')
10
+
11
+ @announcements.keys.each{ |id| timer(id) }
12
+ end
13
+
14
+ def settings
15
+ { 'announcements' => @announcements, 'autoindex' => @autoindex }
16
+ end
17
+
18
+ def add(instance, location, interval, message)
19
+ @announcements["#{@autoindex}"] = {
20
+ 'instance' => instance,
21
+ 'location' => location,
22
+ 'interval' => Integer(interval),
23
+ 'message' => message,
24
+ }
25
+
26
+ @autoindex += 1
27
+ @autoindex - 1
28
+ end
29
+
30
+ def exec(id)
31
+ if (a = @announcements.fetch("#{id}", false)) and (i = @delegate.instances.fetch(a['instance'], false)) then
32
+ i = @delegate.instances[a['instance']]
33
+ i.message(a['location'], a['message']) if i.respond_to?('message')
34
+ end
35
+ end
36
+
37
+ def timer(id)
38
+ a = @announcements["#{id}"]
39
+ a['timer'] = Timer.new(self, :exec, a['interval'], true, id)
40
+ @delegate.timer_add(a['timer'])
41
+ end
42
+
43
+ def delete(id)
44
+ if (a = @announcements.fetch("#{id}", false)) then
45
+ @delegate.timer_delete(a['timer']) if a.has_key?('timer')
46
+ @announcements.delete("#{id}")
47
+ true
48
+ else
49
+ false
50
+ end
51
+ end
52
+
53
+ def commands
54
+ {
55
+ 'announcements' => [:announcements, 0, {
56
+ :permission => 'admin',
57
+ :help => 'List all the id\'s for' }],
58
+ 'announcement' => [:announcement, 1, { :permission => 'admin' }],
59
+ 'announce' => [:announce, 4, {
60
+ :permission => 'admin',
61
+ :help => 'Add a announcement',
62
+ :usage => 'instance location interval message',
63
+ :example => 'efnet #zmb 600 Check github for the latest updates!' }],
64
+ 'announce-del' => [:announce_del, 1, { :permission => 'admin' }],
65
+ }
66
+ end
67
+
68
+ def announcements(e)
69
+ if @announcements.size == 0 then
70
+ 'no announcements'
71
+ else
72
+ @announcements.keys.join(', ')
73
+ end
74
+ end
75
+
76
+ def announcement(e, id)
77
+ if (a = @announcements.fetch(id, false)) then
78
+ "'#{a['message']}' every #{a['interval']} seconds in #{a['instance']}/#{a['location']}"
79
+ else
80
+ 'announcement not found'
81
+ end
82
+ end
83
+
84
+ def announce(e, *args)
85
+ "Announcement added \##{add(*args)}"
86
+ end
87
+
88
+ def announce_del(e, id)
89
+ if delete(id) then
90
+ "#{id} deleted"
91
+ else
92
+ "#{id} not found"
93
+ end
94
+ end
95
+ end
96
+
97
+ Plugin.define do
98
+ name 'announce'
99
+ description 'Send message to a channel automatically in a certain amount of time'
100
+ object Announce
101
+ end
@@ -0,0 +1,15 @@
1
+ class AutoRejoin
2
+ def initialize(sender, s); end
3
+
4
+ def event(sender, e)
5
+ if e.command == 'kick' then
6
+ e.delegate.write("JOIN #{e.channel}") if e.delegate.nick == e.name
7
+ end
8
+ end
9
+ end
10
+
11
+ Plugin.define do
12
+ name 'autorejoin'
13
+ description 'auto rejoin a irc channel once we have been kicked'
14
+ object AutoRejoin
15
+ end
@@ -45,13 +45,13 @@ end
45
45
  class Bank
46
46
  attr_accessor :accounts
47
47
 
48
- def initialize(sender, settings={})
49
- @accounts = settings['accounts'].map{ |acc| Account.create_settings(acc) } if settings.has_key?('accounts')
48
+ def initialize(sender, s={})
49
+ @accounts = s['accounts'].map{ |acc| Account.create_settings(acc) } if s.has_key?('accounts')
50
50
  @accounts = Array.new if not @accounts
51
51
  end
52
52
 
53
- def to_json(*a)
54
- { 'accounts' => @accounts, 'plugin' => 'bank' }.to_json(*a)
53
+ def settings
54
+ { 'accounts' => @accounts }
55
55
  end
56
56
 
57
57
  def create(username)
@@ -73,10 +73,12 @@ class Bank
73
73
  end
74
74
 
75
75
  def commands
76
- require 'zmb/commands'
77
76
  {
78
- 'balance' => AuthCommand.new(self, :balance, 0, 'See how much funds you have.'),
79
- 'transfer' => AuthCommand.new(self, :transfer, 2, 'Transfer funds to another account'),
77
+ 'balance' => [:balance, 0, { :help => 'See how much funds you have.' }],
78
+ 'transfer' => [:transfer, 2, {
79
+ :help => 'Transfer funds to another account',
80
+ :usage => 'user amount',
81
+ :example => 'zynox 20' }],
80
82
  }
81
83
  end
82
84
 
@@ -3,22 +3,18 @@ require 'zmb/commands'
3
3
  class Commands
4
4
  attr_accessor :cmds, :cc
5
5
 
6
- def initialize(sender, settings={})
6
+ def initialize(sender, s={})
7
7
  @delegate = sender
8
8
  @cmds = Hash.new
9
9
 
10
- @cc = settings['cc'] if settings.has_key?('cc')
10
+ @cc = s['cc'] if s.has_key?('cc')
11
11
  @cc = '!' if @cc == nil
12
12
 
13
- @cmds.merge!(commands)
14
-
15
- sender.post('commands').each do |command|
16
- @cmds.merge!(command)
17
- end
13
+ sender.instances.each{ |key, instance| plugin_loaded(key, instance) }
18
14
  end
19
15
 
20
- def to_json(*a)
21
- { 'cc' => @cc, 'plugin' => 'commands' }.to_json(*a)
16
+ def settings
17
+ { 'cc' => @cc }
22
18
  end
23
19
 
24
20
  def self.wizard
@@ -65,29 +61,70 @@ class Commands
65
61
  end
66
62
 
67
63
  cmd = args.delete_at(0)
68
-
69
- if @cmds.has_key?(cmd) then
70
- args << input if input
71
- input = @cmds[cmd].run(e, args)
72
- end
64
+ args << input if input
65
+ input = execute(cmd, e, args)
73
66
  end
74
67
 
75
68
  e.reply(input) if input
76
69
  end
77
70
 
71
+ def execute(cmd, e, args)
72
+ return if not @cmds.has_key?(cmd)
73
+
74
+ c = @cmds[cmd]
75
+
76
+ if c[2] == 0 then
77
+ args = Array.new
78
+ elsif args.size > c[2]
79
+ a = args.first c[2]-1 # Take one under amount of commands
80
+ a << args[c[2]-1..-1].join(' ')
81
+ args = a
82
+ end
83
+
84
+ # User permissions
85
+ if (kwargs = c.at(3)) and kwargs.has_key?(:permission) then
86
+ if not e.respond_to?('user')
87
+ return 'user module not loaded'
88
+ elsif kwargs[:permission] == 'authenticated' then
89
+ return 'permission denied' if not e.user.authenticated?
90
+ elsif not e.user.permission?(kwargs[:permission])
91
+ return 'permission denied'
92
+ end
93
+ end
94
+
95
+ begin
96
+ c[0].send(c[1], e, *args)
97
+ rescue ArgumentError
98
+ 'incorrect arguments'
99
+ rescue Exception
100
+ if e.respond_to?('user') and e.user.admin? and e.private? then
101
+ "#{$!.message}\n#{$!.inspect}\n#{$!.backtrace[0..2].join("\n")}"
102
+ else
103
+ 'command failed'
104
+ end
105
+ end
106
+ end
107
+
78
108
  def plugin_loaded(key, instance)
79
- @cmds.merge!(instance.commands) if instance.respond_to?('commands')
109
+ if instance.respond_to?('commands') then
110
+ instance.commands.each do |k,v|
111
+ v = [v] if v.class != Array
112
+ v.insert(0, instance)
113
+ v << 1 if v.size == 2 # add default command amount
114
+ @cmds[k] = v
115
+ end
116
+ end
80
117
  end
81
118
 
82
119
  def plugin_unloaded(key, instance)
83
- instance.commands.each{|command, cmd| @cmds.delete(command)} if instance.respond_to?('commands')
120
+ @cmds = @cmds.reject{ |k,v| v[0] == instance }
84
121
  end
85
122
 
86
123
  def split_seperators(data)
87
124
  if data.include?("\n") then
88
- data.split("\n")
125
+ data.split("\n").map{ |arg| arg.strip }
89
126
  elsif data.include?(',') then
90
- data.split(',')
127
+ data.split(',').map{ |arg| arg.strip }
91
128
  elsif data.include?(' ') then
92
129
  data.split(' ')
93
130
  else
@@ -97,31 +134,42 @@ class Commands
97
134
 
98
135
  def commands
99
136
  {
100
- 'help' => Command.new(self, :help),
101
- 'instance' => Command.new(self, :instance, 1, 'List all commands availible for a instance.'),
102
- 'cc' => PermCommand.new('admin', self, :control_command),
103
- 'eval' => PermCommand.new('admin', self, :evaluate),
104
- 'count' => Command.new(self, :count),
105
- 'grep' => Command.new(self, :grep, 2),
106
- 'not' => Command.new(self, :not_command, 2),
107
- 'tail' => Command.new(self, :tail),
108
- 'echo' => Command.new(self, :echo),
137
+ 'help' => :help,
138
+ 'instance' => [:instance_command, 1, { :help => 'List all commands availible for a instance.'}],
139
+ 'which' => [:which, 1, { :help => 'Find which plugin handles a command' }],
140
+ 'cc' => [:control_command, 1, { :permission => 'admin' }],
141
+ 'eval' => [:evaluate, 1, { :permission => 'admin' }],
142
+ 'count' => :count,
143
+ 'grep' => [:grep, 2],
144
+ 'not' => [:not_command, 2],
145
+ 'tail' => :tail,
146
+ 'echo' => :echo,
147
+ 'reverse' => :reverse,
109
148
  }
110
149
  end
111
150
 
112
151
  def help(e, command=nil)
113
152
  if command then
114
- if @cmds.has_key?(command) and @cmds[command].help? then
115
- "#{command}: #{@cmds[command].help}"
116
- else
153
+ h = []
154
+
155
+ if @cmds.has_key?(command) and (kwargs = @cmds[command].at(3)).respond_to?('has_key?') then
156
+ h << "#{command}: #{kwargs[:help]}" if kwargs.has_key?(:help)
157
+ h << "Usage: #{command} #{kwargs[:usage]}" if kwargs.has_key?(:usage)
158
+ h << "Example: #{command} #{kwargs[:example]}" if kwargs.has_key?(:example)
159
+ end
160
+
161
+ if h.size == 0 then
117
162
  'Command not found or no help availible for the command.'
163
+ else
164
+ h.join("\n")
118
165
  end
119
166
  else
120
- @cmds.keys.join(', ')
167
+ cmds = @cmds.reject{ |k,v| ((kwargs = v.at(3)) and kwargs.has_key?(:permission)) and not e.user.permission?(kwargs[:permission]) }
168
+ cmds.keys.join(', ')
121
169
  end
122
170
  end
123
171
 
124
- def instance(e, inst)
172
+ def instance_command(e, inst)
125
173
  if @delegate.instances.has_key?(inst) then
126
174
  if @delegate.instances[inst].respond_to?('commands') then
127
175
  @delegate.instances[inst].commands.keys.join(', ')
@@ -133,6 +181,19 @@ class Commands
133
181
  end
134
182
  end
135
183
 
184
+ def which(e, command)
185
+ if @cmds.has_key?(command) then
186
+ c = @cmds[command][0]
187
+ if c.respond_to?('instance') and c.plugin != c.instance then
188
+ "#{c.plugin} (#{c.instance})"
189
+ else
190
+ c.plugin
191
+ end
192
+ else
193
+ 'command not found'
194
+ end
195
+ end
196
+
136
197
  def control_command(e, cc=nil)
137
198
  if cc then
138
199
  @cc = cc
@@ -166,6 +227,10 @@ class Commands
166
227
  def echo(e, data)
167
228
  "#{data}"
168
229
  end
230
+
231
+ def reverse(e, data)
232
+ data.reverse
233
+ end
169
234
  end
170
235
 
171
236
  Plugin.define do