X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=data%2Frbot%2Fplugins%2Fweather.rb;h=cb0a2b186a955bdb61a5861c8ef30a8f71c35c45;hb=b7fad38870b04450feafaf3e96ba4b3011816049;hp=e6284e458e49a0bb84ce97580ec260708509d077;hpb=8aae94a93dd5231a2f910e9f984f56306aea42af;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/data/rbot/plugins/weather.rb b/data/rbot/plugins/weather.rb index e6284e45..cb0a2b18 100644 --- a/data/rbot/plugins/weather.rb +++ b/data/rbot/plugins/weather.rb @@ -1,11 +1,19 @@ +#-- vim:sw=2:et +#++ # -# Weather plugin for rbot -# by MrChucho (mrchucho@mrchucho.net) -# Copyright (C) 2006 Ralph M. Churchill +# :title: Weather plugin for rbot # -require 'open-uri' +# 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 'rexml/document' +# Wraps NOAA National Weather Service information class CurrentConditions def initialize(station) @station = station @@ -27,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}" @@ -58,36 +66,98 @@ private end end -class MyWeatherPlugin < Plugin - +class WeatherPlugin < Plugin + + Config.register Config::BooleanValue.new('weather.advisory', + :default => true, + :desc => "Should the bot report special weather advisories when any is present?") + def help(plugin, topic="") - "weather => display the current conditions at the location specified by the STATION code [Lookup your STATION code at http://www.nws.noaa.gov/data/current_obs/ - this will also store the STATION against your nick, so you can later just say \"weather\", weather => display the current weather at the location you last asked for" + 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/ ); 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 - # this plugin only wants to store strings - class << @registry - def store(val) - val + + @nws_cache = Hash.new + + @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) + if params[:where].empty? + if @registry.has_key?(m.sourcenick) + where = @registry[m.sourcenick] + debug "Loaded weather info #{where.inspect} for #{m.sourcenick}" + + service = where.first.to_sym + loc = where[1].to_s + units = params[:units] || where[2] rescue nil + 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 - def restore(val) - val + else + where = params[:where] + if ['nws','station'].include?(where.first) + service = where.first.to_sym + loc = where[1].to_s + else + service = :wu + loc = where.to_s end + units = params[:units] end - @cc_cache = Hash.new + + 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" + return + end + + wu_units = String.new + if units + case units.to_sym + when :english, :metric + wu_units = "_#{units}" + when :both + else + m.reply "Ignoring unknown units #{units}" + wu_units = String.new + end + end + + case service + when :nws + nws_describe(m, loc) + when :station + wu_station(m, loc, wu_units) + when :wu + wu_weather(m, loc, wu_units) + end + + @registry[m.sourcenick] = [service, loc, units] end - - def describe(m, where) - if @cc_cache.has_key?(where) then - met = @cc_cache[where] + + 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 - @cc_cache[where] = met + @nws_cache[where] = met rescue => e m.reply e.message end @@ -96,20 +166,140 @@ class MyWeatherPlugin < Plugin end end - def weather(m, params) - if params[:where] - @registry[m.sourcenick] = params[:where] - describe(m,params[:where]) - else - if @registry.has_key?(m.sourcenick) - where = @registry[m.sourcenick] - describe(m,where) + def wu_station(m, where, units) + begin + xml = @bot.httputil.get(@wu_station_url % [units, CGI.escape(where)]) + case xml + when nil + m.reply "couldn't retrieve weather information, sorry" + return + when /Search not found:/ + m.reply "no such station found (#{where})" + return + when /(.*?)<\/table>/m + data = $1.dup + m.reply wu_weather_filter(data) + wu_out_special(m, xml) else - m.reply "I don't know where you are yet! Lookup your station at http://www.nws.noaa.gov/data/current_obs/ and tell me 'weather ', then I'll know." + 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, units) + begin + xml = @bot.httputil.get(@wu_url % [units, CGI.escape(where)]) + case xml + when nil + m.reply "couldn't retrieve weather information, sorry" + when /City Not Found/ + m.reply "no such location found (#{where})" + 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 / e + m.reply "retrieving info about '#{where}' failed (#{e})" + end + end + + def wu_clean(stuff) + txt = stuff + txt.gsub!(/[\n\s]+/,' ') + txt.gsub!(/ /, ' ') + txt.gsub!(/°/, ' ') # degree sign + txt.gsub!(/<\/?b>/,'') + txt.gsub!(/<\/?span[^<>]*?>/,'') + txt.gsub!(/]*?>/,'') + txt.gsub!(//,'') + txt + end + + 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, wu_clean(w)]) + else # non-US station + (warning ? "*" : "") + ("station %s (%s): %s" % [loc, par, wu_clean(w)]) + end + } + m.reply stations.join("; ") + end + + 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 + 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) + txt = wu_clean(stuff) + + 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| + next if v.empty? + next if ["-", "- approx.", "N/A", "N/A approx."].include?(v) + next if k == "Raw METAR" + result << ("%s: %s" % [k, v]) + } + return result.join('; ') end end -plugin = MyWeatherPlugin.new -plugin.map 'weather :where', :defaults => {:where => false} +plugin = WeatherPlugin.new +plugin.map 'weather :units *where', :defaults => {:where => false, :units => false}, :requirements => {:units => /metric|english|both/}