nadoka 0.8.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.
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