root/lang/ruby/chokan/trunk/plugins/uri_information.rb @ 5698

Revision 5698, 5.2 kB (checked in by drry, 5 years ago)

lang/ruby/chokan/trunk/plugins/uri_information.rb: expand a scope of letters. fixed a white space.

Line 
1
2require 'chokan/plugin_base'
3require 'net/https'
4require 'net/ftp'
5require "cgi"
6require "image_size"
7require "digest/md5"
8require "timeout"
9
10class UriInformation < Chokan::PluginBase
11        MAX_REDIRECT = 10
12        HEADER       = {
13                "User-Agent" => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0",
14                "Accept"     => "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
15        }
16
17        def initialize(config, chokan)
18                super
19                @uri = /(#{URI.regexp(@config['accept_scheme']).to_s})/
20        end
21
22        def on_privmsg(prefix, channel, message)
23                @channel = channel
24                message.scan(@uri) do |uri|
25                        begin
26                                uri = uri[0]
27                                uri = URI(uri)
28                                mes = nil
29                                timeout(5) do
30                                        mes = chaining_try(uri)
31                                end
32                                log mes
33                                notice(channel, mes) if mes
34                        rescue Exception => e
35                                notice(channel, e.inspect)
36                        end
37                end
38        end
39
40
41        def chaining_try(uri)
42                ret = nil
43                @config['modules'].each do |mod|
44                        begin
45                                if mod.kind_of? Hash
46                                        name = mod.keys[0]
47                                        config = mod[name]
48                                else
49                                        name = mod
50                                        config = {}
51                                end
52                                ret = send("handler_#{name}", config, uri)
53                                break if ret
54                        rescue Exception => e
55                                log e
56                                log name
57                                log e.message
58                                log *e.backtrace
59                        end
60                end
61                ret
62        end
63
64
65        def handler_gigazine(config, uri)
66                return unless uri.scheme =~ /^https?/
67                        require "bdb"
68                        c  = config.find {|h| h["channel"] == @channel }
69                log c.inspect
70                return unless c
71
72                db = BDB::Hash.open(c["db"], nil, 0, 0644)
73                db.each do |k,v|
74                        _, url, datetime, = */^(.+?):(\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d)$/.match(k)
75                                                                                                                        if url == uri.to_s
76                                                                                                                                notice(@channel, "GIGAZINE にものってるわ")
77                                                                                                                                break
78                                                                                                                        end
79                end
80                db.close
81                nil # continue chain
82        end
83
84
85        def handler_mixi(config, uri)
86                return unless uri.host == 'mixi.jp'
87                cookie = {}
88                login_uri = URI("http://mixi.jp/login.pl")
89                Net::HTTP.start(login_uri.host, login_uri.port) do |http|
90                        data = {
91                                "next_url" => "/home.pl",
92                                "email"    => config['user'],
93                                "password" => config['pass'],
94                                "sticky"   => ""
95                        }
96                        data = data.collect {|k,v| "#{k}=#{URI.escape(v, /[^-.!~*'()\w]/n)}" }.join("&")
97                        res = http.post(login_uri.request_uri, data, HEADER)
98                        if res.key?("set-cookie")
99                                res["set-cookie"].split(/\s*,\s*/).each do |c|
100                                        k, v =c.sub(/;.*/, "").split(/=/)
101                                        cookie[k] = v
102                                end
103                                #http.get("/check.pl?n=/home.pl", {"User-Agent" => UA} )
104                        else
105                                raise LoginError, "Invalid e-mail or password"
106                        end
107                end
108
109                http(uri, {
110                        "Cookie" => cookie.collect {|c| c.join("=") }.join(";")
111                })
112        end
113
114        def handler_http(config, uri)
115                return unless uri.scheme =~ /^https?/
116                        http(uri)
117        end
118
119        def handler_urn(config, uri)
120                return unless uri.scheme == 'urn'
121                nid, *nss = uri.opaque.split(/:/)
122                case nid
123                when 'ietf'
124                        subclass, *rest = nss
125                        case subclass
126                        when "rfc"
127                                "http://www.ietf.org/rfc/rfc#{rest[0]}.txt"
128                        end
129                when 'isbn'
130                        "http://www.amazon.co.jp/gp/product/#{nss[0]}/"
131                end
132        end
133
134        def handler_ftp(config, uri)
135                return unless uri.scheme == 'ftp'
136                ftp = Net::FTP.open(uri.host, uri.user || "anonymous", uri.password || "foobar@example.com")
137                begin
138                        size = ftp.size(uri.path)
139                        time = ftp.mtime(uri.path)
140                        "[size: #{size} mtime:#{time}]"
141                rescue Net::FTPPermError => e
142                        e.message
143                ensure
144                        ftp.close
145                end
146        end
147
148        def http(uri, header=HEADER, limit=MAX_REDIRECT)
149                return "Redirect loop?: last:#{uri}" if limit <= 0
150
151                log uri
152                ret = ''
153                http = Net::HTTP.new(uri.host, uri.port)
154                http.use_ssl = (uri.scheme == "https")
155                http.start do |http|
156                        r = http.head(uri.request_uri, header)
157                        log r.code.inspect
158                        case r.code
159                        when '200'
160                                case r["Content-Type"]
161                                when /html/
162                                        ret = html(http.get(uri.request_uri, header.merge({
163                                                "Range" => "0-5000"
164                                })))
165                                when /image/
166                                        ret = image(http.get(uri.request_uri, header))
167                                else
168                                        if r["Content-Length"]
169                                                size = r["Content-Length"].to_i / 1024
170                                                ret = "[#{r["Content-Type"]}] #{size}KB"
171                                        else
172                                                ret = "[#{r["Content-Type"]}]"
173                                        end
174                                end
175                        when '401'
176                                realm = r["WWW-Authenticate"][/Basic realm="([^"]+)"/, 1]
177                                auth  =  @config["http_auth"].find {|e| e["host"] == uri.host and e["realm"] == realm }
178                                if auth
179                                        auth = "Basic " + ["#{auth["user"]}:#{auth["pass"]}"].pack("m")
180                                        ret = http(uri, header.update({'Authorization' => auth}), limit-1)
181                                else
182                                        ret = realm
183                                end
184                        when /^3/
185                                loc = URI(r["Location"])
186                                loc = uri + loc if loc.relative?
187                                ret = http(loc, header, limit-1)
188                        else
189                                ret = "[#{r.code} #{r.message}]"
190                        end
191                end
192
193        rescue Exception => e
194                log e.message
195                log *e.backtrace
196                e.inspect
197        end
198
199        def image(res)
200                size = res.body.length / 1024
201                img = ImageSize.new(res.body)
202                ret =  "#{img.get_type} Image, "
203                ret << "#{img.get_width || "?"}x#{img.get_height || "?"} "
204                ret << "#{size.to_i}KB"
205        end
206
207        def html(res)
208                title = res.body[/<title.*?>(.*?)<\/title\s*>/imn, 1]
209                title = "タイトル無し " if !title || title.empty?
210                title = title.gsub(/\s+/, " ").gsub(/<.*?>/, "").to_u8
211                title.gsub!(/&#(x)?([0-9a-f]+);/i) do |m|
212                        [$1 ? $2.hex : $2.to_i].pack("U")
213                end
214                if title.size > 70
215                        title = title[/.{0,60}/] + "..."
216                end
217
218                title = title.gsub(/&gt;/, ">").gsub(/&lt;/, "<").gsub(/&amp;/, "&")
219
220                "#{title} [#{res["Content-Type"]}]"
221        end
222end
223
224
Note: See TracBrowser for help on using the browser.