zmb 0.2.1 → 0.3.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.
@@ -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