zmb 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -26,12 +26,27 @@ You can run zmb in a shell mode to test plugins without even connecting to any i
26
26
 
27
27
  ### Included plugins
28
28
 
29
- - IRC
30
- - Quote
29
+ - IRC - Connect to a IRC server
30
+ - Quote - Quotes
31
+ - Poll - Voting system
31
32
  - Relay - Relay between servers and/or channels
32
33
  - Users - User management
34
+ - Log - Log everything said in a channel
35
+ - GCalc - Execute a expression using google calculator
36
+ - Announce - Send message to a channel automatically in a certain amount of time
37
+ - DNS - Perform DNS, RDNS and whois lookups
38
+ - NickServ - Log into NickServ
39
+ - Security - Hashes, rot13, and morse code
40
+ - Random - Pick a random value from a list, yes or no, coinflip
41
+ - URL - Dpaste, pastie, bitly, tinyurl, isgd
33
42
  - Bank - Points system
34
43
 
44
+ #### Other features
45
+
46
+ - Piping commands together
47
+
48
+ .help | pastie
49
+
35
50
  ### Support
36
51
 
37
52
  You can find support at #zmb @ efnet.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
data/lib/zmb.rb CHANGED
@@ -7,6 +7,7 @@ rescue LoadError
7
7
  gem 'json'
8
8
  end
9
9
 
10
+ require 'zmb/utils'
10
11
  require 'zmb/plugin'
11
12
  require 'zmb/settings'
12
13
  require 'zmb/event'
@@ -14,10 +14,14 @@ class Timer
14
14
  end
15
15
 
16
16
  def fire(sender)
17
- if @data then
18
- @delegate.send @symbol, @data
19
- else
20
- @delegate.send @symbol
17
+ begin
18
+ if @data then
19
+ @delegate.send @symbol, @data
20
+ else
21
+ @delegate.send @symbol
22
+ end
23
+ rescue Exception
24
+
21
25
  end
22
26
 
23
27
  if not @repeat
@@ -0,0 +1,144 @@
1
+ class String
2
+ def self.random(len=8)
3
+ characters = ('a'..'z').to_a + ('1'..'9').to_a
4
+ (1..len).map{ characters[rand(characters.size)] }.join
5
+ end
6
+
7
+ def split_seperators
8
+ if include?("\n") then
9
+ split("\n").map{ |arg| arg.strip }
10
+ elsif include?(',') then
11
+ split(',').map{ |arg| arg.strip }
12
+ elsif include?(' ') then
13
+ split(' ')
14
+ else
15
+ [self]
16
+ end
17
+ end
18
+
19
+ def truncate_words(num)
20
+ return [self] if size <= num
21
+
22
+ lines = Array.new
23
+ line = Array.new
24
+ len = 0
25
+
26
+ split(' ').each do |word|
27
+ len += 1 unless len == 0
28
+ len += word.size
29
+
30
+ if not len <= num then
31
+ lines << line.join(' ')
32
+ line = Array.new
33
+ len = word.size
34
+ end
35
+
36
+ line << word
37
+ end
38
+
39
+ lines << line.join(' ') if lines.size != 0
40
+
41
+ lines
42
+ end
43
+
44
+ def plural(amount=2)
45
+ if amount == 1 then
46
+ self
47
+ else
48
+ self + 's'
49
+ end
50
+ end
51
+
52
+ def http(type, q=nil)
53
+ u = URI.parse(self)
54
+
55
+ http = Net::HTTP.new(u.host, u.port)
56
+ q = q.to_query_string if q.class == Hash
57
+ q = u.query unless q
58
+
59
+ http.start do |h|
60
+ case type
61
+ when 'get' then h.get(u.path + '?' + q)
62
+ when 'post' then h.post(u.path, q)
63
+ when 'head' then h.head(u.path + '?' + q)
64
+ end
65
+ end
66
+ end
67
+
68
+ def get(q={})
69
+ http('get', q)
70
+ end
71
+
72
+ def post(q={})
73
+ http('post', q)
74
+ end
75
+ end
76
+
77
+ class Array
78
+ def split_seperators
79
+ self
80
+ end
81
+
82
+ def word_count(seperator='')
83
+ join(seperator).size
84
+ end
85
+
86
+ def list_join
87
+ (size > 2 ? [slice(0..-2).join(', '), last] : self).join(' and ')
88
+ end
89
+
90
+ def list_count
91
+ items = {}
92
+
93
+ each do |i|
94
+ items[i] = 0 unless items.has_key?(i)
95
+ items[i] += 1
96
+ end
97
+
98
+ items.map{ |i, c| c == 1 ? i : "#{i} (#{c})" }.join(', ')
99
+ end
100
+ end
101
+
102
+ class Hash
103
+ def to_query_string
104
+ map { |k, v|
105
+ if v.instance_of?(Hash)
106
+ v.map { |sk, sv|
107
+ "#{k}[#{sk}]=#{sv}"
108
+ }.join('&')
109
+ else
110
+ "#{k}=#{v}"
111
+ end
112
+ }.join('&')
113
+ end
114
+ end
115
+
116
+ class Time
117
+ def since
118
+ Time.now - self
119
+ end
120
+
121
+ def since_words
122
+ s = since
123
+ m = Array.new
124
+ s = s * -1 if future = (s < 0) # Is the time in the future?
125
+
126
+ [
127
+ ['year', 60 * 60 * 24 * 365],
128
+ ['month', 60 * 60 * 24 * 30],
129
+ ['week', 60 * 60 * 24 * 7],
130
+ ['day', 60 * 60 * 24],
131
+ ['hour', 60 * 60],
132
+ ['minute', 60],
133
+ ['second', 1],
134
+ ].each do |word, t|
135
+ amount = (s/t).floor
136
+ s -= amount * t
137
+ m << "#{amount} #{word.plural(amount)}" unless amount == 0
138
+ end
139
+
140
+ m << 'now' if m.size == 0
141
+
142
+ m.list_join + (future ? ' left' : '')
143
+ end
144
+ end
@@ -12,9 +12,17 @@ class Alias
12
12
 
