nadoka 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +5 -0
  2. data/ChangeLog.old +1553 -0
  3. data/Gemfile +4 -0
  4. data/README.org +31 -0
  5. data/Rakefile +1 -0
  6. data/bin/nadoka +13 -0
  7. data/lib/rss_check.rb +206 -0
  8. data/lib/tagparts.rb +206 -0
  9. data/nadoka.gemspec +29 -0
  10. data/nadoka.rb +123 -0
  11. data/nadokarc +267 -0
  12. data/ndk/bot.rb +241 -0
  13. data/ndk/client.rb +288 -0
  14. data/ndk/config.rb +571 -0
  15. data/ndk/error.rb +61 -0
  16. data/ndk/logger.rb +311 -0
  17. data/ndk/server.rb +784 -0
  18. data/ndk/server_state.rb +324 -0
  19. data/ndk/version.rb +44 -0
  20. data/plugins/autoawaybot.nb +66 -0
  21. data/plugins/autodumpbot.nb +227 -0
  22. data/plugins/autoop.nb +56 -0
  23. data/plugins/backlogbot.nb +88 -0
  24. data/plugins/checkbot.nb +64 -0
  25. data/plugins/cronbot.nb +20 -0
  26. data/plugins/dictbot.nb +53 -0
  27. data/plugins/drbcl.rb +39 -0
  28. data/plugins/drbot.nb +93 -0
  29. data/plugins/evalbot.nb +49 -0
  30. data/plugins/gonzuibot.nb +41 -0
  31. data/plugins/googlebot.nb +345 -0
  32. data/plugins/identifynickserv.nb +43 -0
  33. data/plugins/mailcheckbot.nb +0 -0
  34. data/plugins/marldiabot.nb +99 -0
  35. data/plugins/messagebot.nb +96 -0
  36. data/plugins/modemanager.nb +150 -0
  37. data/plugins/opensearchbot.nb +156 -0
  38. data/plugins/opshop.nb +23 -0
  39. data/plugins/pastebot.nb +46 -0
  40. data/plugins/roulettebot.nb +33 -0
  41. data/plugins/rss_checkbot.nb +121 -0
  42. data/plugins/samplebot.nb +24 -0
  43. data/plugins/sendpingbot.nb +17 -0
  44. data/plugins/shellbot.nb +59 -0
  45. data/plugins/sixamobot.nb +77 -0
  46. data/plugins/tenkibot.nb +111 -0
  47. data/plugins/timestampbot.nb +62 -0
  48. data/plugins/titlebot.nb +226 -0
  49. data/plugins/translatebot.nb +301 -0
  50. data/plugins/twitterbot.nb +138 -0
  51. data/plugins/weba.nb +209 -0
  52. data/plugins/xibot.nb +113 -0
  53. data/rice/irc.rb +780 -0
  54. metadata +102 -0
