| 1 | #!/usr/bin/env ruby |
|---|
| 2 | |
|---|
| 3 | require 'optparse' |
|---|
| 4 | require "ostruct" |
|---|
| 5 | require "thread" |
|---|
| 6 | require 'socket' |
|---|
| 7 | require 'uri' |
|---|
| 8 | require 'net/http' |
|---|
| 9 | require 'timeout' |
|---|
| 10 | require 'yaml' |
|---|
| 11 | |
|---|
| 12 | Net::HTTP.version_1_2 |
|---|
| 13 | |
|---|
| 14 | class SocialSKK |
|---|
| 15 | VERSION_STRING = "SocialSKK0.2 " |
|---|
| 16 | |
|---|
| 17 | CLIENT_END = ?0 |
|---|
| 18 | CLIENT_REQUEST = ?1 |
|---|
| 19 | CLIENT_VERSION = ?2 |
|---|
| 20 | CLIENT_HOST = ?3 |
|---|
| 21 | SERVER_ERROR = ?0 |
|---|
| 22 | SERVER_FOUND = ?1 |
|---|
| 23 | SERVER_NOT_FOUND = ?4 |
|---|
| 24 | |
|---|
| 25 | BUFSIZE = 512 |
|---|
| 26 | TIMEOUT = 10 |
|---|
| 27 | |
|---|
| 28 | def initialize(host, port, proxy, cache_time, cache=nil) |
|---|
| 29 | @host = host |
|---|
| 30 | @port = port |
|---|
| 31 | @proxy = proxy |
|---|
| 32 | @cache = cache || {} |
|---|
| 33 | @cache_time = cache_time |
|---|
| 34 | @ts = TCPServer.open(@host, @port) |
|---|
| 35 | puts "server is on #{@ts.addr[1..-1].join(":")}" |
|---|
| 36 | puts "proxy is #{@proxy.to_s}" if @proxy |
|---|
| 37 | puts "cache keep time #{@cache_time}sec" |
|---|
| 38 | end |
|---|
| 39 | |
|---|
| 40 | def mainloop |
|---|
| 41 | loop do |
|---|
| 42 | Thread.start(@ts.accept) do |s| |
|---|
| 43 | while cmdbuf = s.sysread(BUFSIZE) |
|---|
| 44 | case cmdbuf[0] |
|---|
| 45 | when CLIENT_END |
|---|
| 46 | break |
|---|
| 47 | when CLIENT_REQUEST |
|---|
| 48 | cmdend = cmdbuf.index(?\ ) || cmdbuf.index(?\n) |
|---|
| 49 | kana = cmdbuf[1 .. (cmdend - 1)] |
|---|
| 50 | ret = '' |
|---|
| 51 | begin |
|---|
| 52 | if kanji = search(kana) |
|---|
| 53 | ret.concat(SERVER_FOUND) |
|---|
| 54 | ret.concat(kanji) |
|---|
| 55 | else |
|---|
| 56 | ret.concat(SERVER_NOT_FOUND) |
|---|
| 57 | ret.concat(cmdbuf[1 .. -1]) |
|---|
| 58 | end |
|---|
| 59 | ret.concat("\n") |
|---|
| 60 | rescue Exception |
|---|
| 61 | ret.concat(SERVER_ERROR) |
|---|
| 62 | ret.concat($!) |
|---|
| 63 | end |
|---|
| 64 | s.write(ret) |
|---|
| 65 | when CLIENT_VERSION |
|---|
| 66 | s.write(VERSION_STRING) |
|---|
| 67 | when CLIENT_HOST |
|---|
| 68 | ret = host(s) |
|---|
| 69 | s.write(ret) |
|---|
| 70 | end |
|---|
| 71 | end |
|---|
| 72 | s.close |
|---|
| 73 | end |
|---|
| 74 | end |
|---|
| 75 | end |
|---|
| 76 | |
|---|
| 77 | private |
|---|
| 78 | def host(sock = nil) |
|---|
| 79 | if sock.nil? |
|---|
| 80 | hostname = Socket.gethostname |
|---|
| 81 | ipaddr = TCPSocket.getaddress(hostname) |
|---|
| 82 | else |
|---|
| 83 | hostname, ipaddr = sock.addr[2], sock.addr[3] |
|---|
| 84 | end |
|---|
| 85 | |
|---|
| 86 | hostname + ':' + ipaddr + ': ' |
|---|
| 87 | end |
|---|
| 88 | |
|---|
| 89 | def search(kana) |
|---|
| 90 | if @cache[kana] and Time.now < (@cache[kana][:ctime] + @cache_time) |
|---|
| 91 | @cache[kana][:kanji] |
|---|
| 92 | else |
|---|
| 93 | kanji = social_ime_search(kana) |
|---|
| 94 | @cache[kana] = { |
|---|
| 95 | :kanji => kanji, |
|---|
| 96 | :ctime => Time.now |
|---|
| 97 | } |
|---|
| 98 | kanji |
|---|
| 99 | end |
|---|
| 100 | end |
|---|
| 101 | |
|---|
| 102 | def social_ime_search(kana) |
|---|
| 103 | size = kana.size / 2 |
|---|
| 104 | kanji = '/' |
|---|
| 105 | begin |
|---|
| 106 | timeout(TIMEOUT) do |
|---|
| 107 | http = Net::HTTP.new('www.social-ime.com', 80) |
|---|
| 108 | http = Net::HTTP.new('www.social-ime.com', 80, @proxy.host, @proxy.port) if @proxy |
|---|
| 109 | http.start do |h| |
|---|
| 110 | res = h.get("/api/?string=#{URI.escape(kana)}&resize[0]=+#{size}") |
|---|
| 111 | kanji += res.body.to_s.split("\n").join('/').gsub(/\t/, '/') |
|---|
| 112 | end |
|---|
| 113 | end |
|---|
| 114 | rescue |
|---|
| 115 | kanji = nil |
|---|
| 116 | end |
|---|
| 117 | kanji |
|---|
| 118 | end |
|---|
| 119 | end |
|---|
| 120 | |
|---|
| 121 | def time_parse(str) |
|---|
| 122 | n, u = str.scan(/(\d+)([dhms]?)/)[0] |
|---|
| 123 | n = n.to_i |
|---|
| 124 | case (u) |
|---|
| 125 | when "d" |
|---|
| 126 | n *= 24 * 60 * 60 |
|---|
| 127 | when "h" |
|---|
| 128 | n *= 60 * 60 |
|---|
| 129 | when "m" |
|---|
| 130 | n *= 60 |
|---|
| 131 | end |
|---|
| 132 | n |
|---|
| 133 | end |
|---|
| 134 | |
|---|
| 135 | opts = OpenStruct.new({ |
|---|
| 136 | :port => 55100, |
|---|
| 137 | :host => '0.0.0.0', |
|---|
| 138 | :proxy => nil, |
|---|
| 139 | :cache_time => 3600, |
|---|
| 140 | }) |
|---|
| 141 | |
|---|
| 142 | OptionParser.new do |parser| |
|---|
| 143 | parser.instance_eval do |
|---|
| 144 | self.banner = "Usage: #{$0} [opts]" |
|---|
| 145 | |
|---|
| 146 | separator '' |
|---|
| 147 | separator 'Options:' |
|---|
| 148 | on('-p', '--port 55100', 'Listen port number') do |port| |
|---|
| 149 | opts.port = port |
|---|
| 150 | end |
|---|
| 151 | on('-h', '--host "0.0.0.0"', 'Listen hostname') do |host| |
|---|
| 152 | opts.host = host |
|---|
| 153 | end |
|---|
| 154 | on('-x', '--proxy "http://proxy.example.com:8080"', 'HTTP Proxy server') do |proxy| |
|---|
| 155 | opts.proxy = URI.parse(proxy) |
|---|
| 156 | end |
|---|
| 157 | on('-c', '--cache-time 1h', 'Cache keep time') do |ct| |
|---|
| 158 | opts.cache_time = time_parse(ct) |
|---|
| 159 | end |
|---|
| 160 | |
|---|
| 161 | parse!(ARGV) |
|---|
| 162 | end |
|---|
| 163 | end |
|---|
| 164 | |
|---|
| 165 | ss = SocialSKK.new(opts.host, opts.port, opts.proxy, opts.cache_time) |
|---|
| 166 | ss.mainloop |
|---|
| 167 | |
|---|