nadoka 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 909f58c2eedf5961fac9c60f3a53be75ab720ece
4
- data.tar.gz: e9ac1b85e70e3c97ebfd3bfc90106bb67dd75ed1
3
+ metadata.gz: f4e8bf1dc42176c81751ad4d20bd178cda8b2ee5
4
+ data.tar.gz: be2444c5552d37730987a6ddae9b12a1c5b1acf3
5
5
  SHA512:
6
- metadata.gz: ac27e1fdae6661c7ffe5f3dd91cfb1f852d0e90e203bccbf9d789ecdde4dea1cad5270913cb0fd4b75405ec0fde265a7d074f56dbb75e7e43cf152f07c28dbbd
7
- data.tar.gz: 82392cf7d9fc592ca7650e86a802c0f5012725a31c6216a877c4b91b5f54be4119a4340ab4c63269c634a3f94c84ba196446d13cd6246b87e191b7535c101412
6
+ metadata.gz: 549e67f5bc2f14276fb5fc53fe92e06bcc9ed567230bd17fcbe0bff71633941e427d9529a4140d70f3bd0626d6578de98ebf441c660ebbe0317391732cb39007
7
+ data.tar.gz: 163d7ef0eb1462044f393da222d377d86fbfe4831ab834796dd2f07cbffb15a0e6dab8699b7adfae8efdfb47044f1321ba1fc6e58cf5df963669e6165c16af61
data/README.md CHANGED
@@ -5,6 +5,7 @@ Written by SASADA Koichi <ko1 at atdot.net>
5
5
  [![Gem Version](https://badge.fury.io/rb/nadoka.png)](http://badge.fury.io/rb/nadoka)
6
6
  [![Build Status](https://travis-ci.org/nadoka/nadoka.png?branch=master)](https://travis-ci.org/nadoka/nadoka)
7
7
  [![Code Climate](https://codeclimate.com/github/nadoka/nadoka.png)](https://codeclimate.com/github/nadoka/nadoka)
8
+ [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/nadoka/nadoka/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
8
9
 
9
10
  ## What's this?
10
11
 
@@ -22,5 +23,4 @@ You can do with this software:
22
23
  See Web pages:
23
24
 
24
25
  - https://github.com/nadoka/nadoka
25
- - http://rubyforge.org/projects/nadoka/
26
26
  - http://www.atdot.net/nadoka/
@@ -16,8 +16,6 @@ Gem::Specification.new do |s|
16
16
  older proxy written in Perl.
17
17
  }.tr_s(" \n", " ").strip
18
18
 
19
- s.rubyforge_project = "nadoka"
20
-
21
19
  s.files = `git ls-files`.split("\n")
22
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
data/nadoka.rb CHANGED
@@ -45,6 +45,7 @@ unless defined? Process.daemon
45
45
  end
46
46
 
47
47
  rcfile = nil
48
+ pidfile = nil
48
49
  daemon = false
49
50
  optparse = OptionParser.new{|opts|
50
51
  opts.banner = "Usage: ruby #{$0} [options]"
@@ -70,6 +71,9 @@ optparse = OptionParser.new{|opts|
70
71
  opts.on("--daemon", "run as daemon"){
71
72
  daemon = true
72
73
  }
74
+ opts.on("--pid [PIDFILE]", "Put process pid into PIDFILE"){|f|
75
+ pidfile = f
76
+ }
73
77
 
74
78
  opts.separator ""
75
79
  opts.separator "Common options:"
@@ -95,6 +99,10 @@ if daemon
95
99
  Process.daemon
96
100
  end
97
101
 
102
+ if pidfile
103
+ open(pidfile, "w") {|f| f.puts Process.pid }
104
+ end
105
+
98
106
  begin
99
107
  GC.start
100
108
  Nadoka::NDK_Server.new(rcfile).start
@@ -111,5 +119,9 @@ rescue Exception => e
111
119
  }
112
120
  end
113
121
 
122
+ if pidfile
123
+ File.unlink(pidfile)
124
+ end
125
+
114
126
  end
115
127
 
@@ -506,9 +506,10 @@ module Nadoka
506
506
  else
507
507
  if m = msgobj[$1.intern]
508
508
  if m.respond_to?(:force_encoding)
509
- m.force_encoding(Encoding::ASCII_8BIT)
509
+ m.dup.force_encoding(Encoding::ASCII_8BIT)
510
+ else
511
+ m
510
512
  end
511
- m
512
513
  else
513
514
  "!!unknown attribute: #{$1}!!"
514
515
  end
@@ -11,7 +11,7 @@
11
11
  #
12
12
 
13
13
  module Nadoka
14
- VERSION = '0.8.6'
14
+ VERSION = '0.9.0'
15
15
  NDK_Version = VERSION.dup
16
16
  NDK_Created = Time.now
17
17
 
@@ -0,0 +1,93 @@
1
+ # -*-ruby-*-
2
+ require 'ffi/clang'
3
+ require 'tempfile'
4
+
5
+ # dependency:
6
+ # * ffi-clang.gem
7
+ # * libclang
8
+ # * lang/clang34 in FreeBSD ports
9
+ # * llvm of homebrew
10
+ #
11
+ # example setting:
12
+ # {
13
+ # :name => :CRubyBot,
14
+ # :channels => [ "#ruby-ja" ],
15
+ # :ruby_srcdir => '/path/of/ruby/src',
16
+ # }
17
+ #
18
+ class CRubyBot < Nadoka::NDK_Bot
19
+ def bot_initialize
20
+ if @bot_config.key?(:channels)
21
+ channels = '\A(?:' + @bot_config[:channels].collect{|ch|
22
+ Regexp.quote(ch)
23
+ }.join('|') + ')\z'
24
+ @available_channel = Regexp.compile(channels)
25
+ else
26
+ @available_channel = @bot_config.fetch(:ch, //)
27
+ end
28
+ unless @ruby_srcdir = @bot_config[:ruby_srcdir]
29
+ raise "ruby_srcdir is not specified"
30
+ end
31
+ unless Dir.exist?(@ruby_srcdir)
32
+ raise "ruby_srcdir(#{@ruby_srcdir}) does not exist"
33
+ end
34
+ @ruby_srcdir << '/' if @ruby_srcdir[-1] != '/'
35
+ @translation_unit = nil
36
+ @translation_unit_rev = nil
37
+ end
38
+
39
+ def bot_state
40
+ "<#{self.class.to_s}>"
41
+ end
42
+
43
+ def translation_unit
44
+ rev = IO.read("#@ruby_srcdir/revision.h").to_s[/\d+/].to_i
45
+ return @translation_unit if @translation_unit_rev == rev
46
+ ary = Dir[File.join @ruby_srcdir, "*.c"]
47
+ f = Tempfile.open(["crubybot-ruby", ".c"])
48
+ ary = Dir[File.join @ruby_srcdir, "*.c"]
49
+ ary << File.join(@ruby_srcdir, "win32/win32.c")
50
+ ary.each do |fn|
51
+ f.puts %[#include "#{fn}"]
52
+ end
53
+ f.flush
54
+ index = FFI::Clang::Index.new
55
+ @translation_unit = index.parse_translation_unit(f.path, "-I#{@ruby_srcdir}/include")
56
+ @translation_unit_rev = rev
57
+ f.close(true)
58
+ @translation_unit
59
+ end
60
+
61
+ def find_def(name, kind)
62
+ translation_unit.cursor.visit_children do |cursor, parent|
63
+ if cursor.kind == kind && cursor.definition? && cursor.spelling == name
64
+ loc = cursor.location
65
+ path = loc.file
66
+ if path.start_with?(@ruby_srcdir)
67
+ return [path[@ruby_srcdir.size..-1], loc.line]
68
+ end
69
+ end
70
+ next :recurse
71
+ end
72
+ return nil
73
+ end
74
+
75
+ def on_privmsg(client, ch, message)
76
+ return unless @available_channel === ch
77
+ case message
78
+ when /\A\S*(struct|fun\w*)\s+(\w+)/
79
+ kind1 = $1
80
+ name = $2
81
+ kind = case kind1
82
+ when "struct"
83
+ :cursor_struct
84
+ when /\Afun/
85
+ :cursor_function
86
+ end
87
+ path, line = find_def(name, kind)
88
+ return unless path
89
+ send_notice(ch, "crubybot: #{kind1} #{name} at " \
90
+ "https://github.com/ruby/ruby/blob/trunk/#{path}#L#{line}")
91
+ end
92
+ end
93
+ end
@@ -68,6 +68,23 @@ rescue LoadError
68
68
  require 'json'
69
69
  end
70
70
 
71
+ if __FILE__ == $0
72
+ # for test
73
+ module Nadoka
74
+ class NDK_Bot
75
+ def bot_init_utils
76
+ end
77
+ def initialize
78
+ @bot_config = Hash.new
79
+ @bot_config[:headers] = {
80
+ "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:27.0) Gecko/20100101 Firefox/27.0",
81
+ }
82
+ bot_initialize
83
+ end
84
+ end
85
+ end
86
+ end
87
+
71
88
  class GoogleBot < Nadoka::NDK_Bot
72
89
  def bot_initialize
73
90
  bot_init_utils
@@ -174,10 +191,11 @@ class GoogleBot < Nadoka::NDK_Bot
174
191
 
175
192
  def google_calc exp
176
193
  @logger.slog("google_calc<#{exp.dump}")
177
- uri = "http://www.google.co.jp/search?ie=UTF8&oe=UTF-8&q=#{CGI.escape(exp)}"
194
+ uri = "https://www.google.co.jp/search?ie=UTF8&oe=UTF-8&q=#{CGI.escape(exp)}"
178
195
  html = open(uri, @headers) do |f|
179
196
  f.read
180
197
  end
198
+ open("g.html", "wb") { |f| f.write html } if $DEBUG
181
199
  if /class=r [^<>]+><b>(.+?)<\/b>/u =~ html
182
200
  result = $1
183
201
  # @logger.slog("google_calc>#{result.dump}")
@@ -194,7 +212,7 @@ class GoogleBot < Nadoka::NDK_Bot
194
212
  result.gsub!(/&nbsp;/u, " ")
195
213
  result.gsub!(/\s+/, " ")
196
214
  return result
197
- elsif /<div class="leg_calc [^<>]*><div[^<>]*>([^<>]*)<\/div><div[^<>]*>([^<>]*)</u =~ html
215
+ elsif /<div class="leg_calc[^<>]*>(?:<div[^<>]*>)+([^<>]+)<\/div><div[^<>]*>([^<>]+)</u =~ html
198
216
  result = "#{$1} #{$2}"
199
217
  #@logger.slog("google_calc>#{result.dump}")
200
218
  return result
@@ -376,3 +394,23 @@ class GoogleBot < Nadoka::NDK_Bot
376
394
  end
377
395
  end
378
396
  end
397
+
398
+ if __FILE__ == $0
399
+ if ARGV.empty?
400
+ puts "ad hoc test usage:"
401
+ puts " ruby -vd plugins/googlebot.nb 'gc>1+1'"
402
+ puts " ruby -vd plugins/googlebot.nb 'gc>1ドルを円で'"
403
+ end
404
+ require 'logger'
405
+ google_bot = GoogleBot.new
406
+ google_bot.instance_eval do
407
+ @logger = Object.new
408
+ def @logger.slog(log)
409
+ STDERR.puts "slog>#{log}"
410
+ end
411
+ end
412
+ ARGV.each do |arg|
413
+ puts arg
414
+ puts google_bot.dispatch_command(arg)
415
+ end
416
+ end
@@ -25,7 +25,7 @@
25
25
  :name => :OpenSearchBot,
26
26
  :bot_name => 'bing',
27
27
  :ch => //,
28
- :referer => 'http://rubyforge.org/projects/nadoka/',
28
+ :referer => 'https://github.com/nadoka/nadoka',
29
29
  :ch_kcode => :jis,
30
30
  :html => 'http://www.bing.com/search?q={searchTerms}',
31
31
  :rss => 'http://api.search.live.com/rss.aspx?source=web&query={searchTerms}'
@@ -37,7 +37,7 @@
37
37
  :bot_name => 'googlecode',
38
38
  :ch => //,
39
39
  :ch_kcode => :jis,
40
- :referer => 'http://rubyforge.org/projects/nadoka/',
40
+ :referer => 'https://github.com/nadoka/nadoka',
41
41
  # Google Code Search Data API (Deprecated)
42
42
  # http://code.google.com/intl/ja/apis/codesearch/docs/2.0/developers_guide.h
43
43
  tml
@@ -49,7 +49,7 @@ tml
49
49
  :name => :OpenSearchBot,
50
50
  :bot_name => 'koders',
51
51
  :ch => //,
52
- :referer => 'http://rubyforge.org/projects/nadoka/',
52
+ :referer => 'https://github.com/nadoka/nadoka',
53
53
  :ch_kcode => :jis,
54
54
  # http://www.koders.com/search/KodersDescriptionOS1_1.xml
55
55
  :html => 'http://www.koders.com/?s={searchTerms}',
@@ -67,7 +67,7 @@ class OpenSearch
67
67
  def initialize(options)
68
68
  @html = options[:html]
69
69
  @rss = options[:rss]
70
- @referer = options[:referer] || 'http://rubyforge.org/projects/nadoka/'
70
+ @referer = options[:referer] || 'https://github.com/nadoka/nadoka'
71
71
  end
72
72
 
73
73
  def result(key)
@@ -89,25 +89,25 @@ end
89
89
  if __FILE__ == $0
90
90
  h = {
91
91
  'bing' => {
92
- :referer => 'http://rubyforge.org/projects/nadoka/',
92
+ :referer => 'https://github.com/nadoka/nadoka',
93
93
  :html => 'http://www.bing.com/search?q={searchTerms}',
94
94
  :rss => 'http://api.search.live.com/rss.aspx?source=web&query={searchTerms}',
95
95
  },
96
96
  'googlecode' => {
97
- :referer => 'http://rubyforge.org/projects/nadoka/',
97
+ :referer => 'https://github.com/nadoka/nadoka',
98
98
  # Google Code Search Data API (Deprecated)
99
99
  # http://code.google.com/intl/ja/apis/codesearch/docs/2.0/developers_guide.html
100
100
  :html => 'http://www.google.com/codesearch?q={searchTerms}',
101
101
  :rss => 'http://www.google.com/codesearch/feeds/search?q={searchTerms}',
102
102
  },
103
103
  'koders' => {
104
- :referer => 'http://rubyforge.org/projects/nadoka/',
104
+ :referer => 'https://github.com/nadoka/nadoka',
105
105
  # http://www.koders.com/search/KodersDescriptionOS1_1.xml
106
106
  :html => 'http://www.koders.com/?s={searchTerms}',
107
107
  :rss => 'http://www.koders.com/?s={searchTerms}&results=code&output=rss&OSversion=1.1',
108
108
  },
109
109
  'youtube' => {
110
- :referer => 'http://rubyforge.org/projects/nadoka/',
110
+ :referer => 'https://github.com/nadoka/nadoka',
111
111
  :html => 'http://www.youtube.com/results?search_query={searchTerms}',
112
112
  :rss => 'http://gdata.youtube.com/feeds/api/videos?q={searchTerms}',
113
113
  },
@@ -33,6 +33,7 @@ rescue LoadError
33
33
  end
34
34
  require 'nkf'
35
35
  require 'open-uri'
36
+ require 'tempfile'
36
37
  require 'timeout'
37
38
  require 'tmpdir'
38
39
 
@@ -148,28 +149,13 @@ module URL2Title
148
149
  title = tweet unless tweet.empty?
149
150
  end
150
151
  end
151
- when /\A(?:github\.com)\z/
152
- # ignore og:title
153
- if title
154
- title = title.gsub(/\xC2\xB8/u, "-")
155
- end
156
152
  else
157
153
  if defined?(::Nokogiri)
158
154
  doc ||= Nokogiri::HTML(body, uri.to_s, 'utf-8')
159
- og_title = doc.xpath("//meta[@property='og:title'][1]/@content")
160
- unless og_title.empty?
161
- # title is escaped string when get by regexp
162
- title = CGI.escapeHTML(og_title.text)
163
- end
164
- elsif /<meta property="og:title" content="(.+?)">/ =~ body
165
- title = $1
166
- end
167
- if defined?(::Nokogiri)
168
- doc ||= Nokogiri::HTML(body, uri.to_s, 'utf-8')
169
- og_title = doc.xpath("//meta[@property='og:title']/@content")
155
+ og_title = doc.xpath("//meta[@property='og:title'][1]/@content").text
170
156
  unless og_title.empty?
171
157
  # title is escaped string when get by regexp
172
- title = CGI.escapeHTML(og_title.text)
158
+ title = CGI.escapeHTML(og_title)
173
159
  end
174
160
  elsif /<meta property="og:title" content="(.+?)">/ =~ body
175
161
  title = $1
@@ -210,6 +196,14 @@ module URL2Title
210
196
  return info
211
197
  else
212
198
  info[:title] = "(unknown image format)"
199
+ temp = Tempfile.new("titlebot", :encoding => 'ascii-8bit')
200
+ begin
201
+ temp.write(body)
202
+ temp.close
203
+ info[:title] = `identify '#{temp.path}'`.sub(/\A#{Regexp.quote(temp.path)}/, '').strip
204
+ ensure
205
+ temp.unlink
206
+ end
213
207
  return info
214
208
  end
215
209
  else
@@ -246,10 +240,14 @@ module URL2Title
246
240
  end
247
241
  end
248
242
 
243
+ def cleanup(title)
244
+ title.gsub(/&nbsp;|&#xA0;|&#160;|\u{A0}/i, ' ')
245
+ end
246
+
249
247
  def url2title(url, headers)
250
248
  url = prepare_url(url)
251
249
  info = get_title(url, headers)
252
- info[:title] = truncate(info[:title])
250
+ info[:title] = truncate(cleanup(info[:title]))
253
251
  info
254
252
  end
255
253
  end
@@ -257,7 +255,7 @@ end
257
255
  if __FILE__ == $0
258
256
  require File.expand_path('../../ndk/version', __FILE__)
259
257
  def u2t(url)
260
- firefox = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/12.0"
258
+ firefox = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:25.0) Gecko/20100101 Firefox/25.0"
261
259
  ua = "NadokaTitleBot/#{Nadoka::VERSION}"
262
260
  headers = {
263
261
  "User-Agent" => "#{firefox} Ruby/#{RUBY_VERSION} #{ua}",
@@ -295,6 +293,7 @@ class TitleBot < Nadoka::NDK_Bot
295
293
  @same_bot = @bot_config.fetch(:same_bot, /(?!)/)
296
294
  @nkf_options = @bot_config.fetch(:nkf, "--oc=CP50221 --ic=UTF-8 --fb-xml")
297
295
  @timeout = @bot_config.fetch(:timeout, 10)
296
+ @skip_nkf_msg = @bot_config.fetch(:skip_nkf_msg, false)
298
297
  @fragment_size_range = @bot_config.fetch(:fragment_size_range, 5..100)
299
298
  @headers = @bot_config.fetch(:headers, {})
300
299
  end
@@ -310,6 +309,7 @@ class TitleBot < Nadoka::NDK_Bot
310
309
  def on_privmsg prefix, ch, msg
311
310
  return unless @available_channel === ch
312
311
 
312
+ msg = NKF.nkf('-w', msg) unless @skip_nkf_msg
313
313
  case msg
314
314
  when /^\S+>/
315
315
  # ignore messages to other bots
@@ -740,6 +740,7 @@ E
740
740
  252,RPL_LUSEROP 253,RPL_LUSERUNKNOWN 254,RPL_LUSERCHANNELS
741
741
  255,RPL_LUSERME 256,RPL_ADMINME 257,RPL_ADMINLOC1
742
742
  258,RPL_ADMINLOC2 259,RPL_ADMINEMAIL 263,RPL_TRYAGAIN
743
+ 265,RPL_LOCALUSERS 266,RPL_GLOBALUSERS
743
744
  401,ERR_NOSUCHNICK 402,ERR_NOSUCHSERVER 403,ERR_NOSUCHCHANNEL
744
745
  404,ERR_CANNOTSENDTOCHAN 405,ERR_TOOMANYCHANNELS
745
746
  406,ERR_WASNOSUCHNICK 407,ERR_TOOMANYTARGETS
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nadoka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.6
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuhiro NISHIYAMA
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-29 00:00:00.000000000 Z
12
+ date: 2014-06-29 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Nadoka is a tool for monitoring and logging IRC conversations and responding
15
15
  to specially formatted requests. You define and customize these responses in Ruby.
@@ -49,6 +49,7 @@ files:
49
49
  - plugins/checkbot.nb
50
50
  - plugins/codesearchbot.nb
51
51
  - plugins/cronbot.nb
52
+ - plugins/crubybot.nb
52
53
  - plugins/dictbot.nb
53
54
  - plugins/drbcl.rb
54
55
  - plugins/drbot.nb
@@ -73,7 +74,6 @@ files:
73
74
  - plugins/tenkibot.nb
74
75
  - plugins/timestampbot.nb
75
76
  - plugins/titlebot.nb
76
- - plugins/translatebot.nb
77
77
  - plugins/twitterbot.nb
78
78
  - plugins/weba.nb
79
79
  - plugins/xibot.nb
@@ -96,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
98
  requirements: []
99
- rubyforge_project: nadoka
99
+ rubyforge_project:
100
100
  rubygems_version: 2.0.3
101
101
  signing_key:
102
102
  specification_version: 4
@@ -1,301 +0,0 @@
1
- # -*-ruby-*-
2
- #
3
- # Copyright (c) 2010 SASADA Koichi <ko1 at atdot.net>
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
- == Usage with irc client
13
-
14
- trans> hello
15
- -> translate hello as a English to Japanese.
16
-
17
- trans:ja> hello
18
- -> ditto.
19
-
20
- trans:en,ja> hello
21
- -> ditto.
22
-
23
- trans:(([lang_from],)[lang_to])> [phrase]
24
- -> translate [phrase] as lang_from to lang_to.
25
-
26
- transj> [phrase]
27
- -> translate to Japanese
28
-
29
- transe> [phrase]
30
- -> translate to English
31
-
32
- == Configuration:
33
-
34
- BotConfig = [
35
- {
36
- :name => :TranslateBot,
37
- :ch => /.*/,
38
- :referer => 'http://rubyforge.org/projects/nadoka/',
39
- # Register URL at http://code.google.com/intl/ja/apis/ajaxsearch/signup.html
40
- # and set your URL to :referer and your API key to :api_key if you want.
41
- :to_lang => 'ja',
42
- :to_lang2 => 'en',
43
- :ch_kcode => :tojis,
44
- },
45
- ]
46
-
47
- =end
48
-
49
- require 'iconv'
50
- require 'kconv'
51
- require 'shellwords'
52
- require 'cgi'
53
- require 'open-uri'
54
- begin
55
- require 'json'
56
- rescue LoadError
57
- require 'rubygems'
58
- require 'json'
59
- end
60
-
61
- class TranslateBot < Nadoka::NDK_Bot
62
- def bot_initialize
63
- @available_channel = @bot_config[:ch] || /.*/
64
- @search_default_lang = (@bot_config[:search_default_lang] || 'ja').sub(/^lang_/, '')
65
- @referer = @bot_config[:referer] || 'http://rubyforge.org/projects/nadoka/'
66
- @ch_kcode = @bot_config.fetch(:ch_kcode, :tojis)
67
- @to_lang = @bot_config.fetch(:to_lang, 'ja')
68
- @to_lang2 = @bot_config.fetch(:to_lang2, 'en')
69
- @bing_appid = @bot_config.fetch(:bing_appid, nil)
70
- end
71
-
72
- def on_privmsg prefix, ch, msg
73
- if @available_channel === ch and /^tr/ =~ msg
74
- if response = dispatch_command(msg)
75
- response.each{|r|
76
- send_notice(ch, r) if r
77
- }
78
- end
79
- end
80
- end
81
-
82
- SHORT_LANG = {'e' => 'en', 'j' => 'ja'}
83
-
84
- def dispatch_command msg
85
- begin
86
- case msg
87
- when /^trans(?:late)?(:(.*?))?>\s*(.+)/o
88
- translate($3, *parse_trans_option($2, $3))
89
- when /^trans([ej])>\s*(.+)/o
90
- translate($2, nil, SHORT_LANG[$1])
91
- when /^trans(?:late)?r(:(.*?))?>\s*(.+)/o
92
- translate_r($3, *parse_trans_option($2, $3))
93
- when /^translang>(.+)/
94
- lang = $1.strip
95
- desc = 'Unknown. See http://code.google.com/intl/ja/apis/ajaxlanguage/documentation/reference.html#LangNameArray'
96
- r = LANGUAGE_MAP_S2L.fetch(lang.downcase,
97
- LANGUAGE_MAP.fetch(lang.upcase, desc))
98
- "translang> #{lang} = #{r}"
99
- end
100
- rescue Exception => e
101
- "translate bot: #{e.message}"
102
- end
103
- end
104
-
105
- def detect_lang str
106
- uri = "http://ajax.googleapis.com/ajax/services/language/detect?v=1.0&q="
107
- uri << CGI.escape(str.toutf8)
108
-
109
- result = open(uri, "Referer" => @referer) do |f|
110
- JSON.parse(f.read)
111
- end
112
-
113
- if result["responseData"]
114
- result["responseData"]["language"]
115
- end
116
- end
117
-
118
- def parse_trans_option opt, str
119
- case opt
120
- when nil
121
- from_lang = detect_lang(str)
122
- to_lang = @to_lang
123
- if to_lang == from_lang
124
- to_lang = @to_lang2
125
- end
126
- [from_lang, to_lang]
127
- when /\A([\w\-]+)[, \>]([\w\-]+)\z/
128
- [$1, $2]
129
- when /\A([\w\-]+)\z/
130
- [nil, $1]
131
- else
132
- raise "can't parse translation option: #{opt}"
133
- end
134
- end
135
-
136
- def translate phrase, from_lang, to_lang
137
- r = []
138
-
139
- gr = translate_ggl(phrase, from_lang, to_lang)
140
- r << "g:translate bot (#{gr[1]}): #{gr[0]}"
141
-
142
- if @bing_appid
143
- mr = translate_ms(phrase, from_lang, to_lang) if @bing_appid
144
- r << "m:translate bot (#{mr[1]}): #{mr[0]}"
145
- end
146
- r
147
- end
148
-
149
- def translate_r phrase, from_lang, to_lang
150
- rs = []
151
- %w(ggl ms).each{|system|
152
- r = send("translate_#{system}", phrase, from_lang, to_lang)
153
- from_lang = r[2]
154
- first = r[0]
155
- if from_lang
156
- r = send("translate_#{system}", r[0], to_lang, from_lang)
157
- rs << "#{system.split(//)[0]}:trans_r (#{from_lang}>#{to_lang}>#{from_lang}): #{r[0]} (#{first})"
158
- end
159
- }
160
- rs
161
- end
162
-
163
-
164
- def translate_ggl(phrase, from_lang, to_lang)
165
- uri = 'http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q='
166
- uri << CGI.escape(phrase.toutf8)
167
- uri << "&langpair=#{from_lang}%7C#{to_lang}"
168
-
169
- result = open(uri, "Referer" => @referer) do |f|
170
- JSON.parse(f.read)
171
- end
172
-
173
- if result["responseData"]
174
- text = CGI.unescapeHTML(result["responseData"]["translatedText"])
175
- text = text.send(@ch_kcode) if @ch_kcode
176
- from_lang ||= result["responseData"]["detectedSourceLanguage"]
177
- opts = "#{from_lang}>#{to_lang}"
178
- [text, opts, from_lang]
179
- else
180
- opts = "#{from_lang ? "from #{from_lang} to " : ''}#{to_lang}"
181
- ["#{result["responseDetails"]} (#{uri})", opts]
182
- end
183
- end
184
-
185
- ## ms translate
186
-
187
- def get_result_ms result
188
- # puts result
189
- doc = REXML::Document.new(result)
190
- doc.elements.map{|e| e.text}[0]
191
- end
192
-
193
- def translate_ms phrase, from_lang, to_lang
194
- api_url = 'http://api.microsofttranslator.com/V2/Http.svc/Translate'
195
- uri = "#{api_url}?appId=#{@bing_appid}&text=#{CGI.escape(phrase.toutf8)}&to=#{CGI.escape(to_lang)}"
196
- begin
197
- text = get_result_ms open(uri, "Referer" => @referer).read
198
- text = text.send(@ch_kcode) if @ch_kcode
199
- opts = "#{from_lang}>#{to_lang}"
200
- [text, opts, from_lang]
201
- rescue OpenURI::HTTPError => e
202
- opts = "#{from_lang ? "from #{from_lang} to " : ''}#{to_lang}"
203
- ["#{e.message} (uri)", opts]
204
- end
205
- end
206
-
207
- # copy from http://code.google.com/intl/ja/apis/ajaxlanguage/documentation/reference.html
208
- LANGUAGE_MAP = {
209
- 'AFRIKAANS' => 'af',
210
- 'ALBANIAN' => 'sq',
211
- 'AMHARIC' => 'am',
212
- 'ARABIC' => 'ar',
213
- 'ARMENIAN' => 'hy',
214
- 'AZERBAIJANI' => 'az',
215
- 'BASQUE' => 'eu',
216
- 'BELARUSIAN' => 'be',
217
- 'BENGALI' => 'bn',
218
- 'BIHARI' => 'bh',
219
- 'BULGARIAN' => 'bg',
220
- 'BURMESE' => 'my',
221
- 'CATALAN' => 'ca',
222
- 'CHEROKEE' => 'chr',
223
- 'CHINESE' => 'zh',
224
- 'CHINESE_SIMPLIFIED' => 'zh-CN',
225
- 'CHINESE_TRADITIONAL' => 'zh-TW',
226
- 'CROATIAN' => 'hr',
227
- 'CZECH' => 'cs',
228
- 'DANISH' => 'da',
229
- 'DHIVEHI' => 'dv',
230
- 'DUTCH'=> 'nl',
231
- 'ENGLISH' => 'en',
232
- 'ESPERANTO' => 'eo',
233
- 'ESTONIAN' => 'et',
234
- 'FILIPINO' => 'tl',
235
- 'FINNISH' => 'fi',
236
- 'FRENCH' => 'fr',
237
- 'GALICIAN' => 'gl',
238
- 'GEORGIAN' => 'ka',
239
- 'GERMAN' => 'de',
240
- 'GREEK' => 'el',
241
- 'GUARANI' => 'gn',
242
- 'GUJARATI' => 'gu',
243
- 'HEBREW' => 'iw',
244
- 'HINDI' => 'hi',
245
- 'HUNGARIAN' => 'hu',
246
- 'ICELANDIC' => 'is',
247
- 'INDONESIAN' => 'id',
248
- 'INUKTITUT' => 'iu',
249
- 'ITALIAN' => 'it',
250
- 'JAPANESE' => 'ja',
251
- 'KANNADA' => 'kn',
252
- 'KAZAKH' => 'kk',
253
- 'KHMER' => 'km',
254
- 'KOREAN' => 'ko',
255
- 'KURDISH'=> 'ku',
256
- 'KYRGYZ'=> 'ky',
257
- 'LAOTHIAN'=> 'lo',
258
- 'LATVIAN' => 'lv',
259
- 'LITHUANIAN' => 'lt',
260
- 'MACEDONIAN' => 'mk',
261
- 'MALAY' => 'ms',
262
- 'MALAYALAM' => 'ml',
263
- 'MALTESE' => 'mt',
264
- 'MARATHI' => 'mr',
265
- 'MONGOLIAN' => 'mn',
266
- 'NEPALI' => 'ne',
267
- 'NORWEGIAN' => 'no',
268
- 'ORIYA' => 'or',
269
- 'PASHTO' => 'ps',
270
- 'PERSIAN' => 'fa',
271
- 'POLISH' => 'pl',
272
- 'PORTUGUESE' => 'pt-PT',
273
- 'PUNJABI' => 'pa',
274
- 'ROMANIAN' => 'ro',
275
- 'RUSSIAN' => 'ru',
276
- 'SANSKRIT' => 'sa',
277
- 'SERBIAN' => 'sr',
278
- 'SINDHI' => 'sd',
279
- 'SINHALESE' => 'si',
280
- 'SLOVAK' => 'sk',
281
- 'SLOVENIAN' => 'sl',
282
- 'SPANISH' => 'es',
283
- 'SWAHILI' => 'sw',
284
- 'SWEDISH' => 'sv',
285
- 'TAJIK' => 'tg',
286
- 'TAMIL' => 'ta',
287
- 'TAGALOG' => 'tl',
288
- 'TELUGU' => 'te',
289
- 'THAI' => 'th',
290
- 'TIBETAN' => 'bo',
291
- 'TURKISH' => 'tr',
292
- 'UKRAINIAN' => 'uk',
293
- 'URDU' => 'ur',
294
- 'UZBEK' => 'uz',
295
- 'UIGHUR' => 'ug',
296
- 'VIETNAMESE' => 'vi',
297
- 'UNKNOWN' => ''
298
- }
299
-
300
- LANGUAGE_MAP_S2L = LANGUAGE_MAP.invert
301
- end