mod_spox 0.0.2 → 0.0.3

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.
Files changed (40) hide show
  1. data/CHANGELOG +1 -1
  2. data/README +6 -2
  3. data/bin/mod_spox +15 -9
  4. data/data/mod_spox/extras/AOLSpeak.rb +271 -0
  5. data/data/mod_spox/extras/AutoKick.rb +119 -0
  6. data/data/mod_spox/extras/AutoRejoin.rb +14 -0
  7. data/data/mod_spox/extras/Bullshit.rb +19 -0
  8. data/data/mod_spox/extras/Confess.rb +166 -0
  9. data/data/mod_spox/extras/DevWatch.rb +154 -0
  10. data/data/mod_spox/extras/EightBall.rb +31 -0
  11. data/data/mod_spox/extras/Headers.rb +50 -0
  12. data/data/mod_spox/extras/Karma.rb +16 -17
  13. data/data/mod_spox/extras/LolSpeak.rb +22 -0
  14. data/data/mod_spox/extras/PhpCli.rb +146 -0
  15. data/data/mod_spox/extras/PhpFuncLookup.rb +261 -0
  16. data/data/mod_spox/extras/Quotes.rb +80 -0
  17. data/data/mod_spox/extras/Roulette.rb +24 -3
  18. data/data/mod_spox/extras/Talk.rb +41 -0
  19. data/data/mod_spox/extras/Translate.rb +95 -0
  20. data/data/mod_spox/extras/Weather.rb +55 -0
  21. data/data/mod_spox/plugins/Authenticator.rb +12 -0
  22. data/data/mod_spox/plugins/Banner.rb +3 -3
  23. data/data/mod_spox/plugins/Helper.rb +37 -0
  24. data/lib/mod_spox/Bot.rb +1 -1
  25. data/lib/mod_spox/ConfigurationWizard.rb +12 -5
  26. data/lib/mod_spox/MessageFactory.rb +1 -2
  27. data/lib/mod_spox/Monitors.rb +26 -8
  28. data/lib/mod_spox/Pipeline.rb +13 -13
  29. data/lib/mod_spox/PluginManager.rb +12 -1
  30. data/lib/mod_spox/Pool.rb +212 -29
  31. data/lib/mod_spox/Socket.rb +6 -6
  32. data/lib/mod_spox/Timer.rb +6 -8
  33. data/lib/mod_spox/handlers/Handler.rb +1 -1
  34. data/lib/mod_spox/handlers/Privmsg.rb +1 -0
  35. data/lib/mod_spox/handlers/Who.rb +1 -0
  36. data/lib/mod_spox/messages/internal/PluginRequest.rb +1 -1
  37. data/lib/mod_spox/messages/outgoing/Privmsg.rb +9 -1
  38. data/lib/mod_spox/models/Setting.rb +3 -3
  39. data/lib/mod_spox/models/Signature.rb +1 -1
  40. metadata +18 -20
