zmb 0.1.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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.markdown +39 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/bin/zmb +188 -0
- data/lib/zmb.rb +296 -0
- data/lib/zmb/commands.rb +63 -0
- data/lib/zmb/event.rb +15 -0
- data/lib/zmb/plugin.rb +110 -0
- data/lib/zmb/settings.rb +48 -0
- data/lib/zmb/timer.rb +28 -0
- data/plugins/bank.rb +101 -0
- data/plugins/commands.rb +119 -0
- data/plugins/irc.rb +230 -0
- data/plugins/quote.rb +100 -0
- data/plugins/relay.rb +70 -0
- data/plugins/users.rb +280 -0
- data/test/helper.rb +10 -0
- data/test/test_zmb.rb +7 -0
- data/zmb.gemspec +71 -0
- metadata +97 -0
data/lib/zmb/commands.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
class Command
|
2
|
+
attr_accessor :delegate, :signal, :help
|
3
|
+
|
4
|
+
def initialize(delegate, signal, commands=1, help=nil)
|
5
|
+
@delegate = delegate
|
6
|
+
@signal = signal
|
7
|
+
@help = help
|
8
|
+
@commands = commands
|
9
|
+
end
|
10
|
+
|
11
|
+
def help?
|
12
|
+
help != nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def run(e, args)
|
16
|
+
if @commands == 0 then
|
17
|
+
args = Array.new
|
18
|
+
elsif args.size > @commands
|
19
|
+
a = args.first @commands-1 # Take one under amount of commands
|
20
|
+
a << args[@commands-1..-1].join(' ')
|
21
|
+
args = a
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
@delegate.send(@signal, e, *args)
|
26
|
+
rescue ArgumentError
|
27
|
+
'incorrect arguments'
|
28
|
+
rescue Exception
|
29
|
+
if e.respond_to?('user') and e.user.respond_to?('admin?') and e.user.admin? and e.private? then
|
30
|
+
"#{$!.message}\n#{$!.inspect}"
|
31
|
+
else
|
32
|
+
"command failed"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class PermCommand < Command
|
39
|
+
attr_accessor :perm
|
40
|
+
|
41
|
+
def initialize(perm, *args)
|
42
|
+
super(*args)
|
43
|
+
@perm = perm
|
44
|
+
end
|
45
|
+
|
46
|
+
def run(e, args)
|
47
|
+
if e.respond_to?('user') and e.user.permission?(@perm) then
|
48
|
+
super(e, args)
|
49
|
+
else
|
50
|
+
'permission denied'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class AuthCommand < Command
|
56
|
+
def run(e, args)
|
57
|
+
if e.respond_to?('user') and e.user.authenticated? then
|
58
|
+
super(e, args)
|
59
|
+
else
|
60
|
+
'permission denied'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/zmb/event.rb
ADDED
data/lib/zmb/plugin.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
class PluginManager
|
2
|
+
attr_accessor :plugin_sources, :plugins
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@plugins = Array.new
|
6
|
+
@plugin_sources = Array.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def plugin(name)
|
10
|
+
plugin = @plugins.find{|plugin| plugin.name == name}
|
11
|
+
|
12
|
+
if plugin then
|
13
|
+
plugin.object
|
14
|
+
else
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_plugin_source(file)
|
20
|
+
begin
|
21
|
+
definition = instance_eval(File.read(file))
|
22
|
+
definition.definitition_file = File.expand_path(file)
|
23
|
+
@plugins << definition
|
24
|
+
rescue Exception
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_plugin_source(directory)
|
30
|
+
@plugin_sources << directory
|
31
|
+
|
32
|
+
definition_files = Dir[
|
33
|
+
File.join(File.expand_path(directory), "*.rb"),
|
34
|
+
File.join(File.expand_path(directory), "*", "plugin.rb")
|
35
|
+
]
|
36
|
+
|
37
|
+
definition_files.map do |file|
|
38
|
+
begin
|
39
|
+
load_plugin_source(file)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def refresh_plugin_sources
|
45
|
+
sources = @plugin_sources
|
46
|
+
@plugin_sources = nil
|
47
|
+
@plugins = nil
|
48
|
+
|
49
|
+
sources.each{|directory| add_plugin_source directory}
|
50
|
+
end
|
51
|
+
|
52
|
+
def reload_plugin(name)
|
53
|
+
plugin = @plugins.find{|plugin| plugin.name == name}
|
54
|
+
|
55
|
+
if plugin then
|
56
|
+
@plugins.delete(plugin)
|
57
|
+
reloaded = load_plugin_source plugin.definitition_file
|
58
|
+
@plugins << plugin if not reloaded
|
59
|
+
reloaded
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class PluginBuilder
|
65
|
+
attr_accessor :plugin
|
66
|
+
|
67
|
+
def initialize(&block)
|
68
|
+
@plugin = Plugin.new
|
69
|
+
@block = block
|
70
|
+
end
|
71
|
+
|
72
|
+
def build
|
73
|
+
instance_eval(&@block)
|
74
|
+
end
|
75
|
+
|
76
|
+
def name(value)
|
77
|
+
@plugin.name = value
|
78
|
+
end
|
79
|
+
|
80
|
+
def description(value)
|
81
|
+
@plugin.description = value
|
82
|
+
end
|
83
|
+
|
84
|
+
def object(value)
|
85
|
+
@plugin.object = value
|
86
|
+
end
|
87
|
+
|
88
|
+
def multi_instances(value)
|
89
|
+
@plugin.multi_instances = value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
class Plugin
|
95
|
+
attr_accessor :name, :description, :object, :definitition_file, :multi_instances
|
96
|
+
|
97
|
+
def self.define(&block)
|
98
|
+
builder = PluginBuilder.new(&block)
|
99
|
+
builder.build
|
100
|
+
builder.plugin
|
101
|
+
end
|
102
|
+
|
103
|
+
def multi_instances?
|
104
|
+
if @multi_instances then
|
105
|
+
@multi_instances
|
106
|
+
else
|
107
|
+
false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/zmb/settings.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
begin
|
2
|
+
require 'json'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
gem 'json'
|
6
|
+
end
|
7
|
+
|
8
|
+
class Settings
|
9
|
+
def initialize(directory)
|
10
|
+
if not File.exist?(directory) then
|
11
|
+
FileUtils.makedirs(directory)
|
12
|
+
end
|
13
|
+
|
14
|
+
if not File.directory?(directory) and not File.owned?(directory) then
|
15
|
+
raise
|
16
|
+
end
|
17
|
+
|
18
|
+
@directory = directory
|
19
|
+
end
|
20
|
+
|
21
|
+
def setting_path(key)
|
22
|
+
File.join(@directory, key.gsub('/', '_') + '.json')
|
23
|
+
end
|
24
|
+
|
25
|
+
def setting(key)
|
26
|
+
begin
|
27
|
+
JSON.parse(File.read(setting_path(key)))
|
28
|
+
rescue
|
29
|
+
{}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(object, name, default=nil)
|
34
|
+
s = setting(object)
|
35
|
+
|
36
|
+
if s.respond_to?('has_key?') and s.has_key?(name) then
|
37
|
+
s[name]
|
38
|
+
else
|
39
|
+
default
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def save(key, data)
|
44
|
+
f = File.open setting_path(key), 'w'
|
45
|
+
f.write data.to_json
|
46
|
+
f.close
|
47
|
+
end
|
48
|
+
end
|
data/lib/zmb/timer.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Timer
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
attr_accessor :delegate
|
5
|
+
|
6
|
+
def initialize(delegate, symbol, interval, repeat=false) # interval is in seconds (decimals accepted)
|
7
|
+
@delegate = delegate
|
8
|
+
@symbol = symbol
|
9
|
+
@interval = interval
|
10
|
+
@repeat = repeat
|
11
|
+
|
12
|
+
@fire_at = Time.now + interval
|
13
|
+
end
|
14
|
+
|
15
|
+
def fire(sender)
|
16
|
+
@delegate.send @symbol
|
17
|
+
|
18
|
+
if not @repeat
|
19
|
+
sender.timer_delete(self) if sender.respond_to?('timer_delete')
|
20
|
+
else
|
21
|
+
@fire_at = Time.now + @interval
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def timeout
|
26
|
+
@fire_at - Time.now
|
27
|
+
end
|
28
|
+
end
|
data/plugins/bank.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
class Account
|
2
|
+
attr_accessor :username, :balance, :log
|
3
|
+
|
4
|
+
def initialize(username, balance=0)
|
5
|
+
@username = username
|
6
|
+
@balance = balance
|
7
|
+
@log = Array.new
|
8
|
+
log 'Opening account'
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.create_settings(settings)
|
12
|
+
require 'time'
|
13
|
+
account = new(settings['username'], settings['balance'])
|
14
|
+
account.log = settings['log']
|
15
|
+
account
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_json(*a)
|
19
|
+
{ 'username' => @username, 'balance' => @balance, 'log' => @log }.to_json(*a)
|
20
|
+
end
|
21
|
+
|
22
|
+
def log(message)
|
23
|
+
@log << { 'message' => message, 'balance' => @balance, 'time' => Time.now }
|
24
|
+
end
|
25
|
+
|
26
|
+
def funds?(amount)
|
27
|
+
@balance >= amount
|
28
|
+
end
|
29
|
+
|
30
|
+
def transaction(amount, message=nil)
|
31
|
+
@balance += amount
|
32
|
+
log message if message
|
33
|
+
end
|
34
|
+
|
35
|
+
def transfer(account, amount)
|
36
|
+
transaction(amount*-1, "#{amount} transfered to #{account.username}")
|
37
|
+
account.transaction(amount, "#{amount} transfered from #{@username}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Event
|
42
|
+
attr_accessor :bank
|
43
|
+
end
|
44
|
+
|
45
|
+
class Bank
|
46
|
+
attr_accessor :accounts
|
47
|
+
|
48
|
+
def initialize(sender, settings={})
|
49
|
+
@accounts = settings['accounts'].map{ |acc| Account.create_settings(acc) } if settings.has_key?('accounts')
|
50
|
+
@accounts = Array.new if not @accounts
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_json(*a)
|
54
|
+
{ 'accounts' => @accounts, 'plugin' => 'bank' }.to_json(*a)
|
55
|
+
end
|
56
|
+
|
57
|
+
def create(username)
|
58
|
+
account = Account.new(username)
|
59
|
+
@accounts << account
|
60
|
+
account
|
61
|
+
end
|
62
|
+
|
63
|
+
def account!(username)
|
64
|
+
@accounts.find{|account| account.username == username}
|
65
|
+
end
|
66
|
+
|
67
|
+
def account(username)
|
68
|
+
account!(username) or create(username)
|
69
|
+
end
|
70
|
+
|
71
|
+
def pre_event(sender, e)
|
72
|
+
e.bank = account(e.user.username) if e.respond_to?('user') and e.user.respond_to?('username')
|
73
|
+
end
|
74
|
+
|
75
|
+
def commands
|
76
|
+
require 'zmb/commands'
|
77
|
+
{
|
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'),
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def balance(e)
|
84
|
+
"Your balance is #{e.bank.balance}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def transfer(e, username, amount)
|
88
|
+
amount = Integer(amount)
|
89
|
+
return "#{e.user.username} attempted theft against #{username}" if amount < 0
|
90
|
+
return 'funds not availible' if not e.bank.funds?(amount)
|
91
|
+
return "#{username} doesn't appear to have a bank account, they need to open one with the balance command" if not r = account!(username)
|
92
|
+
e.bank.transfer(r, amount)
|
93
|
+
"#{amount} transfered to #{username}'s account"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
Plugin.define do
|
98
|
+
name "bank"
|
99
|
+
description "virtual bank/money system"
|
100
|
+
object Bank
|
101
|
+
end
|
data/plugins/commands.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'zmb/commands'
|
2
|
+
|
3
|
+
class Commands
|
4
|
+
attr_accessor :cmds, :cc
|
5
|
+
|
6
|
+
def initialize(sender, settings={})
|
7
|
+
@cmds = Hash.new
|
8
|
+
|
9
|
+
@cc = settings['cc'] if settings.has_key?('cc')
|
10
|
+
@cc = '!' if @cc == nil
|
11
|
+
|
12
|
+
@cmds.merge!(commands)
|
13
|
+
|
14
|
+
sender.post('commands').each do |command|
|
15
|
+
@cmds.merge!(command)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_json(*a)
|
20
|
+
{ 'cc' => @cc, 'plugin' => 'commands' }.to_json(*a)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.wizard
|
24
|
+
{
|
25
|
+
'cc' => { 'help' => 'Control command, commands send to zmb must be prefixed with this.', 'default' => '.' },
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def event(sender, e)
|
30
|
+
return if not e.message?
|
31
|
+
|
32
|
+
if e.message[0, @cc.length] == @cc then
|
33
|
+
line = e.message[@cc.length..-1].clone
|
34
|
+
elsif e.private? then
|
35
|
+
line = e.message.clone
|
36
|
+
end
|
37
|
+
|
38
|
+
# Encode escaped quotation marks
|
39
|
+
line.gsub!(/\\"|\\'/) { |m| m =~ /^\\"$/ ? "\000d\000" : "\000s\000" }
|
40
|
+
|
41
|
+
# Encode pipes inside quotation marks
|
42
|
+
line.gsub!(/"\w*\|\w*"/) { |m| m.sub('|', "\000p\000") }
|
43
|
+
|
44
|
+
# Check there are a even amount of "" and ''
|
45
|
+
if ((line.count("'") % 2) == 1) and ((line.count('""') % 2) == 1) then
|
46
|
+
return e.reply('Incorrect amount of quotation marks\'s')
|
47
|
+
end
|
48
|
+
|
49
|
+
# Split the commands up
|
50
|
+
commands = line.split('|')
|
51
|
+
input = nil
|
52
|
+
|
53
|
+
commands.each do |command|
|
54
|
+
command = command.reverse.chomp.reverse
|
55
|
+
|
56
|
+
# Split strings by quotation marks and spaces
|
57
|
+
args = command.split(/"([^"]*)"|'([^']*)'|\s/).reject{ |x| x.empty? }
|
58
|
+
|
59
|
+
# Decode escape quotation marks and pipes inside the args
|
60
|
+
args.each do |arg|
|
61
|
+
arg.sub!("\000d\000", '"')
|
62
|
+
arg.sub!("\000s\000", "'")
|
63
|
+
arg.sub!("\000p\000", '|')
|
64
|
+
end
|
65
|
+
|
66
|
+
cmd = args.delete_at(0)
|
67
|
+
|
68
|
+
if @cmds.has_key?(cmd) then
|
69
|
+
args << input if input
|
70
|
+
input = @cmds[cmd].run(e, args)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
e.reply(input) if input
|
75
|
+
end
|
76
|
+
|
77
|
+
def plugin_loaded(key, instance)
|
78
|
+
@cmds.merge!(instance.commands) if instance.respond_to?('commands')
|
79
|
+
end
|
80
|
+
|
81
|
+
def plugin_unloaded(key, instance)
|
82
|
+
instance.commands.each{|command, cmd| @cmds.delete(command)} if instance.respond_to?('commands')
|
83
|
+
end
|
84
|
+
|
85
|
+
def commands
|
86
|
+
{
|
87
|
+
'help' => Command.new(self, :help),
|
88
|
+
'cc' => PermCommand.new('admin', self, :control_command),
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def help(e, command=nil)
|
93
|
+
if command then
|
94
|
+
if @cmds.has_key?(command) and @cmds[command].help? then
|
95
|
+
"#{command}: #{@cmds[command].help}"
|
96
|
+
else
|
97
|
+
'Command not found or no help availible for the command.'
|
98
|
+
end
|
99
|
+
else
|
100
|
+
@cmds.keys.join(', ')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def control_command(e, cc=nil)
|
105
|
+
if cc then
|
106
|
+
@cc = cc
|
107
|
+
else
|
108
|
+
@cc = '!'
|
109
|
+
end
|
110
|
+
|
111
|
+
"Control command set to #{@cc}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
Plugin.define do
|
116
|
+
name "commands"
|
117
|
+
description "This plugin is needed for other plugins to function properly."
|
118
|
+
object Commands
|
119
|
+
end
|