lib/Zeta/plugins/weather.rb in zetabot-1.0.7 vs lib/Zeta/plugins/weather.rb in zetabot-1.1.0

- old
+ new

@@ -19,163 +19,174 @@ ) match /w (.+)/, method: :weather match 'w', method: :weather match /setw (.+)/, method: :set_location - match /setws (.+)/, method: :set_source match /wx (.+)/, method: :weather match /weather (.+)/, method: :weather + match /almanac (.+)/, method: :almanac + match /hurricane/, method: :hurricane ##### def initialize(*args) - @api_src = %w{wu noaa ds owm} - @src_translated = {wu: 'Wunderground', noaa: 'NOAA', ds: 'DarkSky', owm: 'Open Weather Map'} - @store = Persist.new(File.join(Dir.home, '.zeta', 'cache', 'weather.pstore')) - @source = Persist.new(File.join(Dir.home, '.zeta', 'cache', 'weather_source.pstore')) + @api_src = %w{wu noaa darksky owm} + @store = Persist.new(File.join(Dir.home, '.zeta', 'cache', 'weather.pstore')) super end # ?w <location> def weather(msg, query=nil) - if !@source[msg.user.to_s].nil? - stored_source = @source[msg.user.to_s] - else - stored_source = 'wu' - end - + # Pull data source and scrub query + # Lookup user from pstore if !@store[msg.user.to_s].nil? && query.nil? - data = send("#{stored_source}_src", msg, @store[msg.user.to_s], true) + stored_location, stored_source = @store[msg.user.to_s].split('::') + stored_source = @api_src.include?(stored_source) ? stored_source : 'wu' + data = send("#{stored_source}_src", stored_location) + # location = geolookup(@store[msg.user.to_s]) + # data = wunderground_src(stored_location, false) elsif query.nil? - return msg.reply 'No location set. ?setw <location> and ?setws <source> for where you get the weather from' + return msg.reply 'No location set. ?setw <location> :(wu|darkscy|noaa|apixu|owm)' else # data = wu_src(query, true) + src = query[/:\w+/].gsub(/:/, '') if query[/:\w+/] query = query.gsub(/:\w+/, '').strip if query - data = send("#{stored_source}_src", msg, query) + true_src = @api_src.include?(src) ? src : 'wu' + data = send("#{true_src}_src", query) end - - return msg.reply "No results found for #{query} with #{@src_translated[stored_source.to_sym]} source." if data.nil? + return msg.reply "No results found for #{query} with #{true_src} source." if data.nil? + # return msg.reply 'Problem getting data. Try again later.' if data.nil? msg.reply(data.reply) end # ?setw <location> def set_location(msg, query) # Establish source + src = query[/:\w+/].gsub(/:/, '') if query[/:\w+/] query = query.gsub(/:\w+/, '').strip if query # Sanity Check - if !@source[msg.user.to_s].nil? - stored_source = @source[msg.user.to_s] - else - stored_source = 'wu' - end - data = send("#{stored_source}_src", msg,query) + true_src = @api_src.include?(src) ? src : 'wu' + data = send("#{true_src}_src", query) # Error return msg.reply "No results found for #{query}." if data.nil? # Store and display general location - - serial_location = "#{data.lat},#{data.lon}" + serial_location = "#{query}::#{src}" @store[msg.user.to_s] = serial_location unless data.nil? - msg.reply "Your location is now set to #{data.ac.formatted_address}!" + msg.reply "Your location is now set to #{data.ac.name}, #{data.ac.c}!" end - # ?setws <source> - def set_source(msg,src) - return msg.reply("You can set your weather source using ?setws <omw|ds|wu|noaa>") if src.nil? - true_src = @api_src.include?(src) ? src : 'wu' - @source[msg.user.to_s] = true_src - msg.reply "Your weather source is set to #{@src_translated[true_src.to_sym]}!" + # ?hurricane + def hurricane(msg) + url = URI.encode "http://api.wunderground.com/api/#{Zsec.wunderground}/currenthurricane/view.json" + location = JSON.parse( + # RestClient.get(url) + open(url).read + ) + return msg.reply "No results found for #{query}." if location.nil? + reply_msg = "∴ #{location['currenthurricane'][0]['stormInfo']['stormName_Nice']} " \ + "(#{location['currenthurricane'][0]['stormInfo']['stormNumber']}) "\ + "≈ Category #{location['currenthurricane'][0]['Current']['SaffirSimpsonCategory']} " \ + "≈ Wind #{location['currenthurricane'][0]['Current']['WindSpeed']['Mph']} mph " \ + "(#{location['currenthurricane'][0]['Current']['WindSpeed']['Kph']} kph) " \ + "≈ Wind Gust #{location['currenthurricane'][0]['Current']['WindGust']['Mph']} mph " \ + "(#{location['currenthurricane'][0]['Current']['WindGust']['Kph']} kph) " \ + "≈ #{location['currenthurricane'][0]['Current']['Time']['pretty']} ∴" + msg.reply(reply_msg) end + # ?almanac <location> + def almanac(msg, locale) + autocomplete = JSON.parse(open(URI.encode("http://autocomplete.wunderground.com/aq?query=#{locale}")).read) + url = URI.encode("http://api.wunderground.com/api/#{Config.secrets[:wunderground]}/almanac/#{autocomplete['RESULTS'][0]['l']}.json") + location = JSON.parse( + # RestClient.get(url) + open(url).read + ) + return msg.reply "No results found for #{query}." if location.nil? + + time = Time.now() + + data = OpenStruct.new( + date: time.strftime('%B, %d %Y (%A) '), + airport: location['almanac']['airport_code'], + high_norm_f: location['almanac']['temp_high']['normal']['F'], + high_norm_c: location['almanac']['temp_high']['normal']['C'], + high_record_y: location['almanac']['temp_high']['recordyear'], + high_record_f: location['almanac']['temp_high']['record']['F'], + high_record_c: location['almanac']['temp_high']['normal']['C'], + low_norm_f: location['almanac']['temp_low']['normal']['F'], + low_norm_c: location['almanac']['temp_low']['normal']['C'], + low_record_y: location['almanac']['temp_low']['recordyear'], + low_record_f: location['almanac']['temp_low']['record']['F'], + low_record_c: location['almanac']['temp_low']['normal']['C'], + ) + + reply_msg = "∴ Almanac #{data.date} ≈ Airport #{data.airport} " \ + "≈ Normal #{data.high_norm_f} F (#{data.high_norm_c} C) | #{data.low_norm_f} F (#{data.low_norm_c} C) " \ + "≈ High #{data.high_record_f} F (#{data.high_record_c} C) [#{data.high_record_y}] " \ + "≈ Low #{data.low_record_f} F (#{data.low_record_c} C) [#{data.low_record_y}] ∴" + + msg.reply(reply_msg) + end + + + # -private private #### Weather Sources # Weather Underground - https://wunderground.com - def wu_src(msg, location, stored=false) + def wu_src(location) # Fuzzy location lookup - # ac = JSON.parse( - # open(URI.encode("https://autocomplete.wunderground.com/aq?query=#{location}")).read, - # object_class: OpenStruct - # ) - # return nil if ac.RESULTS.empty? - if stored - lat, lon = @store[msg.user.to_s].split(',') - else + ac = JSON.parse( + open(URI.encode("https://autocomplete.wunderground.com/aq?query=#{location}")).read, + object_class: OpenStruct + ) + return nil if ac.RESULTS.empty? - ac = JSON.parse( - open(URI.encode("https://maps.googleapis.com/maps/api/geocode/json?address=#{location}&key=#{Config.secrets[:google]}")).read, - object_class: OpenStruct - ) - - info(ac.status) - return nil if ac.results.nil? - if ac.status == "OVER_QUERY_LIMIT" - msg.reply("Google API - Over query Limit") - return nil - end - - ac = ac.results[0] - lat = ac.geometry.location.lat - lon = ac.geometry.location.lng - localname = ac.formatted_address - end - # ac = ac.RESULTS[0] + ac = ac.RESULTS[0] geolookup = JSON.parse( - # open(URI.encode("https://api.wunderground.com/api/#{Config.secrets[:wunderground]}/geolookup/#{ac.l}.json")).read, - open(URI.encode("https://api.wunderground.com/api/#{Config.secrets[:wunderground]}/geolookup/q/#{lat},#{lon}.json")).read, + open(URI.encode("https://api.wunderground.com/api/#{Config.secrets[:wunderground]}/geolookup/#{ac.l}.json")).read, object_class: OpenStruct ).location.l rescue nil # Get Data data = JSON.parse( open("https://api.wunderground.com/api/#{Config.secrets[:wunderground]}/alerts/conditions#{geolookup}.json").read, object_class: OpenStruct ) + debug "DATA: #{data}" data.ac = ac - data.lat = lat - data.lon = lon - localname = data.current_observation.display_location.full if localname.nil? current = data.current_observation alerts = data.alerts.empty? ? 'none' : data.alerts.map {|l| l['type']}.join(',') - pressure_si = Unitwise((current.pressure_in.to_f)+14.7, '[psi]').convert_to('kPa').to_f.round(2) + # pressure_si = Unitwise((current.pressure_in.to_f)+14.7, '[psi]').convert_to('kPa').to_f.round(2) - data.reply = "WU ∴ #{localname} " \ + data.reply = "WU ∴ #{ac.name}, #{ac.c} " \ "≈ #{current.weather} #{current.temperature_string} " \ "≈ Feels like #{current.feelslike_string} " \ "≈ Humidity: #{current.relative_humidity} " \ - "≈ Pressure: #{current.pressure_in} psi (#{pressure_si} kPa) " \ + "≈ Pressure: #{current.pressure_in} in/Hg (#{current.pressure_mb} mbar) " \ "≈ Wind: #{current.wind_string} ≈ Alerts: #{alerts} ∴" return data # rescue # return nil end # Open Weather map - https://openweathermap.org/api - def owm_src(msg, location, stored=false) - if stored - lat, lon = @store[msg.user.to_s].split(',') - else + def owm_src(location) + ac = JSON.parse( + open(URI.encode("http://maps.googleapis.com/maps/api/geocode/json?address=#{location}")).read, + object_class: OpenStruct + ) - ac = JSON.parse( - open(URI.encode("https://maps.googleapis.com/maps/api/geocode/json?address=#{location}&key=#{Config.secrets[:google]}")).read, - object_class: OpenStruct - ) - return nil if ac.results.nil? ## Unable to locate + return nil if ac.results.nil? ## Unable to locate - info(ac.status) - if ac.status == "OVER_QUERY_LIMIT" - msg.reply("Google API - Over query Limit") - return nil - end + ac = ac.results[0] + lat = ac.geometry.location.lat + lon = ac.geometry.location.lng - ac = ac.results[0] - lat = ac.geometry.location.lat - lon = ac.geometry.location.lng - localname = ac.formatted_address - end - # Get Data data = JSON.parse( open( URI.encode("https://api.openweathermap.org/data/2.5/weather?lat=#{lat}&lon=#{lon}&APPID=#{Config.secrets[:owm]}") ).read, @@ -184,139 +195,97 @@ temp = Unitwise(data.main.temp, 'K') # Data is given in kelvin pressure = Unitwise((data.main.pressure.to_f/10)+101, 'kPa') wind = Unitwise(data.wind.speed, 'kilometer') - localname = data.name if localname.nil? - - data.lat = lat - data.lon = lon - data.reply = "OWM ∴ #{localname} " \ + data.reply = "OWM ∴ #{ac.formatted_address} " \ "≈ #{data.weather[0].description}, #{temp.convert_to('[degF]').to_i.round(2)} F (#{temp.convert_to('Cel').to_i.round(2)} C) " \ "≈ Humidity: #{data.main.humidity}% " \ - "≈ Pressure: #{pressure.convert_to('[psi]').to_f.round(2)} psi (#{pressure.convert_to('kPa').to_f} kPa) " \ + "≈ Pressure: #{pressure.convert_to('[psi]').to_f.round(2)} in/Hg (#{data.main.pressure} mbar) " \ "≈ Wind: #{wind.convert_to('mile').to_i.round(2)} mph (#{wind.to_i.round(2)} km/h) ∴" return data end # DarkSky - https://darksky.net/dev - def ds_src(msg,location,stored=false) - + def darksky_src(location) ac = JSON.parse( - open(URI.encode("https://maps.googleapis.com/maps/api/geocode/json?address=#{location}&key=#{Config.secrets[:google]}")).read, + open(URI.encode("http://maps.googleapis.com/maps/api/geocode/json?address=#{location}")).read, object_class: OpenStruct ) return nil if ac.results.nil? ## Unable to locate - info(ac.status) - if ac.status == "OVER_QUERY_LIMIT" - msg.reply("Google API - Over query Limit") - return nil - end - ac = ac.results[0] - if stored - lat, lon = @store[msg.user.to_s].split(',') - else - lat = ac.geometry.location.lat - lon = ac.geometry.location.lng - end + lat = ac.geometry.location.lat + lon = ac.geometry.location.lng - localname = ac.formatted_address - data = JSON.parse( open( URI.encode("https://api.darksky.net/forecast/#{Config.secrets[:darksky]}/#{lat},#{lon}") ).read, object_class: OpenStruct ) - data.ac = ac - data.lat = lat - data.lon = lon - - current = data.currently alerts = data.alerts.count rescue 0 c = Unitwise(current.temperature, '[degF]').convert_to('Cel').to_i c_feels = Unitwise(current.apparentTemperature, '[degF]').convert_to('Cel').to_i p = Unitwise((current.pressure.to_f/10)+101, 'kPa') gusts = Unitwise(current.windGust, 'mile').convert_to('kilometer').to_i tempstring = "#{current.temperature.to_i} F (#{c} C)" feelslike = "#{current.apparentTemperature.to_i} F (#{c_feels} C)" - data.reply = "DS ∴ #{localname} " \ + data.reply = "DS ∴ #{ac.formatted_address} " \ "≈ #{current.summary} #{tempstring} " \ "≈ Feels like #{feelslike} " \ "≈ Humidity: #{current.relative_humidity} " \ - "≈ Pressure: #{p.convert_to('[psi]').to_f.round(2)} psi (#{p.to_f} kPa) " \ + "≈ Pressure: #{p.convert_to('[psi]').to_f.round(2)} in/Hg (#{current.pressure} mbar) " \ "≈ Wind: gusts upto #{current.windGust} mph (#{gusts} km/h) ≈ Alerts: #{alerts} ∴" return data # rescue # return nil end # NOAA - https://graphical.weather.gov/xml/ - def noaa_src(msg, location, stored=false) - if stored - lat, lon = @store[msg.user.to_s].split(',') - else + def noaa_src(location) + ac = JSON.parse( + open(URI.encode("http://maps.googleapis.com/maps/api/geocode/json?address=#{location}")).read, + object_class: OpenStruct + ) + return nil if ac.results.nil? ## Unable to locate - ac = JSON.parse( - open(URI.encode("https://maps.googleapis.com/maps/api/geocode/json?address=#{location}&key=#{Config.secrets[:google]}")).read, - object_class: OpenStruct - ) - return nil if ac.results.nil? ## Unable to locate + ac = ac.results[0] + lat = ac.geometry.location.lat + lon = ac.geometry.location.lng - info(ac.status) - if ac.status == "OVER_QUERY_LIMIT" - msg.reply("Google API - Over query Limit") - return nil - end - - ac = ac.results[0] - lat = ac.geometry.location.lat - lon = ac.geometry.location.lng - localname = ac.formatted_address - end - - station_list = JSON.parse( + stations = JSON.parse( open(URI.encode("https://api.weather.gov/points/#{lat},#{lon}/stations/")).read ) rescue nil - return nil if station_list.nil? ## Unable to find station. probably not in the USA + return nil if stations.nil? ## Unable to find station. probably not in the USA - station = JSON.parse( - open(URI.encode("#{station_list['observationStations'][0]}")).read, - object_class: OpenStruct - ) - parsed = JSON.parse( - open(URI.encode("#{station_list['observationStations'][0]}/observations/current")).read, + open(URI.encode("#{stations['observationStations'][0]}/observations/current")).read, object_class: OpenStruct ) - localname = station.properties.name if localname.nil? data = parsed.properties data.ac = ac - data.lat = lat - data.lon = lon f = data.temperature.value * 9/5 temp = "#{f.round(2)} F (#{data.temperature.value.to_i.round(2)} C) " wind = "Gusts: #{data.windGust.value} avg: #{data.windSpeed.value.to_i.round(2)}" feelslike = "#{data.windChill.value.to_i.round(2)} C" pressure = Unitwise(data.barometricPressure.value.to_f+101325, 'Pa') - data.reply = "NOAA ∴ #{localname} " \ + data.reply = "NOAA ∴ #{ac.formatted_address} " \ "≈ #{data.textDescription} #{temp} " \ "≈ Feels like #{feelslike} " \ "≈ Humidity: #{data.relativeHumidity.value.round(2)} " \ - "≈ Pressure: #{pressure.convert_to('[psi]').to_f.round(2)} psi (#{pressure.convert_to('kPa').to_f} kPa) " \ + "≈ Pressure: #{pressure.convert_to('[psi]').to_f.round(2)} in/Hg (#{pressure.convert_to('kPa').to_f} mbar) " \ "≈ Wind: #{wind} ≈ Alerts: ∴" return data # rescue # data.reply = "Error fetching data" end