@@ -0,0 +1,261 @@
1
+ require 'cgi'
2
+
3
+ # Inspired from an old plugin for the original mod_spox
4
+ # Original development: spox & Ryan "pizza_milkshake" Flynn
5
+ # Ported: 2008
6
+
7
+ class PhpFuncLookup < ModSpox::Plugin
8
+
9
+ include Models
10
+
11
+ def initialize(pipeline)
12
+ super(pipeline)
13
+ setup_setting
14
+ @path = Setting[:phpfunc][:directory]
15
+ @trigger = Setting[:phpfunc][:trigger]
16
+ @manual = "#{@path}/html"
17
+ @classlist = []
18
+ fetch_manual unless File.exists?("#{@path}/manual.tar.gz")
19
+ @ops = {
20
+ "-" => [ "arithmetic", "Subtraction or Negation", "3 - 2 == 1" ],
21
+ "+" => [ "arithmetic", "Addition", "3 + 2 == 5" ],
22
+ "*" => [ "arithmetic", "Multiplication", "3 * 2 == 6" ],
23
+ "/" => [ "arithmetic", "Division", "3 / 2 == 1.5" ],
24
+ "%" => [ "arithmetic", "Modulus", "3 % 2 == 1" ],
25
+ "=" => [ "assignment", "Assignment", "$foo = 1; -> $foo == 1" ],
26
+ "&" => [
27
+ [ "bitwise", "references" ],
28
+ [ "Bitwise And", "Reference" ],
29
+ [ "0x3 & 0x1 -> 1", "$foo=1; $bar=&$foo; $bar++; -> $foo == 2" ]
30
+ ],
31
+ "|" => [ "bitwise", "Bitwise Or", "" ],
32
+ "^" => [ "bitwise", "Bitwise Xor", "" ],
33
+ "~" => [ "bitwise", "Bitwise Not", "" ],
34
+ "<<" => [ "bitwise", "Bitwise Shift left", "" ],
35
+ ">>" => [ "bitwise", "Bitwise Shift right", "" ],
36
+ "==" => [ "comparison", "Equal", "" ],
37
+ "===" => [ "comparison", "Identical", "" ],
38
+ "!=" => [ "comparison", "Not Equal", "" ],
39
+ "<>" => [ "comparison", "Not Equal", "" ],
40
+ "!==" => [ "comparison", "Not Identical", "" ],
41
+ "<" => [ "comparison", "Less Than", "" ],
42
+ ">" => [ "comparison", "Greater Than", "" ],
43
+ "<=" => [ "comparison", "Less Than Or Equal To", "" ],
44
+ ">=" => [ "comparison", "Greater Than Or Equal To", "" ],
45
+ "@" => [ "errorcontrol","Error Control", "" ],
46
+ "`" => [ "execution", "Execution", "" ],
47
+ "++" => [ "increment", "Pre- or Post-Increment", "" ],
48
+ "--" => [ "increment", "Pre- or Post-Decrement", "" ],
49
+ "and" => [ "logical", "And", "" ],
50
+ "or" => [ "logical", "Or", "" ],
51
+ "xor" => [ "logical", "Xor", "" ],
52
+ "!" => [ "logical", "Not", "" ],
53
+ "&&" => [ "logical", "And", "" ],
54
+ "||" => [ "logical", "Or", "" ],
55
+ "." => [ "string", "Concatenation", "" ],
56
+ ".=" => [ "string", "Concatenation Assignment", "" ],
57
+ "instanceof" => [ "type", "Instance Of", "" ],
58
+ "new" => [ "", "New Object", "" ],
59
+ "+=" => [ "assignment", "Addition Assignment", "" ],
60
+ "-=" => [ "assignment", "Subtraction Assignment", "" ],
61
+ "/=" => [ "assignment", "Division Assignment", "" ],
62
+ "*=" => [ "assignment", "Multiplication Assignment", "" ],
63
+ "%=" => [ "assignment", "Modulus Assignment", "" ],
64
+ "->" => [ "?", "Object member accessor thingy","" ],
65
+ }
66
+ admin = Group.filter(:name => 'admin').first
67
+ Signature.find_or_create(:signature => 'pfunc (\S+)', :plugin => name, :method => 'phpfunc', :description => 'Lookup PHP function').params = [:name]
68
+ Signature.find_or_create(:signature => 'fetch php manual', :plugin => name, :method => 'fetch', :group_id => admin.pk,
69
+ :description => 'Download and extract PHP manual')
70
+ Signature.find_or_create(:signature => 'pfunc trigger (\S+)', :plugin => name, :method => 'set_trigger', :group_id => admin.pk,
71
+ :description => 'Set the trigger for auto-lookups').params = [:trigger]
72
+ Signature.find_or_create(:signature => 'pfunc show trigger', :plugin => name, :method => 'show_trigger', :description => 'Show current trigger')
73
+ Signature.find_or_create(:signature => 'pfunc (add|remove) (\S+)', :plugin => name, :method => 'set_channels', :group_id => admin.pk,
74
+ :description => 'Add or remove channels from auto-lookups').params = [:action, :channel]
75
+ Signature.find_or_create(:signature => 'pfunc show channels', :plugin => name, :method => 'list_channels', :description => 'Show channels with auto lookup enabled')
76
+ @pipeline.hook(self, :listen, :Incoming_Privmsg)
77
+ populate_classes
78
+ end
79
+
80
+ def phpfunc(m, params)
81
+ name = params[:name].downcase
82
+ Logger.log "phpfunc name=#{name}"
83
+ if name =~ /^\S+$/ && name =~ /\*/
84
+ parse_wildcard(m, name)
85
+ elsif name =~ /^\$/
86
+ parse_predefined(m, name)
87
+ elsif name =~ /^\S+$/ && filename = find_file(name)
88
+ parse_function(m, name, filename)
89
+ elsif @ops.has_key?(name)
90
+ parse_operator(m, name)
91
+ end
92
+ end
93
+
94
+ def fetch(m, params)
95
+ reply m.replyto, "Fetching PHP manual (This could take a few minutes)"
96
+ fetch_manual
97
+ end
98
+
99
+ def listen(m)
100
+ if(m.target.is_a?(Channel) && Setting[:phpfunc][:channels].include?(m.target.pk))
101
+ if m.message =~ /^#{Regexp.escape(@trigger)}(\S+)$/
102
+ phpfunc(m, {:name => $1})
103
+ end
104
+ end
105
+ end
106
+
107
+ def set_trigger(message, params)
108
+ vals = Setting[:phpfunc]
109
+ vals[:trigger] = params[:trigger]
110
+ Setting.filter(:name => 'phpfunc').first.value = vals
111
+ @trigger = params[:trigger]
112
+ reply message.replyto, "PHP function lookup trigger set to: #{params[:trigger]}"
113
+ end
114
+
115
+ def set_channels(message, params)
116
+ channel = Channel.filter(:name => params[:channel]).first
117
+ if(channel)
118
+ vals = Setting[:phpfunc]
119
+ if(params[:action] == 'add')
120
+ vals[:channels] << channel.pk unless Setting[:phpfunc][:channels].include?(channel.pk)
121
+ reply message.replyto, "Channel \2#{params[:channel]}\2 added to PHP auto lookup"
122
+ else
123
+ vals[:channels].delete(channel.pk) if Setting[:phpfunc][:channels].include?(channel.pk)
124
+ reply message.replyto, "Channel \2#{params[:channel]}\2 has been removed from PHP auto lookup"
125
+ end
126
+ Setting.filter(:name => 'phpfunc').first.value = vals
127
+ else
128
+ reply message.replyto, "Error: No record of channel #{params[:channel]}"
129
+ end
130
+ end
131
+
132
+ def list_channels(message, params)
133
+ if(Setting[:phpfunc][:channels].size > 0)
134
+ chans = []
135
+ Setting[:phpfunc][:channels].each do |id|
136
+ chans << Channel[id].name
137
+ end
138
+ reply message.replyto, "PHP auto lookup enabled channels: #{chans.join(', ')}"
139
+ else
140
+ reply message.replyto, 'No channels currently enabled for PHP auto lookup'
141
+ end
142
+ end
143
+
144
+ def show_trigger(message, p)
145
+ reply message.replyto, "PHP auto lookup trigger: \2#{Setting[:phpfunc][:trigger]}\2"
146
+ end
147
+
148
+ private
149
+
150
+ def find_file(name)
151
+ Dir.new(@manual).each do |filename|
152
+ return filename if filename.downcase == "function.#{name.gsub(/(_|->)/, '-').downcase}.html"
153
+ end
154
+ return nil
155
+ end
156
+
157
+ def fetch_manual(message=nil, params=nil)
158
+ Thread.new do
159
+ manual_site = 'http://us.php.net/'
160
+ Logger.log "Fetching PHP manual from #{manual_site}"
161
+ connection = Net::HTTP.new("us.php.net", 80)
162
+ File.open("#{@path}/manual.tar.gz", 'w'){ |manual|
163
+ connection.get('/distributions/manual/php_manual_en.tar.gz', nil){ |line|
164
+ manual.write(line)
165
+ }
166
+ }
167
+ Dir.chdir(@path)
168
+ Helpers.safe_exec("tar -xzf #{@path}/manual.tar.gz", 60)
169
+ Logger.log "PHP manual fetching complete."
170
+ reply message.replyto, "PHP manual fetch is now complete" unless message.nil?
171
+ end
172
+ end
173
+
174
+ def setup_setting
175
+ s = Setting.filter(:name => 'phpfunc').first
176
+ unless(s)
177
+ s = Setting.find_or_create(:name => 'phpfunc')
178
+ s.value = {:directory => Config[:plugin_directory] + '/php', :trigger => '@', :channels => []}
179
+ end
180
+ unless(File.directory?(Setting[:phpfunc][:directory]))
181
+ FileUtils.mkdir_p(Setting[:phpfunc][:directory])
182
+ end
183
+ end
184
+
185
+ def parse_predefined(m, name)
186
+ name.upcase!
187
+ page = File.open("#{@manual}/language.variables.predefined.html").readlines.join(' ').gsub(/[\n\r]/, '')
188
+ if page =~ /<dt>\s*<span class="term"><a href="reserved\.variables\.html.+? class="link">#{name.gsub(/\$/, '\$')}<\/a><\/span>\s*<dd>\s*<span class="simpara">(.+?)<\/span>/
189
+ desc = $1
190
+ desc.gsub!(/[\r\n]/, ' ')
191
+ desc.gsub!(/<.+?>/, ' ')
192
+ desc = CGI::unescapeHTML(desc.gsub(/\s+/, ' '))
193
+ reply m.replyto, "\2PHP Superglobal\2"
194
+ reply m.replyto, "\2#{name}:\2 #{desc}"
195
+ else
196
+ reply m.replyto, "No superglobal found matching: #{name}"
197
+ end
198
+ end
199
+
200
+ def parse_wildcard(m, name)
201
+ matches = []
202
+ pattern = name.gsub(/\*/, '.*?')
203
+ Dir.open(@manual).each do |file|
204
+ if(file =~ /^(.+?#{pattern}.+?)\.html/)
205
+ match = $1
206
+ if(match =~ /^function\.(.+?)\-/)
207
+ if(@classlist.include?($1.downcase))
208
+ match.gsub!(/[-]/, '_')
209
+ match.sub!(/_/, '->')
210
+ else
211
+ match.gsub!(/[-]/, '_')
212
+ end
213
+ match.gsub!(/^function\./, '')
214
+ else
215
+ match = nil
216
+ end
217
+ matches << match unless match.nil?
218
+ end
219
+ end
220
+ matches.sort!
221
+ reply m.replyto, "Lots of matching functions. Truncating list to 20 results."
222
+ reply m.replyto, matches.values_at(0..19).join(', ')
223
+ end
224
+
225
+ def parse_function(m, name, filename)
226
+ page = File.open("#{@manual}/#{filename}", 'r').readlines.join('')
227
+ page.gsub!(/[\r\n]/, '')
228
+ versions = page =~ /<p class="verinfo">(.+?)<\/p>/i ? $1 : '(UNKNOWN)'
229
+ proto = page =~ /<div class="methodsynopsis dc-description">(.+?)<\/div>/i ? $1 : name
230
+ desc = page =~ /<p class="refpurpose dc-title">.+? — (.+?)<\/p>/i ? $1 : '(UNKNOWN)'
231
+ versions = CGI::unescapeHTML(versions)
232
+ proto = CGI::unescapeHTML(proto.gsub(/<.+?>/, ' ').gsub(/[\s]+/, ' '))
233
+ desc = CGI::unescapeHTML(desc.gsub(/<.+?>/, ' ').gsub(/[\s]+/, ' '))
234
+ reply m.replyto, versions
235
+ reply m.replyto, "\2#{proto}\2"
236
+ reply m.replyto, desc
237
+ reply m.replyto, "http://www.php.net/manual/en/#{filename.gsub(/\.html$/, '.php')}"
238
+ end
239
+
240
+ def parse_operator(m, name)
241
+ Logger.log "parse_operator name=#{name}"
242
+ name.downcase!
243
+ type, title, ejemplo = @ops[name]
244
+ reply m.replyto, "\2#{name}\2 is the \2#{title.to_a.join("\2 or \2")}\2 operator"
245
+ type.to_a.each do |t|
246
+ reply m.replyto, "http://php.net/manual/en/language.operators.#{t}.php"
247
+ end
248
+ end
249
+
250
+ def populate_classes
251
+ if(@classlist.empty?)
252
+ Dir.open(@manual).each do |file|
253
+ if(file =~ /^class\.(.+?)\.html/)
254
+ @classlist << $1
255
+ end
256
+ end
257
+ @classlist.uniq!
258
+ end
259
+ end
260
+
261
+ end
@@ -0,0 +1,80 @@
1
+ class Quotes < ModSpox::Plugin
2
+
3
+ include Models
4
+
5
+ def initialize(pipeline)
6
+ super
7
+ Quote.create_table unless Quote.table_exists?
8
+ group = Group.find_or_create(:name => 'quote')
9
+ Signature.find_or_create(:signature => 'quote ?(.+)?', :plugin => name, :method => 'quote',
10
+ :description => 'Display random quote, random quote containing search term, or quote with given ID').params = [:term]
11
+ Signature.find_or_create(:signature => 'addquote (.+)', :plugin => name, :method => 'addquote',
12
+ :description => 'Add a new quote').params = [:quote]
13
+ Signature.find_or_create(:signature => 'searchquote (.+)', :plugin => name, :method => 'searchquote',
14
+ :description => 'Return IDs of quotes matching term').params = [:term]
15
+ Signature.find_or_create(:signature => 'delquote (\d+)', :plugin => name, :method => 'delquote', :group_id => group.pk,
16
+ :description => 'Delete quote with given ID').params = [:id]
17
+ end
18
+
19
+ def quote(message, params)
20
+ quote = nil
21
+ reg = false
22
+ if(params[:term])
23
+ if(params[:term] =~ /^\d+$/)
24
+ quote = Quote[params[:term].to_i]
25
+ else
26
+ quote = Quote.filter(:quote => Regexp.new(params[:term])).first
27
+ reg = true
28
+ end
29
+ else
30
+ ids = Quote.map(:id)
31
+ quote = Quote[ids[rand(ids.size - 1)].to_i]
32
+ end
33
+ if(quote)
34
+ reply message.replyto, "\2[\2#{quote.pk}\2|\2#{quote.added.year}/#{quote.added.month}/#{quote.added.day}\2]:\2 #{reg ? quote.quote.gsub(/(#{params[:term]})/, "\2\\1\2") : quote.quote}"
35
+ else
36
+ reply message.replyto, "\2Error:\2 Failed to find quote"
37
+ end
38
+ end
39
+
40
+ def addquote(message, params)
41
+ quote = Quote.new(:nick_id => message.source.pk, :channel_id => message.target.pk)
42
+ quote.quote = params[:quote]
43
+ quote.added = Time.now
44
+ quote.save
45
+ reply message.replyto, "\2Quote added:\2 ##{quote.pk}"
46
+ end
47
+
48
+ def searchquote(message, params)
49
+ result = Quote.filter(:quote => Regexp.new(params[:term]))
50
+ if(result.size > 0)
51
+ ids = result.map(:id)
52
+ ids.sort!
53
+ ids = ids.slice(0, 20)
54
+ reply message.replyto, "Quotes matching term (#{params[:term]}): #{ids.join(', ')}"
55
+ else
56
+ reply message.replyto, "\2Error:\2 No quotes match search term: #{params[:term]}"
57
+ end
58
+ end
59
+
60
+ def delquote(message, params)
61
+ result = Quote.filter(:id => params[:id].to_i)
62
+ if(result.size < 1)
63
+ reply message.replyto, "\2Error:\2 Failed to find quote with ID: #{params[:id]}"
64
+ else
65
+ result.destroy
66
+ reply message.replyto, "\2Quote deleted:\2 ##{params[:id]}"
67
+ end
68
+ end
69
+
70
+ class Quote < Sequel::Model
71
+ set_schema do
72
+ primary_key :id
73
+ text :quote, :null => false
74
+ timestamp :added, :null => false
75
+ foreign_key :nick_id, :table => :nicks
76
+ foreign_key :channel_id, :table => :channels
77
+ end
78
+ end
79
+
80
+ end
@@ -11,6 +11,14 @@ class Roulette < ModSpox::Plugin
11
11
  Signature.find_or_create(:signature => 'roulette stats ?(\S+)?', :plugin => name, :method => 'stats').params = [:nick]
12
12
  Game.create_table unless Game.table_exists?
13
13
  Info.create_table unless Info.table_exists?
14
+ @banner = nil
15
+ @pipeline.hook(:self, :get_banner, :Internal_PluginResponse)
16
+ end
17
+
18
+ # message:: ModSpox::Messages::Internal::PluginResponse
19
+ # Get the banner plugin
20
+ def get_banner(message)
21
+ @banner = message.plugin if message.found?
14
22
  end
15
23
 
16
24
  # message:: ModSpox::Messages::Incoming::Privmsg
@@ -136,8 +144,7 @@ class Roulette < ModSpox::Plugin
136
144
  reply(channel, "#{nick.nick}: *click*")
137
145
  rescue Bullet => bang
138
146
  game_over(nick, bang.game)
139
- #TODO: add banner here
140
- reply(channel, "#{nick.nick}: *BANG*")
147
+ kill_nick(nick, channel)
141
148
  end
142
149
  end
143
150
 
@@ -151,7 +158,20 @@ class Roulette < ModSpox::Plugin
151
158
  end
152
159
  rescue Bullet => bang
153
160
  game_over(nick, bang.game)
154
- #TODO: add banner here
161
+ kill_nick(nick, channel)
162
+ end
163
+ end
164
+
165
+ def kill_nick(nick, channel)
166
+ unless(@banner.nil?)
167
+ begin
168
+ @banner.ban(nick, channel, '*BANG*', true, false)
169
+ rescue NotOperator => boom
170
+ reply(channel, "#{nick.nick}: *BANG*")
171
+ rescue Object => boom
172
+ Logger.log("Error: Roulette ban generated an unexpected error: #{boom}")
173
+ end
174
+ else
155
175
  reply(channel, "#{nick.nick}: *BANG*")
156
176
  end
157
177
  end
@@ -159,6 +179,7 @@ class Roulette < ModSpox::Plugin
159
179
  # channel:: ModSpox::Models::Channel
160
180
  # Return current game
161
181
  def game(channel)
182
+ @pipeline << Messages::Internal::PluginRequest.new(self, 'Banner') if @banner.nil?
162
183
  game = Game.filter{:shots > 0 && :channel_id == channel.pk}.first
163
184
  unless(game)
164
185
  chamber = rand(5) + 1
@@ -0,0 +1,41 @@
1
+ class Talk < ModSpox::Plugin
2
+
3
+ include Models
4
+
5
+ def initialize(pipeline)
6
+ super
7
+ group = Group.find_or_create(:name => 'talk')
8
+ Signature.find_or_create(:signature => 'say (\S+) (.+)', :plugin => name, :method => 'talk', :group_id => group.pk,
9
+ :description => 'Make bot speak given text to target', :requirement => 'private').params = [:target, :text]
10
+ Signature.find_or_create(:signature => 'action (\S+) (.+)', :plugin => name, :method => 'action', :group_id => group.pk,
11
+ :description => 'Make bot perform action for target', :requirement => 'private').params = [:target, :text]
12
+ end
13
+
14
+ def talk(message, params)
15
+ send_p(message, params)
16
+ end
17
+
18
+ def action(message, params)
19
+ send_p(message, params, true)
20
+ end
21
+
22
+ private
23
+
24
+ def send_p(message, params, action=false)
25
+ target = find_target(params[:target])
26
+ if(target.nil?)
27
+ reply message.replyto, "\2Error:\2 Failed to locate target: #{params[:target]}"
28
+ else
29
+ @pipeline << Messages::Outgoing::Privmsg.new(target, params[:text], action)
30
+ end
31
+ end
32
+
33
+ def find_target(string)
34
+ result = Channel.filter(:name => string).first
35
+ return result if result
36
+ result = Nick.filter(:nick => string).first
37
+ return result if result
38
+ return nil
39
+ end
40
+
41
+ end
@@ -0,0 +1,95 @@
1
+ class Translate < ModSpox::Plugin
2
+
3
+ include Models
4
+
5
+ def initialize(pipeline)
6
+ super(pipeline)
7
+ Signature.find_or_create(:signature => 'translate ([a-z]{2}\|[a-z]{2}) (.+)', :plugin => name, :method => 'translate',
8
+ :description => 'Translate text').params = [:lang, :text]
9
+ Signature.find_or_create(:signature => 'autotranslate add ([a-z]{2}) (\S+)', :plugin => name, :method => 'auto_add',
10
+ :description => 'Add a nick to the autotranslate service').params = [:lang, :nick]
11
+ Signature.find_or_create(:signature => 'autotranslate remove (\S+)', :plugin => name, :method => 'auto_remove',
12
+ :description => 'Remove a nick from the autotranslate service').params = [:nick]
13
+ @watchers = {}
14
+ @cache = {}
15
+ end
16
+
17
+ def auto_add(message, params)
18
+ return unless message.is_public?
19
+ nick = Nick.filter(:nick => params[:nick]).first
20
+ if(nick && nick.channels.include?(message.target))
21
+ @watchers[message.target.pk] = {} unless @watchers.has_key?(message.target.pk)
22
+ @watchers[message.target.pk][nick.pk] = params[:lang] unless @watchers[message.target.pk].has_key?(nick.pk)
23
+ hook
24
+ reply message.replyto, "#{params[:nick]} is now being tracked for auto translation"
25
+ else
26
+ reply message.replyto, "\2Error:\2 Failed to locate #{params[:nick]}"
27
+ end
28
+ end
29
+
30
+ def auto_remove(message, params)
31
+ return unless message.is_public?
32
+ nick = Nick.filter(:nick => params[:nick]).first
33
+ if(nick)
34
+ if(@watchers.has_key?(message.target.pk))
35
+ @watchers[message.target.pk].delete(nick.pk) if @watchers[message.target.pk].has_key?(nick.pk)
36
+ @watchers.delete(message.target.pk) if @watchers[message.target.pk].empty?
37
+ hook
38
+ reply message.replyto, "#{params[:nick]} is no longer being tracked for auto translation"
39
+ else
40
+ reply message.replyto, "No one is currently being tracked"
41
+ end
42
+ else
43
+ reply message.replyto, "\2Error:\2 Failed to locate #{params[:nick]}"
44
+ end
45
+ end
46
+
47
+ def translate(message, params)
48
+ reply message.replyto, "\2Translation:\2 #{do_translation(params[:lang], params[:text])}"
49
+ end
50
+
51
+ def listener(message)
52
+ if(message.is_public? && @watchers.has_key?(message.target.pk))
53
+ if(@watchers[message.target.pk].has_key?(message.source.pk))
54
+ reply message.replyto, "\2Translation (#{message.source.nick}):\2 #{do_translation("#{@watchers[message.target.pk][message.source.pk]}en", message.message)}"
55
+ elsif(message.message =~ /^(\S+)[:,]/)
56
+ Logger.log("Matched a nick: #{$1}")
57
+ nick = Nick.filter(:nick => $1).first
58
+ return unless nick
59
+ if(@watchers[message.target.pk].has_key?(nick.pk))
60
+ reply message.replyto, "\2(#{do_translation("en|#{@watchers[message.target.pk][nick.pk]}", 'translation')})\2 #{do_translation("en|#{@watchers[message.target.pk][nick.pk]}", message.message)}"
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def do_translation(langs, text)
69
+ if(@cache.has_key?(langs) && @cache[langs].has_key?(text))
70
+ return @cache[langs][text]
71
+ end
72
+ connection = Net::HTTP.new('babelfish.altavista.com', 80)
73
+ response = connection.request_get("/tr?tt=urltext&trtext=#{CGI::escape(text)}&lp=#{langs.gsub(/\|/, '_')}", nil)
74
+ response.value()
75
+ if response.body.gsub(/[\r\n]/, '') =~ /<div style=padding:10px;>(.+?)<\/div>/
76
+ result = $1
77
+ if(text.length < 15)
78
+ @cache[langs] = {} unless @cache.has_key?(langs)
79
+ @cache[langs][text] = result
80
+ end
81
+ return result.gsub(/\s+/, ' ')
82
+ else
83
+ raise "Failed to extract translation"
84
+ end
85
+ end
86
+
87
+ def hook
88
+ if(@watchers.size > 0)
89
+ @pipeline.hook(self, :listener, :Incoming_Privmsg)
90
+ else
91
+ @pipeline.unhook(self, :listener, :Incoming_Privmsg)
92
+ end
93
+ end
94
+
95
+ end
@@ -0,0 +1,55 @@
1
+ class Weather < ModSpox::Plugin
2
+
3
+ include Models
4
+
5
+ def initialize(pipeline)
6
+ super
7
+ Signature.find_or_create(:signature => 'weather (\d+)', :plugin => name, :method => 'weather',
8
+ :description => 'Show weather for given zipcode').params = [:zipcode]
9
+ end
10
+
11
+ def weather(message, params)
12
+ connection = Net::HTTP.new('www.weather.com', 80)
13
+ response = connection.request_get("/weather/local/#{params[:zipcode]}?lswe=96094&lwsa=WeatherLocalUndeclared&from=whatwhere", nil)
14
+ begin
15
+ response.value
16
+ page = response.body.gsub(/[\r\n]/, ' ')
17
+ if page =~ /R for (.+?) \(.+?>([0-9]+)&.+?Like<BR> ([0-9]+)&.+?UV In.+?TextA">(.+?)\s*<.+?Wind.+?TextA">(.+?)\s*<.+?idity.+?TextA">(.+?)\s*<.+?ssure.+?TextA">(.+?)\s*<.+?oint.+?TextA">(.+?)\s*<.+?bility.+?TextA">(.+?)\s*</
18
+ location = $1
19
+ curtemp = $2
20
+ feeltemp = $3
21
+ uv = $4
22
+ wind = $5
23
+ humid = $6
24
+ pressure = $7
25
+ dewpoint = $8
26
+ visibility = $9
27
+ future = Array.new
28
+ i = 0
29
+ page.scan(/.+?ialLink11"><B>(.+?)</).each{|match| future.push(match[0])}
30
+ page.scan(/font.+?lueFont10">([^<].+?)<\/nobr>/).each{|match|
31
+ future[i] << "|#{match[0].gsub(/<.+?>/, '').gsub(/&.+?;/, '').gsub(/\s+/, ' ')}"
32
+ i += 1
33
+ }
34
+ i = 0
35
+ for item in future do
36
+ if item =~ /^([^\|]+)\|(.+)$/
37
+ future[i] = "\2#{$1}:\2 #{$2}"
38
+ else
39
+ future.delete_at(i)
40
+ end
41
+ i += 1
42
+ end
43
+ reply message.replyto, "Weather for: \2#{location}\2"
44
+ reply message.replyto, "Current Temp: #{curtemp} - Feels like: #{feeltemp}"
45
+ reply message.replyto, "[UV Index: #{uv.gsub(/&.+?;/, '')}][Wind: #{wind.gsub(/&.+?;/, '')}][Humiditiy: #{humid.gsub(/&.+?;/, '')}][Pressure: #{pressure.gsub(/&.+?;/, '')}][Dew Point: #{dewpoint.gsub(/&.+?;/, '')}][Visibility: #{visibility.gsub(/&.+?;/, '')}]"
46
+ reply message.replyto, future.values_at(0..2).join(' ')
47
+ else
48
+ reply message.replyto, "Failed to retrieve weather data."
49
+ end
50
+ rescue Object => boom
51
+ reply message.replyto, "Error: Received invalid response from server"
52
+ end
53
+ end
54
+
55
+ end
@@ -32,6 +32,8 @@ class Authenticator < ModSpox::Plugin
32
32
  :group_id => group.pk, :description => 'List available authentication groups')
33
33
  Models::Signature.find_or_create(:signature => 'auth group info (\S+)', :plugin => name, :method => 'group_info',
34
34
  :group_id => group.pk, :description => 'List members of given group').params = [:group]
35
+ Models::Signature.find_or_create(:signature => 'groups', :plugin => name, :method => 'show_groups',
36
+ :description => 'Show user groups they are currently a member of')
35
37
  end
36
38
 
37
39
  # message:: ModSpox::Messages::Incoming::Privmsg
@@ -172,6 +174,16 @@ class Authenticator < ModSpox::Plugin
172
174
  end
173
175
  end
174
176
 
177
+ def show_groups(message, params)
178
+ groups = []
179
+ message.source.auth_groups.each{|g| groups << g.name}
180
+ if(groups.empty?)
181
+ reply message.replyto, "You are not currently a member of any groups"
182
+ else
183
+ reply message.replyto, "\2Groups (#{message.source.nick}):\2 #{groups.join(', ')}"
184
+ end
185
+ end
186
+
175
187
  # message:: ModSpox::Messages::Incoming::Privmsg
176
188
  # params:: Signature parameters
177
189
  # Add given nick to authentication group
@@ -14,7 +14,7 @@ class Banner < ModSpox::Plugin
14
14
  :description => 'Kickban given nick from given channel for given number of seconds').params = [:nick, :channel, :time, :message]
15
15
  Signature.find_or_create(:signature => 'banmask (\S+) (\S+) (\d+) ?(.+)?', :plugin => name, :method => 'message_mask_ban', :group_id => admin.pk,
16
16
  :description => 'Kickban given mask from given channel for given number of seconds providing an optional message'
17
- ).params = [:mask, :message, :time, :channel]
17
+ ).params = [:mask, :channel, :time, :message]
18
18
  Signature.find_or_create(:signature => 'banmask list', :plugin => name, :method => 'mask_list', :group_id => admin.pk,
19
19
  :description => 'List all currently active banmasks')
20
20
  Signature.find_or_create(:signature => 'banmask remove (\d+)', :plugin => name, :method => 'mask_remove', :group_id => admin.pk,
@@ -83,9 +83,9 @@ class Banner < ModSpox::Plugin
83
83
  raise Exceptions::InvalidType.new("Nick given is not a nick model") unless nick.is_a?(Models::Nick)
84
84
  raise Exceptions::InvalidType.new("Channel given is not a channel model") unless channel.is_a?(Models::Channel)
85
85
  if(!me.is_op?(channel))
86
- raise Exceptions::NotOperator.new("I am not an operator in #{channel.name}")
86
+ raise NotOperator.new("I am not an operator in #{channel.name}")
87
87
  elsif(!nick.channels.include?(channel))
88
- raise Exceptions::NotInChannel.new("#{nick.nick} is not in channel: #{channel.name}")
88
+ raise NotInChannel.new("#{nick.nick} is not in channel: #{channel.name}")
89
89
  else
90
90
  mask = nick.source.nil? || nick.source.empty? ? "#{nick.nick}!*@*" : "*!*@#{nick.address}"
91
91
  BanRecord.new(:nick_id => nick.pk, :bantime => time.to_i, :remaining => time.to_i,