4 # :title: Weather plugin for rbot
6 # Author:: MrChucho (mrchucho@mrchucho.net): NOAA National Weather Service support
7 # Author:: Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
9 # Copyright:: (C) 2006 Ralph M. Churchill
10 # Copyright:: (C) 2006-2007 Giuseppe Bilotta
15 require 'rexml/document'
17 # Wraps NOAA National Weather Service information
18 class CurrentConditions
19 def initialize(station)
21 @url = "http://www.nws.noaa.gov/data/current_obs/#{@station.upcase}.xml"
23 @mtime = Time.mktime(0)
24 @current_conditions = String.new
29 open(@url,"If-Modified-Since" => @mtime.rfc2822) do |feed|
30 # open(@url,"If-None-Match"=>@etag) do |feed|
31 @etag = feed.meta['etag']
32 @mtime = feed.last_modified
33 cc_doc = (REXML::Document.new feed).root
35 @current_conditions = parse(cc_doc)
37 rescue OpenURI::HTTPError => e
42 raise "Data for #{@station} not found"
44 raise "Error retrieving data: #{e}"
47 @current_conditions # +" Cached? "+ ((@iscached) ? "Y" : "N")
51 cc_doc.elements.each do |c|
52 cc[c.name.to_sym] = c.text
54 "At #{cc[:observation_time_rfc822]}, the wind was #{cc[:wind_string]} at #{cc[:location]} (#{cc[:station_id]}). The temperature was #{cc[:temperature_string]}#{heat_index_or_wind_chill(cc)}, and the pressure was #{cc[:pressure_string]}. The relative humidity was #{cc[:relative_humidity]}%. Current conditions are #{cc[:weather]} with #{cc[:visibility_mi]}mi visibility."
57 def heat_index_or_wind_chill(cc)
58 hi = cc[:heat_index_string]
59 wc = cc[:windchill_string]
61 " with a heat index of #{hi}"
63 " with a windchill of #{wc}"
70 class WeatherPlugin < Plugin
72 def help(plugin, topic="")
75 "weather nws <station> => display the current conditions at the location specified by the NOAA National Weather Service station code <station> ( lookup your station code at http://www.nws.noaa.gov/data/current_obs/ )"
77 "weather [<units>] <location> => display the current conditions at the location specified, looking it up on the Weather Underground site; you can use 'station <code>' to look up data by station code ( lookup your station code at http://www.weatherunderground.com/ ); you can optionally set <units> to 'metric' or 'english' if you only want data with the units; use 'both' for units to go back to having both."
79 "weather information lookup. Looks up weather information for the last location you specified. See topics 'nws' and 'wu' for more information"
88 @wu_url = "http://mobile.wunderground.com/cgi-bin/findweather/getForecast?brand=mobile%s&query=%s"
89 @wu_station_url = "http://mobile.wunderground.com/auto/mobile%s/global/stations/%s.html"
92 def weather(m, params)
93 if params[:where].empty?
94 if @registry.has_key?(m.sourcenick)
95 where = @registry[m.sourcenick]
96 debug "Loaded weather info #{where.inspect} for #{m.sourcenick}"
98 service = where.first.to_sym
100 units = params[:units] || where[2] rescue nil
102 debug "No weather info for #{m.sourcenick}"
103 m.reply "I don't know where you are yet, #{m.sourcenick}. See 'help weather nws' or 'help weather wu' for additional help"
107 where = params[:where]
108 if ['nws','station'].include?(where.first)
109 service = where.first.to_sym
115 units = params[:units]
119 debug "No weather location found for #{m.sourcenick}"
120 m.reply "I don't know where you are yet, #{m.sourcenick}. See 'help weather nws' or 'help weather wu' for additional help"
124 wu_units = String.new
127 when :english, :metric
128 wu_units = "_#{units}"
131 m.reply "Ignoring unknown units #{units}"
132 wu_units = String.new
140 wu_station(m, loc, wu_units)
142 wu_weather(m, loc, wu_units)
145 @registry[m.sourcenick] = [service, loc, units]
148 def nws_describe(m, where)
149 if @nws_cache.has_key?(where) then
150 met = @nws_cache[where]
152 met = CurrentConditions.new(where)
157 @nws_cache[where] = met
162 m.reply "couldn't find weather data for #{where}"
166 def wu_station(m, where, units)
168 xml = @bot.httputil.get(@wu_station_url % [units, URI.escape(where)])
171 m.reply "couldn't retrieve weather information, sorry"
173 when /Search not found:/
174 m.reply "no such station found (#{where})"
176 when /<table border.*?>(.*?)<\/table>/m
178 m.reply wu_weather_filter(data)
181 m.reply "something went wrong with the data for #{where}..."
184 m.reply "retrieving info about '#{where}' failed (#{e})"
188 def wu_weather(m, where, units)
190 xml = @bot.httputil.get(@wu_url % [units, URI.escape(where)])
193 m.reply "couldn't retrieve weather information, sorry"
195 when /City Not Found/
196 m.reply "no such location found (#{where})"
198 when /<table border.*?>(.*?)<\/table>/m
200 m.reply wu_weather_filter(data)
201 when /<a href="\/global\/stations\//
202 stations = xml.scan(/<a href="\/global\/stations\/(.*?)\.html">/)
203 m.reply "multiple stations available, use 'weather station <code>' where code is one of " + stations.join(", ")
206 m.reply "something went wrong with the data from #{where}..."
209 m.reply "retrieving info about '#{where}' failed (#{e})"
213 def wu_weather_filter(stuff)
215 txt.gsub!(/[\n\s]+/,' ')
217 txt.gsub!(/ /, ' ')
218 txt.gsub!(/°/, ' ') # degree sign
219 txt.gsub!(/<\/?b>/,'')
220 txt.gsub!(/<\/?span[^<>]*?>/,'')
221 txt.gsub!(/<img\s*[^<>]*?>/,'')
222 txt.gsub!(/<br\s?\/?>/,'')
225 if txt.match(/<\/a>\s*Updated:\s*(.*?)\s*Observed at\s*(.*?)\s*<\/td>/)
226 result << ("Weather info for %s (updated on %s)" % [$2, $1])
228 txt.scan(/<tr>\s*<td>\s*(.*?)\s*<\/td>\s*<td>\s*(.*?)\s*<\/td>\s*<\/tr>/) { |k, v|
230 next if ["-", "- approx.", "N/A", "N/A approx."].include?(v)
231 next if k == "Raw METAR"
232 result << ("%s: %s" % [k, v])
234 return result.join('; ')
238 plugin = WeatherPlugin.new
239 plugin.map 'weather :units *where', :defaults => {:where => false, :units => false}, :requirements => {:units => /metric|english|both/}