zmb 0.1.3 → 0.2.0

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