]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/utils.rb
Safe-save tempfiles are now created in a botclass-local directory, because atomic...
[user/henk/code/ruby/rbot.git] / lib / rbot / utils.rb
1 require 'net/http'
2 require 'uri'
3 require 'tempfile'
4
5 begin
6   $we_have_html_entities_decoder = require 'htmlentities'
7 rescue LoadError
8   $we_have_html_entities_decoder = false
9   module Irc
10     module Utils
11       UNESCAPE_TABLE = {
12     'raquo' => '>>',
13     'quot' => '"',
14     'apos' => '\'',
15     'micro' => 'u',
16     'copy' => '(c)',
17     'trade' => '(tm)',
18     'reg' => '(R)',
19     '#174' => '(R)',
20     '#8220' => '"',
21     '#8221' => '"',
22     '#8212' => '--',
23     '#39' => '\'',
24 =begin
25     # extras codes, for future use...
26     'zwnj' => '‌',
27     'aring' => '\xe5',
28     'gt' => '>',
29     'yen' => '\xa5',
30     'ograve' => '\xf2',
31     'Chi' => 'Χ',
32     'bull' => '•',
33     'Egrave' => '\xc8',
34     'Ntilde' => '\xd1',
35     'upsih' => 'ϒ',
36     'Yacute' => '\xdd',
37     'asymp' => '≈',
38     'radic' => '√',
39     'otimes' => '⊗',
40     'nabla' => '∇',
41     'aelig' => '\xe6',
42     'oelig' => 'œ',
43     'equiv' => '≡',
44     'Psi' => 'Ψ',
45     'auml' => '\xe4',
46     'circ' => 'ˆ',
47     'Acirc' => '\xc2',
48     'Epsilon' => 'Ε',
49     'Yuml' => 'Ÿ',
50     'Eta' => 'Η',
51     'lt' => '<',
52     'Icirc' => '\xce',
53     'Upsilon' => '&#933;',
54     'ndash' => '&#8211;',
55     'there4' => '&#8756;',
56     'Prime' => '&#8243;',
57     'prime' => '&#8242;',
58     'psi' => '&#968;',
59     'Kappa' => '&#922;',
60     'rsaquo' => '&#8250;',
61     'Tau' => '&#932;',
62     'darr' => '&#8595;',
63     'ocirc' => '\xf4',
64     'lrm' => '&#8206;',
65     'zwj' => '&#8205;',
66     'cedil' => '\xb8',
67     'Ecirc' => '\xca',
68     'not' => '\xac',
69     'amp' => '&',
70     'AElig' => '\xc6',
71     'oslash' => '\xf8',
72     'acute' => '\xb4',
73     'lceil' => '&#8968;',
74     'laquo' => '\xab',
75     'shy' => '\xad',
76     'rdquo' => '&#8221;',
77     'ge' => '&#8805;',
78     'Igrave' => '\xcc',
79     'Ograve' => '\xd2',
80     'euro' => '&#8364;',
81     'dArr' => '&#8659;',
82     'sdot' => '&#8901;',
83     'nbsp' => '\xa0',
84     'lfloor' => '&#8970;',
85     'lArr' => '&#8656;',
86     'Auml' => '\xc4',
87     'larr' => '&#8592;',
88     'Atilde' => '\xc3',
89     'Otilde' => '\xd5',
90     'szlig' => '\xdf',
91     'clubs' => '&#9827;',
92     'diams' => '&#9830;',
93     'agrave' => '\xe0',
94     'Ocirc' => '\xd4',
95     'Iota' => '&#921;',
96     'Theta' => '&#920;',
97     'Pi' => '&#928;',
98     'OElig' => '&#338;',
99     'Scaron' => '&#352;',
100     'frac14' => '\xbc',
101     'egrave' => '\xe8',
102     'sub' => '&#8834;',
103     'iexcl' => '\xa1',
104     'frac12' => '\xbd',
105     'sbquo' => '&#8218;',
106     'ordf' => '\xaa',
107     'sum' => '&#8721;',
108     'prop' => '&#8733;',
109     'Uuml' => '\xdc',
110     'ntilde' => '\xf1',
111     'sup' => '&#8835;',
112     'theta' => '&#952;',
113     'prod' => '&#8719;',
114     'nsub' => '&#8836;',
115     'hArr' => '&#8660;',
116     'rlm' => '&#8207;',
117     'THORN' => '\xde',
118     'infin' => '&#8734;',
119     'yuml' => '\xff',
120     'Mu' => '&#924;',
121     'le' => '&#8804;',
122     'Eacute' => '\xc9',
123     'thinsp' => '&#8201;',
124     'ecirc' => '\xea',
125     'bdquo' => '&#8222;',
126     'Sigma' => '&#931;',
127     'fnof' => '&#402;',
128     'Aring' => '\xc5',
129     'tilde' => '&#732;',
130     'frac34' => '\xbe',
131     'emsp' => '&#8195;',
132     'mdash' => '&#8212;',
133     'uarr' => '&#8593;',
134     'permil' => '&#8240;',
135     'Ugrave' => '\xd9',
136     'rarr' => '&#8594;',
137     'Agrave' => '\xc0',
138     'chi' => '&#967;',
139     'forall' => '&#8704;',
140     'eth' => '\xf0',
141     'rceil' => '&#8969;',
142     'iuml' => '\xef',
143     'gamma' => '&#947;',
144     'lambda' => '&#955;',
145     'harr' => '&#8596;',
146     'rang' => '&#9002;',
147     'xi' => '&#958;',
148     'dagger' => '&#8224;',
149     'divide' => '\xf7',
150     'Ouml' => '\xd6',
151     'image' => '&#8465;',
152     'alefsym' => '&#8501;',
153     'igrave' => '\xec',
154     'otilde' => '\xf5',
155     'Oacute' => '\xd3',
156     'sube' => '&#8838;',
157     'alpha' => '&#945;',
158     'frasl' => '&#8260;',
159     'ETH' => '\xd0',
160     'lowast' => '&#8727;',
161     'Nu' => '&#925;',
162     'plusmn' => '\xb1',
163     'Euml' => '\xcb',
164     'real' => '&#8476;',
165     'sup1' => '\xb9',
166     'sup2' => '\xb2',
167     'sup3' => '\xb3',
168     'Oslash' => '\xd8',
169     'Aacute' => '\xc1',
170     'cent' => '\xa2',
171     'oline' => '&#8254;',
172     'Beta' => '&#914;',
173     'perp' => '&#8869;',
174     'Delta' => '&#916;',
175     'loz' => '&#9674;',
176     'pi' => '&#960;',
177     'iota' => '&#953;',
178     'empty' => '&#8709;',
179     'euml' => '\xeb',
180     'brvbar' => '\xa6',
181     'iacute' => '\xed',
182     'para' => '\xb6',
183     'micro' => '\xb5',
184     'cup' => '&#8746;',
185     'weierp' => '&#8472;',
186     'uuml' => '\xfc',
187     'part' => '&#8706;',
188     'icirc' => '\xee',
189     'delta' => '&#948;',
190     'omicron' => '&#959;',
191     'upsilon' => '&#965;',
192     'Iuml' => '\xcf',
193     'Lambda' => '&#923;',
194     'Xi' => '&#926;',
195     'kappa' => '&#954;',
196     'ccedil' => '\xe7',
197     'Ucirc' => '\xdb',
198     'cap' => '&#8745;',
199     'mu' => '&#956;',
200     'scaron' => '&#353;',
201     'lsquo' => '&#8216;',
202     'isin' => '&#8712;',
203     'Zeta' => '&#918;',
204     'supe' => '&#8839;',
205     'deg' => '\xb0',
206     'and' => '&#8743;',
207     'tau' => '&#964;',
208     'pound' => '\xa3',
209     'hellip' => '&#8230;',
210     'curren' => '\xa4',
211     'int' => '&#8747;',
212     'ucirc' => '\xfb',
213     'rfloor' => '&#8971;',
214     'ensp' => '&#8194;',
215     'crarr' => '&#8629;',
216     'ugrave' => '\xf9',
217     'notin' => '&#8713;',
218     'exist' => '&#8707;',
219     'uArr' => '&#8657;',
220     'cong' => '&#8773;',
221     'Dagger' => '&#8225;',
222     'oplus' => '&#8853;',
223     'times' => '\xd7',
224     'atilde' => '\xe3',
225     'piv' => '&#982;',
226     'ni' => '&#8715;',
227     'Phi' => '&#934;',
228     'lsaquo' => '&#8249;',
229     'Uacute' => '\xda',
230     'Omicron' => '&#927;',
231     'ang' => '&#8736;',
232     'ne' => '&#8800;',
233     'iquest' => '\xbf',
234     'eta' => '&#951;',
235     'yacute' => '\xfd',
236     'Rho' => '&#929;',
237     'uacute' => '\xfa',
238     'Alpha' => '&#913;',
239     'zeta' => '&#950;',
240     'Omega' => '&#937;',
241     'nu' => '&#957;',
242     'sim' => '&#8764;',
243     'sect' => '\xa7',
244     'phi' => '&#966;',
245     'sigmaf' => '&#962;',
246     'macr' => '\xaf',
247     'minus' => '&#8722;',
248     'Ccedil' => '\xc7',
249     'ordm' => '\xba',
250     'epsilon' => '&#949;',
251     'beta' => '&#946;',
252     'rArr' => '&#8658;',
253     'rho' => '&#961;',
254     'aacute' => '\xe1',
255     'eacute' => '\xe9',
256     'omega' => '&#969;',
257     'middot' => '\xb7',
258     'Gamma' => '&#915;',
259     'Iacute' => '\xcd',
260     'lang' => '&#9001;',
261     'spades' => '&#9824;',
262     'rsquo' => '&#8217;',
263     'uml' => '\xa8',
264     'thorn' => '\xfe',
265     'ouml' => '\xf6',
266     'thetasym' => '&#977;',
267     'or' => '&#8744;',
268     'raquo' => '\xbb',
269     'acirc' => '\xe2',
270     'ldquo' => '&#8220;',
271     'hearts' => '&#9829;',
272     'sigma' => '&#963;',
273     'oacute' => '\xf3',
274 =end
275       }
276     end
277   end
278 end
279
280
281 module Irc
282
283   # miscellaneous useful functions
284   module Utils
285
286     # turn a number of seconds into a human readable string, e.g
287     # 2 days, 3 hours, 18 minutes, 10 seconds
288     def Utils.secs_to_string(secs)
289       ret = ""
290       days = (secs / (60 * 60 * 24)).to_i
291       secs = secs % (60 * 60 * 24)
292       hours = (secs / (60 * 60)).to_i
293       secs = (secs % (60 * 60))
294       mins = (secs / 60).to_i
295       secs = (secs % 60).to_i
296       ret += "#{days} days, " if days > 0
297       ret += "#{hours} hours, " if hours > 0 || days > 0
298       ret += "#{mins} minutes and " if mins > 0 || hours > 0 || days > 0
299       ret += "#{secs} seconds"
300       return ret
301     end
302
303
304     def Utils.safe_exec(command, *args)
305       IO.popen("-") {|p|
306         if(p)
307           return p.readlines.join("\n")
308         else
309           begin
310             $stderr = $stdout
311             exec(command, *args)
312           rescue Exception => e
313             puts "exec of #{command} led to exception: #{e.inspect}"
314             Kernel::exit! 0
315           end
316           puts "exec of #{command} failed"
317           Kernel::exit! 0
318         end
319       }
320     end
321
322
323     @@safe_save_dir = nil
324     def Utils.set_safe_save_dir(str)
325       @@safe_save_dir = str.dup
326     end
327
328     def Utils.safe_save(file)
329       raise 'No safe save directory defined!' if @@safe_save_dir.nil?
330       basename = File.basename(file)
331       temp = Tempfile.new(basename,@@safe_save_dir)
332       temp.binmode
333       yield temp if block_given?
334       temp.close
335       File.rename(temp.path, file)
336     end
337
338
339     # returns a string containing the result of an HTTP GET on the uri
340     def Utils.http_get(uristr, readtimeout=8, opentimeout=4)
341
342       # ruby 1.7 or better needed for this (or 1.6 and debian unstable)
343       Net::HTTP.version_1_2
344       # (so we support the 1_1 api anyway, avoids problems)
345
346       uri = URI.parse uristr
347       query = uri.path
348       if uri.query
349         query += "?#{uri.query}"
350       end
351
352       proxy_host = nil
353       proxy_port = nil
354       if(ENV['http_proxy'] && proxy_uri = URI.parse(ENV['http_proxy']))
355         proxy_host = proxy_uri.host
356         proxy_port = proxy_uri.port
357       end
358
359       begin
360         http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port)
361         http.open_timeout = opentimeout
362         http.read_timeout = readtimeout
363
364         http.start {|http|
365           resp = http.get(query)
366           if resp.code == "200"
367             return resp.body
368           end
369         }
370       rescue => e
371         # cheesy for now
372         error "Utils.http_get exception: #{e.inspect}, while trying to get #{uristr}"
373         return nil
374       end
375     end
376
377     def Utils.decode_html_entities(str)
378       if $we_have_html_entities_decoder
379         return HTMLEntities.decode_entities(str)
380       else
381         str.gsub(/(&(.+?);)/) {
382           symbol = $2
383           # remove the 0-paddng from unicode integers
384           if symbol =~ /#(.+)/
385             symbol = "##{$1.to_i.to_s}"
386           end
387
388           # output the symbol's irc-translated character, or a * if it's unknown
389           UNESCAPE_TABLE[symbol] || '*'
390         }
391       end
392     end
393   end
394 end