X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=data%2Frbot%2Fplugins%2Fweather.rb;h=cda68e102e5d0567299bd6a52724edc642967d0a;hb=7d2f6b2aa00632f55df231c72cec7661af8845f1;hp=036fbbdd77347399f941a04aa2f576805b7dea68;hpb=1974a4f0d4a6b5634fab6a57277f18a9684334f4;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/data/rbot/plugins/weather.rb b/data/rbot/plugins/weather.rb index 036fbbdd..cda68e10 100644 --- a/data/rbot/plugins/weather.rb +++ b/data/rbot/plugins/weather.rb @@ -1,19 +1,23 @@ -# Weather plugin for rbot +#-- vim:sw=2:et +#++ # -# NOAA National Weather Service support by MrChucho (mrchucho@mrchucho.net) -# Copyright (C) 2006 Ralph M. Churchill +# :title: Weather plugin for rbot # -# Weather Undeground support by Giuseppe "Oblomov" Bilotta -# Copyright (C) 2006 Giuseppe Bilotta +# Author:: MrChucho (mrchucho@mrchucho.net): NOAA National Weather Service support +# Author:: Giuseppe "Oblomov" Bilotta +# +# Copyright:: (C) 2006 Ralph M. Churchill +# Copyright:: (C) 2006-2007 Giuseppe Bilotta +# +# License:: GPL v2 -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" + @url = "http://www.nws.noaa.gov/data/current_obs/#{URI.encode @station.upcase}.xml" @etag = String.new @mtime = Time.mktime(0) @current_conditions = String.new @@ -31,9 +35,9 @@ class CurrentConditions end rescue OpenURI::HTTPError => e case e - when /304/: + when /304/ @iscached = true - when /404/: + when /404/ raise "Data for #{@station} not found" else raise "Error retrieving data: #{e}" @@ -63,66 +67,88 @@ private end class WeatherPlugin < Plugin - + + Config.register Config::BooleanValue.new('weather.advisory', + :default => true, + :desc => "Should the bot report special weather advisories when any is present?") + Config.register Config::EnumValue.new('weather.units', + :values => ['metric', 'english', 'both'], + :default => 'both', + :desc => "Units to be used by default in Weather Underground reports") + + 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/ )" + "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/ ); you can optionally set to 'metric' or 'english' if you only want data with the units; use 'both' for units to go back to having both." 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" + @wu_url = "http://mobile.wunderground.com/cgi-bin/findweather/getForecast?brand=mobile%s&query=%s" + @wu_station_url = "http://mobile.wunderground.com/auto/mobile%s/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 + where = params[:where].to_s + service = params[:service].to_sym rescue nil + units = params[:units] + + if where.empty? or !service or !units and @registry.has_key?(m.sourcenick) + reg = @registry[m.sourcenick] + debug "loaded weather info #{reg.inspect} for #{m.sourcenick}" + service = reg.first.to_sym if !service + where = reg[1].to_s if where.empty? + units = reg[2] rescue nil + end + + if !service + if where.sub!(/^station\s+/,'') + service = :nws 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? + + if where.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" return end + + wu_units = String.new + + case (units || @bot.config['weather.units']).to_sym + when :english, :metric + wu_units = "_#{units}" + when :both + else + m.reply "Ignoring unknown units #{units}" + end + case service when :nws - nws_describe(m, loc) + nws_describe(m, where) when :station - wu_station(m, loc) + wu_station(m, where, wu_units) when :wu - wu_weather(m, loc) + wu_weather(m, where, wu_units) + when :google + google_weather(m, where) + else + m.reply "I don't know the weather service #{service}, sorry" + return end - @registry[m.sourcenick] = [service, loc] if save + + @registry[m.sourcenick] = [service, where, units] end def nws_describe(m, where) @@ -143,9 +169,9 @@ class WeatherPlugin < Plugin end end - def wu_station(m, where) + def wu_station(m, where, units) begin - xml = @bot.httputil.get_cached(@wu_station_url % URI.escape(where)) + xml = @bot.httputil.get(@wu_station_url % [units, CGI.escape(where)]) case xml when nil m.reply "couldn't retrieve weather information, sorry" @@ -154,8 +180,9 @@ class WeatherPlugin < Plugin m.reply "no such station found (#{where})" return when /(.*?)<\/table>/m - data = $1 + data = $1.dup m.reply wu_weather_filter(data) + wu_out_special(m, xml) else debug xml m.reply "something went wrong with the data for #{where}..." @@ -165,22 +192,27 @@ class WeatherPlugin < Plugin end end - def wu_weather(m, where) + def wu_weather(m, where, units) begin - xml = @bot.httputil.get_cached(@wu_url % URI.escape(where)) + xml = @bot.httputil.get(@wu_url % [units, CGI.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(", ") + when /Current<\/a>/ + data = "" + xml.scan(/
(.*?)<\/table>/m).each do |match| + data += wu_weather_filter(match.first) + end + if data.length > 0 + m.reply data + else + m.reply "couldn't parse weather data from #{where}" + end + wu_out_special(m, xml) + when //,'') - txt.gsub!(/<\/?span[^<>]*?>/,'') - txt.gsub!(/]*?>/,'') - txt.gsub!(//,'') + def wu_weather_multi(m, xml) + # debug xml + stations = xml.scan(/\\s+\\s+
\s*(?:]*>]+><\/a>\s*)?(.*?)<\/a>\s*:\s*(.*?)<\/td>/m) + # debug stations + m.reply "multiple stations available, use 'weather station ' or 'weather ' as appropriate, for one of the following (current temp shown):" + stations.map! { |ar| + warning = ar[0] + loc = ar[2] + state = ar[1] + par = ar[3] + w = ar[4] + if state # US station + (warning ? "*" : "") + ("%s, %s (%s): %s" % [loc, state, par, w.ircify_html]) + else # non-US station + (warning ? "*" : "") + ("station %s (%s): %s" % [loc, par, w.ircify_html]) + end + } + m.reply stations.join("; ") + end - 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]) + def wu_check_special(xml) + specials = [] + # We only scan the first half to prevent getting the advisories twice + xml[0,xml.length/2].scan(%r{]*>([^<]+)}) do + special = { + :url => "http://mobile.wunderground.com"+$1, + :type => $2.dup, + :special => $3.dup + } + spec_rx = Regexp.new("(?:.+?)\\s+(.+?)\\s+
", Regexp::MULTILINE) + spec_xml = @bot.httputil.get(special[:url]) + if spec_xml and spec_td = spec_xml.match(spec_rx) + special.merge!(:text => spec_td.captures.first.ircify_html) + end + specials << special 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]) + return specials + end + + def wu_out_special(m, xml) + return unless @bot.config['weather.advisory'] + specials = wu_check_special(xml) + debug specials + specials.each do |special| + special.merge!(:underline => Underline) + if special[:text] + m.reply("%{underline}%{special}%{underline}: %{text}" % special) + else + m.reply("%{underline}%{special}%{underline} @ %{url}" % special) end + end + end + + def wu_weather_filter(stuff) + result = Array.new + if stuff.match(/<\/a>\s*Updated:\s*(.*?)\s*Observed at\s*(.*?)\s*<\/td>/) + result << ("Weather info for %s (updated on %s)" % [$2.ircify_html, $1.ircify_html]) + end + stuff.scan(/\s*\s*(.*?)\s*<\/td>\s*\s*(.*?)\s*<\/td>\s*<\/tr>/m) { |k, v| + kk = k.riphtml + vv = v.riphtml + next if vv.empty? + next if ["-", "- approx.", "N/A", "N/A approx."].include?(vv) + next if kk == "Raw METAR" + result << ("%s: %s" % [kk, vv]) } return result.join('; ') end + + # TODO allow units choice other than lang, find how the API does it + def google_weather(m, where) + botlang = @bot.config['core.language'].intern + if Language::Lang2Locale.key?(botlang) + lang = Language::Lang2Locale[botlang].sub(/.UTF.?8$/,'') + else + lang = botlang.to_s[0,2] + end + + debug "Google weather with language #{lang}" + xml = @bot.httputil.get("http://www.google.com/ig/api?hl=#{lang}&weather=#{CGI.escape where}") + debug xml + weather = REXML::Document.new(xml).root.elements["weather"] + begin + error = weather.elements["problem_cause"] + if error + ermsg = error.attributes["data"] + ermsg = _("no reason specified") if ermsg.empty? + raise ermsg + end + city = weather.elements["forecast_information/city"].attributes["data"] + date = Time.parse(weather.elements["forecast_information/current_date_time"].attributes["data"]) + units = weather.elements["forecast_information/unit_system"].attributes["data"].intern + current_conditions = weather.elements["current_conditions"] + foreconds = weather.elements.to_a("forecast_conditions") + + conds = [] + current_conditions.each { |el| + name = el.name.intern + value = el.attributes["data"].dup + debug [name, value] + case name + when :icon + next + when :temp_f + next if units == :SI + value << "°F" + when :temp_c + next if units == :US + value << "°C" + end + conds << value + } + + forecasts = [] + foreconds.each { |forecast| + cond = [] + forecast.each { |el| + name = el.name.intern + value = el.attributes["data"] + case name + when :icon + next + when :high, :low + value << (units == :SI ? "°C" : "°F") + value << " |" if name == :low + when :condition + value = "(#{value})" + end + cond << value + } + forecasts << cond.join(' ') + } + + m.reply _("Google weather info for %{city} on %{date}: %{conds}. Three-day forecast: %{forecast}") % { + :city => city, + :date => date, + :conds => conds.join(', '), + :forecast => forecasts.join('; ') + } + rescue => e + debug e + m.reply _("Google weather failed: %{e}") % { :e => e} + end + + end + end plugin = WeatherPlugin.new -plugin.map 'weather *where', :defaults => {:where => false} +plugin.map 'weather :units :service *where', + :defaults => { + :where => false, + :units => false, + :service => false + }, + :requirements => { + :units => /metric|english|both/, + :service => /wu|nws|station|google/ + }