13
13
  def commands
14
14
  {
15
- 'alias' => [:add, 2, { :permission => 'admin' }],
16
- 'unalias' => [:del, 1, { :permission => 'admin' }],
17
- 'aliases' => [:aliases, 0, { :permission => 'admin' }]
15
+ 'alias' => [:add, 2, {
16
+ :permission => 'admin',
17
+ :help => 'Create a alias',
18
+ :example => 'hello? .echo Hello!' }],
19
+ 'unalias' => [:del, 1, {
20
+ :permission => 'admin',
21
+ :help => 'Remove a alias',
22
+ :usage => 'hello?' }],
23
+ 'aliases' => [:aliases, 0, {
24
+ :permission => 'admin',
25
+ :help => 'List all aliases' }],
18
26
  }
19
27
  end
20
28
 
@@ -55,13 +55,21 @@ class Announce
55
55
  'announcements' => [:announcements, 0, {
56
56
  :permission => 'admin',
57
57
  :help => 'List all the id\'s for' }],
58
- 'announcement' => [:announcement, 1, { :permission => 'admin' }],
58
+ 'announcement' => [:announcement, 1, {
59
+ :permission => 'admin',
60
+ :help => 'Display infomation about a announcement',
61
+ :usage => 'id',
62
+ :example => '1' }],
59
63
  'announce' => [:announce, 4, {
60
64
  :permission => 'admin',
61
65
  :help => 'Add a announcement',
62
66
  :usage => 'instance location interval message',
63
67
  :example => 'efnet #zmb 600 Check github for the latest updates!' }],
64
- 'announce-del' => [:announce_del, 1, { :permission => 'admin' }],
68
+ 'announce-del' => [:announce_del, 1, {
69
+ :permission => 'admin',
70
+ :help => 'Delete a announcement',
71
+ :usage => 'id',
72
+ :example => '1' }],
65
73
  }
66
74
  end
67
75
 
@@ -21,22 +21,47 @@ class Commands
21
21
  }
22
22
  end
23
23
 
24
+ def escape
25
+ {
26
+ '"' => "\000d\000",
27
+ "'" => "\000s\000",
28
+ '|' => "\000p\000",
29
+ }
30
+ end
31
+
24
32
  def event(sender, e)
25
33
  return if not e.message?
26
34
 
27
35
  if e.message[0, @cc.length] == @cc then
28
36
  line = e.message[@cc.length..-1].clone
37
+ elsif e.delegate.respond_to?('nick') and e.message[0, (e.delegate.nick.length+2)] == (e.delegate.nick + ': ') then
38
+ line = e.message[(e.delegate.nick.length+2)..-1].clone
29
39
  elsif e.private? then
30
40
  line = e.message.clone
41
+ else
42
+ return
31
43
  end
32
44
 
