#-- vim:sw=2:et #++ # # :title: Forecast plugin for rbot # # Author:: MrChucho (mrchucho@mrchucho.net) # Copyright:: (C) 2006 Ralph M. Churchill require 'soap/wsdlDriver' # TODO why not use HttpUtil instead of open-uri? require 'open-uri' require 'rexml/document' require 'erb' class LatLong include ERB::Util # Determine the latitude and longitude of a location. City, State and/or ZIP # are all valid. # [+return+] latitude,longitude def get_lat_long(loc) loc = url_encode(loc) url="http://where.yahooapis.com/geocode?appid=mrchucho_rbot_weather&location=#{loc}" lat,long = 0,0 begin open(url) do |xmldoc| results = (REXML::Document.new xmldoc).root lat = results.elements["//latitude/text()"].to_s long = results.elements["//longitude/text()"].to_s end rescue => err raise err #? end return lat.to_f,long.to_f end end class Forecast WSDL_URI="http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl" def initialize(lat,long) @lat,@long=lat,long # this extra step is for backward/forward compatibility factory = SOAP::WSDLDriverFactory.new(WSDL_URI) @forecaster=factory.respond_to?(:create_rpc_driver) ? factory.create_rpc_driver : factory.create_driver end def forecast return parse(retrieve),Time.new end private def retrieve forecast = @forecaster.NDFDgenByDay( @lat,@long,Time.now.strftime("%Y-%m-%d"),2,"e","24 hourly") (REXML::Document.new(forecast)).root end def parse(xml) msg = String.new (1..2).each do |day| d = (day==1) ? 'Today' : 'Tomorrow' hi = xml.elements["//temperature[@type='maximum']/value[#{day}]/text()"] lo = xml.elements["//temperature[@type='minimum']/value[#{day}]/text()"] w = xml.elements["//weather/weather-conditions[#{day}]/@weather-summary"] precip_am = xml.elements["//probability-of-precipitation/value[#{day*2-1}]/text()"] precip_pm = xml.elements["//probability-of-precipitation/value[#{day*2}]/text()"] msg += "#{d}: Hi #{hi} Lo #{lo}, #{w}. Precip: AM #{precip_am}% PM #{precip_pm}%\n" end msg end end class ForecastPlugin < Plugin USAGE='forecast => show the 2-day forecast for a location. Location can be any combination of City, State, Country and ZIP' def help(plugin,topic="") USAGE end def usage(m,params={}) m.reply USAGE end def initialize super # this plugin only wants to store strings class << @registry def store(val) val end def restore(val) val end end @forecast_cache = Hash.new @cache_mutex = Mutex.new end def forecast(m,params) if params[:location] and params[:location].any? loc = params[:location].join @registry[m.sourcenick] = loc get_forecast(m,loc) else if @registry.has_key?(m.sourcenick) then loc = @registry[m.sourcenick] get_forecast(m,loc) else m.reply "Please specifiy the City, State or ZIP" end end end def get_forecast(m,loc) begin @cache_mutex.synchronize do if @forecast_cache.has_key?(loc) and Time.new - @forecast_cache[loc][:date] < 3600 forecast = @forecast_cache[loc][:forecast] if forecast m.reply forecast return end end end begin l = LatLong.new f = Forecast.new(*l.get_lat_long(loc)) forecast,forecast_date = f.forecast rescue => err m.reply err end if forecast m.reply forecast @cache_mutex.synchronize do @forecast_cache[loc] = { :forecast => forecast, :date => forecast_date } end else m.reply "Couldn't find forecast for #{loc}" end rescue => e m.reply "ERROR: #{e}" end end end plugin = ForecastPlugin.new plugin.map 'forecast *location', :defaults => {:location => false}, :thread => true