nadoka 0.8.6 → 0.9.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.
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