45
+ return if e.name[0..0] == '*'
46
+
47
+ line.sub!('{time}', Time.now.strftime('%H:%M:%S'))
48
+ line.sub!('{day}', Time.now.strftime('%d'))
49
+ line.sub!('{weekday}', Time.now.strftime('%A'))
50
+ line.sub!('{timezone}', Time.now.strftime('%Z'))
51
+ line.sub!('{month}', Time.now.strftime('%B'))
52
+ line.sub!('{year}', Time.now.strftime('%Y'))
53
+ line.sub!('{username}', e.user.username) if e.respond_to?('user') and e.user.respond_to?('username')
54
+ line.sub!('{points}', "#{e.bank.balance}") if e.respond_to?('bank') and e.bank.respond_to?('balance')
55
+ line.sub!('{channel}', e.channel) if e.respond_to?('channel')
56
+ line.sub!('{name}', e.name) if e.respond_to?('name')
57
+ line.sub!('{userhost}', e.userhost) if e.respond_to?('userhost')
58
+ line.sub!('{rand}', String.random)
59
+
33
60
  # Encode escaped quotation marks and pipes
34
- line.gsub!('\"', "\000d\000")
35
- line.gsub!("\\'", "\000s\000")
36
- line.gsub!('\|', "\000p\000")
61
+ escape.each{ |k,v| line.gsub!("\\" + k, v) }
37
62
 
38
63
  # Check there are a even amount of "" and ''
39
- if ((line.count("'") % 2) == 1) and ((line.count('""') % 2) == 1) then
64
+ if ((line.count("'") % 2) == 1) and ((line.count('"') % 2) == 1) then
40
65
  return e.reply('Incorrect amount of quotation marks\'s')
41
66
  end
42
67
 
@@ -51,11 +76,7 @@ class Commands
51
76
  args = command.split(/"([^"]*)"|'([^']*)'|\s/).reject{ |x| x.empty? }
52
77
 
53
78
  # Decode escape quotation marks and pipes inside the args
54
- args.each do |arg|
55
- arg.gsub!("\000d\000", '"')
56
- arg.gsub!("\000s\000", "'")
57
- arg.gsub!("\000p\000", '|')
58
- end
79
+ args.each{ |arg| escape.each{ |k,v| arg.gsub!(v, k) } }
59
80
 
60
81
  cmd = args.delete_at(0)
61
82
  args << input if input
@@ -65,8 +86,8 @@ class Commands
65
86
  e.reply(input) if input
66
87
  end
67
88
 
68
- def execute(cmd, e, args)
69
- return if not @cmds.has_key?(cmd)
89
+ def execute(cmd, e, args=[])
90
+ return "#{cmd}: command not found" if not @cmds.has_key?(cmd)
70
91
 
71
92
  c = @cmds[cmd]
72
93
 
@@ -90,7 +111,13 @@ class Commands
90
111
  end
91
112
 
92
113
  begin
93
- c[0].send(c[1], e, *args)
114
+ if c[1].class == Symbol then
115
+ c[0].send(c[1], e, *args)
116
+ elsif c[1].class == Proc then
117
+ c[1].call(e, *args)
118
+ else
119
+ "Bad command definition"
120
+ end
94
121
  rescue ArgumentError
95
122
  'incorrect arguments'
96
123
  rescue Exception
@@ -117,18 +144,6 @@ class Commands
117
144
  @cmds = @cmds.reject{ |k,v| v[0] == instance }
118
145
  end
119
146
 
120
- def split_seperators(data)
121
- if data.include?("\n") then
122
- data.split("\n").map{ |arg| arg.strip }
123
- elsif data.include?(',') then
124
- data.split(',').map{ |arg| arg.strip }
125
- elsif data.include?(' ') then
126
- data.split(' ')
127
- else
128
- data
129
- end
130
- end
131
-
132
147
  def commands
