# Weather plugin for rbot # # NOAA National Weather Service support by MrChucho (mrchucho@mrchucho.net) # Copyright (C) 2006 Ralph M. Churchill # # Weather Undeground support by Giuseppe "Oblomov" Bilotta # Copyright (C) 2006 Giuseppe Bilotta require 'uri' require 'rexml/document' # Wraps NOAA National Weather Service information class CurrentConditions def initialize(station) @station = station @url = "http://www.nws.noaa.gov/data/current_obs/#{@station.upcase}.xml" @etag = String.new @mtime = Time.mktime(0) @current_conditions = String.new @iscached = false end def update begin open(@url,"If-Modified-Since" => @mtime.rfc2822) do |feed| # open(@url,"If-None-Match"=>@etag) do |feed| @etag = feed.meta['etag'] @mtime = feed.last_modified cc_doc = (REXML::Document.new feed).root @iscached = false @current_conditions = parse(cc_doc) end rescue OpenURI::HTTPError => e case e when /304/: @iscached = true when /404/: raise "Data for #{@station} not found" else raise "Error retrieving data: #{e}" end end @current_conditions # +" Cached? "+ ((@iscached) ? "Y" : "N") end def parse(cc_doc) cc = Hash.new cc_doc.elements.each do |c| cc[c.name.to_sym] = c.text end "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." end private def heat_index_or_wind_chill(cc) hi = cc[:heat_index_string] wc = cc[:windchill_string] if hi != 'NA' then " with a heat index of #{hi}" elsif wc != 'NA' then " with a windchill of #{wc}" else "" end end end class WeatherPlugin < Plugin def help(plugin, topic="") case topic when "nws" "weather nws => display the current conditions at the location specified by the NOAA National Weather Service station code ( lookup your station code at http://www.nws.noaa.gov/data/current_obs/ )" when "station", "wu" "weather => display the current conditions at the location specified, looking it up on the Weather Underground site; you can use 'station ' to look up data by station code ( lookup your station code at http://www.weatherunderground.com/ )" else "weather information lookup. Looks up weather information for the last location you specified. See topics 'nws' and 'wu' for more information" end end def initialize super @nws_cache = Hash.new @wu_url="http://mobile.wunderground.com/cgi-bin/findweather/getForecast?brand=mobile&query=%s" @wu_station_url="http://mobile.wunderground.com/global/stations/%s.html" end def weather(m, params) loc = nil service = nil if params[:where].empty? if @registry.has_key?(m.sourcenick) where = @registry[m.sourcenick] debug "Loaded weather info #{where.inspect} for #{m.sourcenick}" save = false else debug "No weather info for #{m.sourcenick}" m.reply "I don't know where you are yet, #{m.sourcenick}. See 'help weather nws' or 'help weather wu' for additional help" return end else where = params[:where] save = true unless ['nws','station'].include?(where.first) loc = where.to_s service = :wu end end unless service loc = where[1].to_s service = where.first.to_sym end if loc.empty? debug "No weather location found for #{m.sourcenick}" m.reply "I don't know where you are yet, #{m.sourcenick}. See 'help weather nws' or 'help weather wu' for additional help" end case service when :nws nws_describe(m, loc) when :station wu_station(m, loc) when :wu wu_weather(m, loc) end @registry[m.sourcenick] = [service, loc] if save end def nws_describe(m, where) if @nws_cache.has_key?(where) then met = @nws_cache[where] else met = CurrentConditions.new(where) end if met begin m.reply met.update @nws_cache[where] = met rescue => e m.reply e.message end else m.reply "couldn't find weather data for #{where}" end end def wu_station(m, where) begin xml = @bot.httputil.get_cached(@wu_station_url % URI.escape(where)) case xml when nil m.reply "couldn't retrieve weather information, sorry" return when /(.*?)<\/table>/m data = $1 m.reply wu_weather_filter(data) else debug xml m.reply "something went wrong with the data for #{where}..." end rescue => e m.reply "retrieving info about '#{where}' failed (#{e})" end end def wu_weather(m, where) begin xml = @bot.httputil.get_cached(@wu_url % URI.escape(where)) case xml when nil m.reply "couldn't retrieve weather information, sorry" return when /City Not Found/ m.reply "no such location found (#{where})" return when /
(.*?)<\/table>/m data = $1 m.reply wu_weather_filter(data) when //) m.reply "multiple stations available, use 'weather station ' where code is one of " + stations.join(", ") else debug xml m.reply "something went wrong with the data from #{where}..." end rescue => e m.reply "retrieving info about '#{where}' failed (#{e})" end end def wu_weather_filter(stuff) txt = stuff txt.gsub!(/[\n\s]+/,' ') data = Hash.new txt.gsub!(/ /, ' ') txt.gsub!(/°/, ' ') # degree sign txt.gsub!(/<\/?b>/,'') txt.gsub!(/<\/?span[^<>]*?>/,'') txt.gsub!(/]*?>/,'') txt.gsub!(//,'') result = Array.new if txt.match(/<\/a>\s*Updated:\s*(.*?)\s*Observed at\s*(.*?)\s*<\/td>/) result << ("Weather info for %s (updated on %s)" % [$2, $1]) end txt.scan(/\s*
\s*(.*?)\s*<\/td>\s*\s*(.*?)\s*<\/td>\s*<\/tr>/) { |k, v| unless v.empty? or v == "-" or k =="Raw METAR" result << ("%s: %s" % [k, v]) end } return result.join('; ') end end plugin = WeatherPlugin.new plugin.map 'weather *where', :defaults => {:where => false}