@@ -0,0 +1,41 @@
1
+ # gonzui bot
2
+ #
3
+
4
+ require 'open-uri'
5
+ require 'uri'
6
+
7
+ class GonzuiBot < Nadoka::NDK_Bot
8
+ ResultRegexp = %r(>Results <strong\n>(\d+)</strong\n> - <strong\n>(\d+)</strong\n> of <strong\n>(\d+)</strong)
9
+
10
+ def on_privmsg prefix, ch, msg
11
+ if /\Agonzui(?:\:([\w\-\+]+))?>\s*(.+)/ =~ msg
12
+ send_notice ch, "gonzui bot: #{gonzui_result($1, $2)}"
13
+ end
14
+ end
15
+
16
+ EngineURI = {
17
+ 'raa' => 'http://raa.ruby-lang.org/gonzui/',
18
+ 'gnome' => 'http://gonzui.tagonome.org/',
19
+ 'cpan' => 'http://cpansearch.bulknews.net/',
20
+ 'b-src' => 'http://b-src.cbrc.jp/',
21
+ }
22
+
23
+ def gonzui_result engine, key
24
+ engine ||= 'raa'
25
+ key_uri = URI.encode(key)
26
+ engine_uri = EngineURI[engine.downcase]
27
+ return "unknown engine: #{engine}" unless engine_uri
28
+
29
+ url = "#{engine_uri}search?q=#{key_uri}"
30
+ open(url){|f|
31
+ result = f.read
32
+ if ResultRegexp =~ result
33
+ "#{$3} for #{key} - #{url}"
34
+ else
35
+ "#{key} - not found in #{engine}"
36
+ end
37
+ }
38
+
39
+ end
40
+ end
41
+
@@ -0,0 +1,345 @@
1
+ # -*-ruby-*-
2
+ #
3
+ # Copyright (c) 2004-2005 SASADA Koichi <ko1 at atdot.net>
4
+ # Copyright (c) 2009, 2010 Kazuhiro NISHIYAMA
5
+ #
6
+ # This program is free software with ABSOLUTELY NO WARRANTY.
7
+ # You can re-distribute and/or modify this program under
8
+ # the same terms of the Ruby's license.
9
+ #
10
+ #
11
+ # $Id$
12
+ #
13
+
14
+ =begin
15
+
16
+ == Usage with irc client
17
+
18
+ google> keyword
19
+ -> search keyword by google with default search langage
20
+
21
+ google:[lang]> keyword
22
+ -> search keyword by google with [lang] langage
23
+
24
+ googlec> k1 k2 k3 k4 k5(max 5 words)
25
+ -> search and show each hit count
26
+
27
+ googlec> k1 k2 k3 k4 k5(max 5 words)
28
+ -> search and show each hit count with default count language
29
+
30
+ googlec:[lang]> k1 k2 k3 k4 k5(max 5 words)
31
+ -> search and show each hit count with [lang] langage
32
+
33
+
34
+ == Configuration:
35
+
36
+ BotConfig = [
37
+ {
38
+ :name => :GoogleBot,
39
+ :ch => /.*/,
40
+ :headers => {
41
+ #"User-Agent" => "Ruby/#{RUBY_VERSION}",
42
+ 'Referer' => 'https://github.com/nadoka/nadoka',
43
+ },
44
+ # Register URL at http://code.google.com/intl/ja/apis/ajaxsearch/signup.html
45
+ # and set your URL to :referer and your API key to :api_key if you want.
46
+ :api_key => nil,
47
+ :googlec_maxwords => 5,
48
+ :search_default_lang => 'ja',
49
+ :count_default_lang => '',
50
+ :ch_kcode => :tojis,
51
+ },
52
+ ]
53
+
54
+ =end
55
+
56
+
57
+ require 'iconv'
58
+ require 'kconv'
59
+ require 'shellwords'
60
+ require 'cgi'
61
+ require 'open-uri'
62
+ begin
63
+ require 'json'
64
+ rescue LoadError
65
+ require 'rubygems'
66
+ require 'json'
67
+ end
68
+
69
+ class GoogleBot < Nadoka::NDK_Bot
70
+ def bot_initialize
71
+ bot_init_utils
72
+
73
+ @search_default_lang = (@bot_config[:search_default_lang] || 'ja').sub(/^lang_/, '')
74
+ @googlec_maxwords = @bot_config[:googlec_maxwords] || 5
75
+ @count_default_lang = (@bot_config[:count_default_lang] || '').sub(/^lang_/, '')
76
+ @headers = @bot_config.fetch(:headers, {})
77
+ @api_key = @bot_config[:api_key]
78
+ @uri_slog = @bot_config.fetch(:uri_slog, false)
79
+ @ch_kcode = @bot_config.fetch(:ch_kcode, :tojis)
80
+ end
81
+
82
+ def on_privmsg prefix, ch, msg
83
+ return unless @available_channel === ch
84
+ return if same_bot?(ch)
85
+ if response = dispatch_command(msg)
86
+ send_notice(ch, response)
87
+ end
88
+ end
89
+
90
+ SEARCHER = %w!web calc code local video blogs news books images ime imed patent suggest!.freeze
91
+ SEARCHER_RE = Regexp.new("(?:" + SEARCHER.join('|') + ")").freeze
92
+
93
+ def search_searcher key
94
+ SEARCHER.each{|searcher|
95
+ if /\A#{key}/ =~ searcher
96
+ return searcher
97
+ end
98
+ }; nil
99
+ end
100
+
101
+ def dispatch_command msg
102
+ begin
103
+ case msg
104
+ when /^goo(o*)gle( #{SEARCHER_RE})?(:.*?)?>\s*(.+)/o, /^gu(u*)guru(#{SEARCHER_RE})?(:.+)?>\s*(.+)/o
105
+ "goo#{$1}gle#{$2} bot#{$3}: #{search($1.length, $3, $4, $2)}"
106
+ when /^googlec( #{SEARCHER_RE})?(:.*?)?>\s*(.+)/o
107
+ "googlec#{$1} bot#{$2}: #{googlec($1, $3, $2)}"
108
+ when /^g(\w+)?(:.*?)?>\s*(.+)/
109
+ searcher = $1 ? search_searcher($1) : 'web'
110
+ "google #{searcher} bot#{$2}: #{search(0, $2, $3, searcher)}" if searcher
111
+ end
112
+ rescue Exception => e
113
+ @manager.ndk_error e
114
+ "google bot: #{e.class} (#{e.message} @ #{e.backtrace[0]})"
115
+ end
116
+ end
117
+
118
+ def do_search word, cnt, lang, searcher='web'
119
+ i = 0
120
+ begin
121
+ uri = "http://ajax.googleapis.com/ajax/services/search/"
122
+ uri << searcher
123
+ uri << "?v=1.0&q="
124
+ uri << CGI.escape(word)
125
+ if @api_key
126
+ uri << "&key=#{CGI.escape(@api_key)}"
127
+ end
128
+ cnt = cnt.to_i
129
+ if cnt > 0
130
+ uri << "&start=#{cnt.to_i}"
131
+ end
132
+ if lang
133
+ uri << "&hl=#{CGI.escape(lang)}"
134
+ if searcher == 'web'
135
+ uri << "&lr=lang_#{CGI.escape(lang)}"
136
+ end
137
+ end
138
+ @logger.slog "GoogleBot: #{uri}" if @uri_slog
139
+
140
+ result = open(uri, @headers) do |f|
141
+ JSON.parse(f.read)
142
+ end
143
+ def result.estimatedTotalResultsCount
144
+ self["responseData"]["cursor"]["estimatedResultCount"]
145
+ end
146
+ result
147
+ rescue Exception => e
148
+ retry if (i+=1) < 5
149
+ raise
150
+ end
151
+ end
152
+
153
+ def api_search word, cnt, lang, searcher
154
+ result = do_search word, cnt, lang, searcher
155
+
156
+ count = result.estimatedTotalResultsCount.to_i
157
+
158
+ if count > 0
159
+ count = count.to_s.gsub(/(\d)(?=\d{3}+$)/, '\\1,')
160
+ url = title = ''
161
+
162
+ e = result["responseData"]["results"][0]
163
+ url = e['unescapedUrl'] || e['url'] || e['postUrl']
164
+ title = show_char_code_and_erase_tag(e['titleNoFormatting'])
165
+ "#{title} - #{url} (and #{count} hit#{(count.to_i > 1) ? 's' : ''})"
166
+ else
167
+ "no match"
168
+ end
169
+ end
170
+
171
+ def google_calc exp
172
+ @logger.slog("google_calc<#{exp.dump}")
173
+ uri = "http://www.google.co.jp/search?ie=UTF8&oe=UTF-8&q=#{CGI.escape(exp)}"
174
+ html = open(uri, @headers) do |f|
175
+ f.read
176
+ end
177
+ if /class=r [^<>]+><b>(.+?)<\/b>/u =~ html
178
+ result = $1
179
+ # @logger.slog("google_calc>#{result.dump}")
180
+ result.gsub!(/<sup>(.+?)<\/sup>/u) { "^(#{$1})" }
181
+ result.gsub!(/<.+?>/u, '')
182
+ result.gsub!(/&\#215;/u, "\303\227")
183
+ result.send(@ch_kcode) # NKF.nkf('-j', result)
184
+ else
185
+ "response error"
186
+ end
187
+ rescue Exception
188
+ $!.to_s
189
+ end
190
+
191
+ def google_suggest(word, lang)
192
+ uri = "http://suggestqueries.google.com/complete/search?output=firefox"
193
+ uri << "&q="
194
+ uri << CGI.escape(word)
195
+ if lang
196
+ uri << "&hl=#{CGI.escape(lang)}"
197
+ end
198
+ @logger.slog "GoogleBot: #{uri}" if @uri_slog
199
+
200
+ result = open(uri, @headers) do |f|
201
+ JSON.parse(f.read)
202
+ end
203
+ result[1].join(", ").send(@ch_kcode)
204
+ end
205
+
206
+ def google_code key
207
+ return "http://google.com/codesearch#search/&q=#{CGI.escape(key)}&ct=os"
208
+ end
209
+
210
+ if defined?(URI.encode_www_form)
211
+ def encode_www_form(enum)
212
+ URI.encode_www_form(enum)
213
+ end
214
+ else
215
+ def encode_www_form(enum)
216
+ enum.map do |k, v|
217
+ "#{URI.encode(k)}=#{URI.encode(v)}"
218
+ end.join('&')
219
+ end
220
+ end
221
+
222
+ # see http://www.google.com/intl/ja/ime/cgiapi.html
223
+ def google_ime text, d=false
224
+ url = 'http://www.google.com/transliterate?'
225
+ url << encode_www_form('langpair' => 'ja-Hira|ja', 'text' => text)
226
+ data = open(url,@headers){|f|
227
+ # TODO: gsub fix invalid JSON, should remove after fix response
228
+ # see http://www.google.com/support/forum/p/ime/thread?tid=06501c8b7a16add3&hl=ja
229
+ JSON.parse(f.read.gsub(/,(?=\n\])/,''))
230
+ }
231
+ if d
232
+ result = data.map do |org, candidates|
233
+ "#{org}=#{candidates.join('/')}"
234
+ end.join(' ')
235
+ else
236
+ result = data.map do |org, candidates|
237
+ candidates[0]
238
+ end.join('')
239
+ end
240
+ show_char_code_and_erase_tag(result)
241
+ rescue Exception
242
+ $!.to_s[/.+/] # first line
243
+ end
244
+
245
+ def search cnt, lang, word, searcher=nil
246
+ lang = lang_check(lang)
247
+ searcher = searcher_check(searcher)
248
+ word = search_char_code(word)
249
+
250
+ case searcher
251
+ when 'code'
252
+ google_code word
253
+ when 'calc'
254
+ google_calc word
255
+ when 'ime'
256
+ google_ime word
257
+ when 'imed'
258
+ google_ime word, true
259
+ when 'suggest'
260
+ google_suggest word, lang
261
+ else
262
+ api_search word, cnt, lang, searcher
263
+ end
264
+ end
265
+
266
+ def googlec lang, word, searcher=nil
267
+ lang = lang_check(lang, @count_default_lang)
268
+ searcher = searcher_check(searcher)
269
+ words = Shellwords.shellwords(word).map{|e| "\"#{e}\""}
270
+ return 'too many options' if words.size > @googlec_maxwords
271
+
272
+ words.map{|rw|
273
+ w = search_char_code(rw)
274
+ result = do_search "'#{w}'", 0, lang, searcher
275
+ "#{rw}(#{result.estimatedTotalResultsCount.to_s.gsub(/(\d)(?=\d{3}+$)/, '\\1,')})"
276
+ }.join(', ')
277
+ end
278
+
279
+ def erase_tag str
280
+ CGI.unescapeHTML(str.gsub(/\<.+?\>/, ''))
281
+ end
282
+
283
+ def lang_check lang, default = @search_default_lang
284
+ if !lang
285
+ @search_default_lang
286
+ else
287
+ lang = lang[1..-1]
288
+ if lang.empty?
289
+ nil
290
+ elsif /^lang_/ =~ lang
291
+ lang.sub(/^lang_/, '')
292
+ else
293
+ lang
294
+ end
295
+ end
296
+ end
297
+
298
+ def searcher_check searcher
299
+ if !searcher
300
+ 'web'
301
+ else
302
+ searcher = searcher.strip
303
+ if SEARCHER.include?(searcher)
304
+ searcher
305
+ else
306
+ 'web'
307
+ end
308
+ end
309
+ end
310
+
311
+ def show_char_code_and_erase_tag str
312
+ return CGI.unescapeHTML(erase_tag(str.toeuc)).send(@ch_kcode)
313
+
314
+ case $KCODE
315
+ when 'EUC', 'SJIS'
316
+ CGI.unescapeHTML(str.gsub(/\<.+?\>/, '')).send(@ch_kcode)
317
+ when 'NONE', 'UTF-8'
318
+ begin
319
+ str = Iconv.conv("EUC-JP", "UTF-8", str)
320
+ CGI.unescapeHTML(str.gsub(/\<.+?\>/, '')).send(@ch_kcode)
321
+ rescue => e
322
+ "(char code problem: #{e.class}[#{e.message.dump}])"
323
+ end
324
+ else
325
+ str
326
+ end
327
+ end
328
+
329
+ def search_char_code str
330
+ case $KCODE
331
+ when 'EUC', 'SJIS'
332
+ str.toeuc
333
+ when 'NONE'
334
+ begin
335
+ Iconv.conv("UTF-8", "EUC-JP", str.toeuc)
336
+ rescue => e
337
+ raise "(char code problem: #{e.class})"
338
+ end
339
+ when 'UTF-8'
340
+ str
341
+ else
342
+ raise
343
+ end
344
+ end
345
+ end
@@ -0,0 +1,43 @@
1
+ # -*-ruby-*-
2
+ #
3
+ # Copyright (c) 2009 Kazuhiro NISHIYAMA
4
+ #
5
+ # This program is free software with ABSOLUTELY NO WARRANTY.
6
+ # You can re-distribute and/or modify this program under
7
+ # the same terms of the Ruby's license.
8
+ #
9
+
10
+ =begin
11
+
12
+ == Abstract
13
+
14
+ send IDENTIFY to NickServ.
15
+
16
+ == Configuration
17
+
18
+ BotConfig << {
19
+ :name => :IdentifyNickServ,
20
+ #:nickserv => "NickServ",
21
+ :nick => "nadoka",
22
+ :pass => "hoge",
23
+ }
24
+
25
+ =end
26
+
27
+ class IdentifyNickServ < Nadoka::NDK_Bot
28
+ def bot_initialize
29
+ @nickserv = @bot_config.fetch(:nickserv, "NickServ")
30
+ @nick = @bot_config.fetch(:nick, false)
31
+ @pass = @bot_config.fetch(:pass, false)
32
+ end
33
+
34
+ def on_server_connected(*args)
35
+ if @pass
36
+ if @nick
37
+ send_privmsg @nickserv, "IDENTIFY #{@nick} #{@pass}"
38
+ else
39
+ send_privmsg @nickserv, "IDENTIFY #{@pass}"
40
+ end
41
+ end
42
+ end
43
+ end
Binary file
@@ -0,0 +1,99 @@
1
+ # -*-ruby-*-
2
+ require 'cgi'
3
+ require 'net/http'
4
+ require 'rexml/document'
5
+ require 'uri'
6
+ require 'time'
7
+ require 'json'
8
+
9
+ class MarldiaBot < Nadoka::NDK_Bot
10
+ def bot_initialize
11
+ @chats = @bot_config.fetch(:chats, nil)
12
+ @proxy_addr = @bot_config.fetch(:proxy_addr, nil)
13
+ @proxy_port = @bot_config.fetch(:proxy_port, 80)
14
+
15
+ @chs = @chats.keys
16
+ @chats.each_value do |chat|
17
+ chat[:uri] = URI(chat[:url])
18
+ chat[:query] = 'type=xml&' + chat[:data].
19
+ map{|k,v|"#{CGI.escape k.to_s}=#{CGI.escape v.to_s}"}.join('&')
20
+ req = Net::HTTP::Get.new(chat[:uri].path+ '?' + chat[:query])
21
+ chat[:req] = req
22
+ end
23
+ end
24
+
25
+ def bot_state
26
+ "<#{self.class.to_s}>"
27
+ end
28
+
29
+ def slog(msg, nostamp = false)
30
+ current_method = caller.first[/:in \`(.*?)\'/, 1].to_s
31
+ msg.each_line do |line|
32
+ @logger.slog "#{self.class.to_s}##{current_method} #{line}", nostamp
33
+ end
34
+ end
35
+
36
+ def on_timer(t)
37
+ @chats.each_pair do |ch, chat|
38
+ uri = chat[:uri]
39
+ current_id = chat[:current_id]
40
+ body = nil
41
+ messages = []
42
+ Net::HTTP::Proxy(@proxy_addr, @proxy_port).start(uri.host, uri.port) do |http|
43
+ body = http.request(chat[:req]).body
44
+ end
45
+ doc = REXML::Document.new(body)
46
+ doc.each_element('feed/entry/log/article') do |art|
47
+ id = art.text('id')
48
+ break if current_id == id
49
+ time = Time.parse(art.text('updated'))
50
+ next if time + 10 * 60 < Time.now
51
+ user = art.text('author/name')
52
+ text = CGI.unescapeHTML(art.text('body')).gsub(/\n|<.*?>/," ")
53
+ text = text[/^[\w\W]{0,100}/u]
54
+ text.gsub!(/([\w\W])/u){|c|c == "\xA0" ? " " : c}
55
+ messages << "#{time.strftime('%H:%M')} #{user}: #{text}"
56
+ end
57
+ messages.reverse_each do |message|
58
+ send_notice ch, message
59
+ sleep 0.5
60
+ end
61
+ chat[:current_id] = doc.text('/feed/entry/log/article/id')
62
+ end
63
+ rescue Errno::ETIMEDOUT, Timeout::Error, SocketError
64
+ rescue Errno::ECONNRESET => err
65
+ slog "%s: %s (%s)" % [err.backtrace[0], err.message, err.class]
66
+ rescue Exception => err
67
+ detail = ("%s: %s (%s)\n" % [err.backtrace[0], err.message, err.class]) + err.backtrace[1..-1].join("\n")
68
+ slog "Exception\n#{detail}"
69
+ end
70
+
71
+ def send_marldia(ch, message)
72
+ chat = @chats[ch]
73
+ uri = chat[:uri]
74
+ req = Net::HTTP::Post.new(uri.path)
75
+ req.body = chat[:query] + '&type=xml&body=' + CGI.escape(message)
76
+
77
+ Net::HTTP::Proxy(@proxy_addr, @proxy_port).start(uri.host, uri.port) {|http|
78
+ http.read_timeout = @timeout
79
+ res = http.request(req)
80
+ @logger.dlog res.code
81
+ }
82
+ return true
83
+ rescue Errno::ECONNRESET => err
84
+ slog "%s: %s (%s)" % [err.backtrace[0], err.message, err.class]
85
+ rescue Exception => err
86
+ detail = ("%s: %s (%s)\n" % [err.backtrace[0], err.message, err.class]) +
87
+ err.backtrace[1..-1].join("\n")
88
+ slog "Exception\n#{detail}"
89
+ return false
90
+ end
91
+
92
+ def on_client_privmsg(client, ch, message)
93
+ ch.downcase!
94
+ return unless @chs.include?(ch)
95
+ msg = send_marldia(ch, message) ? 'sent to marldia: ' : 'marldia send faild: '
96
+ msg << message
97
+ slog msg
98
+ end
99
+ end