133
148
  {
134
149
  'help' => :help,
@@ -136,12 +151,27 @@ class Commands
136
151
  'which' => [:which, 1, { :help => 'Find which plugin handles a command' }],
137
152
  'cc' => [:control_command, 1, { :permission => 'admin' }],
138
153
  'eval' => [:evaluate, 1, { :permission => 'admin' }],
139
- 'count' => :count,
154
+ 'ieval' => [:instance_evaluate, 2, { :permission => 'admin' }],
155
+ 'count' => lambda { |e, data| "#{data.split_seperators.size}" },
140
156
  'grep' => [:grep, 2],
141
157
  'not' => [:not_command, 2],
142
158
  'tail' => :tail,
143
- 'echo' => :echo,
144
- 'reverse' => :reverse,
159
+ 'echo' => [:echo, 1, { :example => 'Hello, {username}' }],
160
+ 'reverse' => lambda { |e, data| data.reverse },
161
+ 'first' => lambda { |e, data| data.split_seperators.first },
162
+ 'last' => lambda { |e, data| data.split_seperators.last },
163
+ 'sub' => [:sub, 3, {
164
+ :help => 'Replace all occurances of a pattern',
165
+ :usage => 'pattern replacement data',
166
+ :example => 'l * Hello World!' }],
167
+ 'tr' => [:tr, 3, {
168
+ :help => 'Returns a copy of str with the characters in from_str replaced by the corresponding characters in to_str',
169
+ :usage => 'from_str to_str data',
170
+ :example => 'aeiou * hello' }],
171
+ 'downcase' => lambda { |e, data| data.downcase },
172
+ 'upcase' => lambda { |e, data| data.upcase },
173
+ 'swapcase' => lambda { |e, data| data.swapcase },
174
+ 'capitalize' => lambda { |e, data| data.capitalize },
145
175
  }
146
176
  end
147
177
 
@@ -209,28 +239,40 @@ class Commands
209
239
  end
210
240
  end
211
241
 
212
- def count(e, data)
213
- "#{split_seperators(data).size}"
242
+ def instance_evaluate(e, inst, string)
243
+ begin
244
+ if @delegate.instances.has_key?(inst) then
245
+ "#{@delegate.instances[inst].instance_eval string}"
246
+ else
247
+ "#{inst}: No such instance"
248
+ end
249
+ rescue Exception
250
+ "#{$!.message}\n#{$!.inspect}"
251
+ end
214
252
  end
215
253
 
216
254
  def grep(e, search, data)
217
- split_seperators(data).reject{ |d| not d.include?(search) }.join(', ')
255
+ data.split_seperators.reject{ |d| not d.include?(search) }.join(', ')
218
256
  end
219
257
 
220
258
  def not_command(e, search, data)
221
- split_seperators(data).reject{ |d| d.include?(search) }.join(', ')
259
+ data.split_seperators.reject{ |d| d.include?(search) }.join(', ')
222
260
  end
223
261
 
224
262
  def tail(e, data)
225
- split_seperators(data).reverse[0..2].join(', ')
263
+ data.split_seperators.reverse[0..2].join(', ')
226
264
  end
227
265
 
228
266
  def echo(e, data)
229
267
  "#{data}"
230
268
  end
231
269
 
232
- def reverse(e, data)
233
- data.reverse
270
+ def sub(e, pattern, replacement, data)
271
+ data.gsub(pattern, replacement)
272
+ end
273
+
274
+ def tr(e, from_str, to_str, data)
275
+ data.tr(from_str, to_str)
234
276
  end
235
277
  end
236
278
 
@@ -0,0 +1,46 @@
1
+ require 'time'
2
+
3
+ class Countdown
4
+ attr_accessor :settings
5
+
6
+ def initialize(sender, settings)
7
+ @settings = { 'countdown' => {} }
8
+ settings['countdown'].each{ |k,v| @settings['countdown'][k] = Time.parse(v) } if settings.has_key?('countdown')
9
+ end
10
+
11
+ def commands
12
+ {
13
+ 'countdown' => [:countdown, 1, { :usage => 'key' }],
14
+ 'countdowns' => [:countdowns, 0],
15
+ 'add-countdown' => [:add, 2, { :permission => 'countdown', :usage => 'key date/time', :example => 'xmas 25-12-2010' }],
16
+ 'rm-countdown' => [:remove, 1, { :permission => 'countdown', :usage => 'key' }]
17
+ }
18
+ end
19
+
20
+ def countdown(e, key)
21
+ if @settings['countdown'].has_key?(key) then
22
+ @settings['countdown'][key].since_words
23
+ else
24
+ "No such countdown"
25
+ end
26
+ end
27
+
28
+ def countdowns(e)
29
+ @settings['countdown'].keys.join(', ')
30
+ end
31
+
32
+ def add(e, key, time)
33
+ @settings['countdown'][key] = Time.parse(time)
34
+ "Countdown #{key} added"
35
+ end
36
+
37
+ def remove(e, key)
38
+ @settings['countdown'].delete(key)
39
+ "Countdown #{key} removed"
40
+ end
41
+ end
42
+
43
+ Plugin.define do
44
+ name 'countdown'
45
+ object Countdown
46
+ end