1 # Weather plugin for rbot
3 # NOAA National Weather Service support by MrChucho (mrchucho@mrchucho.net)
4 # Copyright (C) 2006 Ralph M. Churchill
6 # Weather Undeground support by Giuseppe "Oblomov" Bilotta <giuseppe.bilotta@gmail.com>
7 # Copyright (C) 2006 Giuseppe Bilotta
10 require 'rexml/document'
12 # Wraps NOAA National Weather Service information
13 class CurrentConditions
14 def initialize(station)
16 @url = "http://www.nws.noaa.gov/data/current_obs/#{@station.upcase}.xml"
18 @mtime = Time.mktime(0)
19 @current_conditions = String.new
24 open(@url,"If-Modified-Since" => @mtime.rfc2822) do |feed|
25 # open(@url,"If-None-Match"=>@etag) do |feed|
26 @etag = feed.meta['etag']
27 @mtime = feed.last_modified
28 cc_doc = (REXML::Document.new feed).root
30 @current_conditions = parse(cc_doc)
32 rescue OpenURI::HTTPError => e
37 raise "Data for #{@station} not found"
39 raise "Error retrieving data: #{e}"
42 @current_conditions # +" Cached? "+ ((@iscached) ? "Y" : "N")
46 cc_doc.elements.each do |c|
47 cc[c.name.to_sym] = c.text
49 "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."
52 def heat_index_or_wind_chill(cc)
53 hi = cc[:heat_index_string]
54 wc = cc[:windchill_string]
56 " with a heat index of #{hi}"
58 " with a windchill of #{wc}"
65 class WeatherPlugin < Plugin
67 def help(plugin, topic="")
70 "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/ )"
72 "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."
74 "weather information lookup. Looks up weather information for the last location you specified. See topics 'nws' and 'wu' for more information"
83 @wu_url = "http://mobile.wunderground.com/cgi-bin/findweather/getForecast?brand=mobile%s&query=%s"
84 @wu_station_url = "http://mobile.wunderground.com/auto/mobile%s/global/stations/%s.html"
87 def weather(m, params)
88 if params[:where].empty?
89 if @registry.has_key?(m.sourcenick)
90 where = @registry[m.sourcenick]
91 debug "Loaded weather info #{where.inspect} for #{m.sourcenick}"
93 service = where.first.to_sym
95 units = params[:units] || where[2] rescue nil
97 debug "No weather info for #{m.sourcenick}"
98 m.reply "I don't know where you are yet, #{m.sourcenick}. See 'help weather nws' or 'help weather wu' for additional help"
102 where = params[:where]
103 if ['nws','station'].include?(where.first)
104 service = where.first.to_sym
110 units = params[:units]
114 debug "No weather location found for #{m.sourcenick}"
115 m.reply "I don't know where you are yet, #{m.sourcenick}. See 'help weather nws' or 'help weather wu' for additional help"
119 wu_units = String.new
122 when :english, :metric
123 wu_units = "_#{units}"
126 m.reply "Ignoring unknown units #{units}"
127 wu_units = String.new
135 wu_station(m, loc, wu_units)
137 wu_weather(m, loc, wu_units)
140 @registry[m.sourcenick] = [service, loc, units]
143 def nws_describe(m, where)
144 if @nws_cache.has_key?(where) then
145 met = @nws_cache[where]
147 met = CurrentConditions.new(where)
152 @nws_cache[where] = met
157 m.reply "couldn't find weather data for #{where}"
161 def wu_station(m, where, units)
163 xml = @bot.httputil.get_cached(@wu_station_url % [units, URI.escape(where)])
166 m.reply "couldn't retrieve weather information, sorry"
168 when /Search not found:/
169 m.reply "no such station found (#{where})"
171 when /<table border.*?>(.*?)<\/table>/m
173 m.reply wu_weather_filter(data)
176 m.reply "something went wrong with the data for #{where}..."
179 m.reply "retrieving info about '#{where}' failed (#{e})"
183 def wu_weather(m, where, units)
185 xml = @bot.httputil.get_cached(@wu_url % [units, URI.escape(where)])
188 m.reply "couldn't retrieve weather information, sorry"
190 when /City Not Found/
191 m.reply "no such location found (#{where})"
193 when /<table border.*?>(.*?)<\/table>/m
195 m.reply wu_weather_filter(data)
196 when /<a href="\/global\/stations\//
197 stations = xml.scan(/<a href="\/global\/stations\/(.*?)\.html">/)
198 m.reply "multiple stations available, use 'weather station <code>' where code is one of " + stations.join(", ")
201 m.reply "something went wrong with the data from #{where}..."
204 m.reply "retrieving info about '#{where}' failed (#{e})"
208 def wu_weather_filter(stuff)
210 txt.gsub!(/[\n\s]+/,' ')
212 txt.gsub!(/ /, ' ')
213 txt.gsub!(/°/, ' ') # degree sign
214 txt.gsub!(/<\/?b>/,'')
215 txt.gsub!(/<\/?span[^<>]*?>/,'')
216 txt.gsub!(/<img\s*[^<>]*?>/,'')
217 txt.gsub!(/<br\s?\/?>/,'')
220 if txt.match(/<\/a>\s*Updated:\s*(.*?)\s*Observed at\s*(.*?)\s*<\/td>/)
221 result << ("Weather info for %s (updated on %s)" % [$2, $1])
223 txt.scan(/<tr>\s*<td>\s*(.*?)\s*<\/td>\s*<td>\s*(.*?)\s*<\/td>\s*<\/tr>/) { |k, v|
225 next if ["-", "- approx.", "N/A", "N/A approx."].include?(v)
226 next if k == "Raw METAR"
227 result << ("%s: %s" % [k, v])
229 return result.join('; ')
233 plugin = WeatherPlugin.new
234 plugin.map 'weather :units *where', :defaults => {:where => false, :units => false}, :requirements => {:units => /metric|english|both/}