Skip to content

Instantly share code, notes, and snippets.

@mdusher
Created January 20, 2015 08:32
Show Gist options
  • Save mdusher/9828d12fe652a00e5694 to your computer and use it in GitHub Desktop.
Save mdusher/9828d12fe652a00e5694 to your computer and use it in GitHub Desktop.
Crappy Weather plugin for toast (cinch bot)
#! /usr/bin/env ruby
#
# Forecasts ftp://ftp2.bom.gov.au/anon/gen/fwo/IDA00001.dat
#
#
require 'cinch'
require 'nokogiri'
require 'open-uri'
require 'cgi'
require 'json'
require 'yaml'
module Cinch::Plugins
class MultiWeather2
include Cinch::Plugin
match /^w\s(.*)/i
match /^w$/i, method: :execute
match /^fc$/i, method: :forecast
match /^fc\s(.*)$/i, method: :forecast
match /^7fc\s(.*)$/i, method: :forecast7day
match /^7fc$/i, method: :forecast7day
match /^setw$/i, method: :get_pref
match /^setw\s(.*)/i, method: :set_pref
def initialize(*)
super
@yaml = "/home/services/toast/prefs/openWeather.yaml"
@prefs = Hash.new
if File.exist?(@yaml)
@prefs = YAML.load_file(@yaml)
end
end
def execute(m, query=nil)
if (query.nil?)
query = @prefs[m.user.name] || nil
end
if (query)
m.reply weather(query)
end
end
def set_pref(m, query)
@prefs[m.user.name] = query
File.open(@yaml,'w') { |p| p.write YAML.dump(@prefs) }
m.user.send "Set weather preference for %s to: %s" % [m.user.name, query]
end
def get_pref(m)
m.user.send "Weather preference for %s is set to: %s" % [m.user.name, @prefs[m.user.name] || ""]
end
def weather(query)
case query
when /northam$/i then agricwa() #rescue openWeather("Northam, WA")
else openWeather(query)
end
end
def openWeatherCoords(query)
url = "http://api.openweathermap.org/data/2.5/weather?units=metric&mode=json&q=%s" % CGI.escape(query)
jsondata = JSON.load(open(url)) rescue nil
if (jsondata.nil?)
return nil
elsif (jsondata["cod"] == "404")
return nil
else
w = {
coords: "%s,%s" % [jsondata["coord"]["lat"],jsondata["coord"]["lon"]],
name: "%s" % jsondata["name"],
country: "%s" % [jsondata["sys"]["country"]]
}
end
end
def openWeather(query)
url = "http://api.openweathermap.org/data/2.5/weather?units=metric&mode=json&q=%s" % CGI.escape(query)
jsondata = JSON.load(open(url)) rescue nil
if (jsondata["cod"] == "404")
return "Unable to find co-ordinates for %s" % query
else
ow = {
name: "%s %s" % [jsondata["name"],jsondata["sys"]["country"]],
coords: "%s,%s" % [jsondata["coord"]["lat"],jsondata["coord"]["lon"]],
temp: "%s" % jsondata["main"]["temp"],
humidity: "%s" % jsondata["main"]["humidity"],
wind: "%sm/s" % jsondata["wind"]["speed"],
source: "OpenWeatherMap"
}
bw = bom ow[:coords]
if (!bw.nil?)
ow = bw
"%s: %s (feels %s) %s | humidity: %s | wind: %s %s [%s/%s]" % [ow[:station], ow[:temp], ow[:feels], ow[:summary], ow[:humidity], ow[:windspeed], ow[:winddirection], ow[:source], ow[:update]]
elsif (ow.nil?)
"Unable to find weather for %s" % [query]
else
"%s: %sC | humidity: %s | wind: %s [%s]" % [ow[:name],ow[:temp],ow[:humidity],ow[:wind],ow[:source]]
end
end
end
def checkText(obj,ret="N/A")
data = obj.respond_to?(:text) ? obj.text.strip : ret
if (!obj.nil?)
if data.include?("\u00B0".encode('utf-8')) && (data =~ /\d+\.\d+.$/)
data+"C"
else
data
end
end
end
def bom(query)
base = "http://www.bom.gov.au"
url = "/places/search/?q=%s" % CGI.escape(query)
nope = nil
w = Nokogiri::HTML(open(base+url)) rescue nil
if (!w.nil?)
obs = Nokogiri::HTML(open(base+w.at_css("ul.menu li.obs a").attr("href")), nil, "UTF-8") rescue nil
if (!obs.nil?)
fc = Nokogiri::HTML(open(base+w.at_css("ul.menu li.forecast a").attr("href")), nil, "UTF-8") rescue nil
if (!fc.nil?)
ow = {
temp: checkText(obs.at_css("div.obs-summary p")).gsub(/Current Temperature/,"").gsub(/ /,"").strip,
feels: checkText(obs.css("table.summary tr td")[1]).gsub(/ /,""),
humidity: checkText(obs.css("table.summary tr td")[0]),
winddirection: checkText(obs.css("table.wind tr td")[0]),
windspeed: checkText(obs.css("table.wind tr td")[1]).gsub(/\d+\sknot./,"").strip,
station: checkText(w.at_css("p.station-name a")),
summary: checkText(w.at_css("dl.forecast-summary dd.summary")),
update: checkText(w.at_css("div#summaries div.wrapper ul li.summary h3")).strip.split.last,
source: "BOM"
}
if (ow[:temp] == "N/A")
return nil
else
return ow
end
else
nope = 1
end
else
nope = 3
end
else
nope = 2
end
if (!nope.nil?)
return nil
end
end
def agricwa
url = "https://www.agric.wa.gov.au/apex/edw/f?p=102:22:::::P22_STATION_CODE,P22_STATION_NAME:NO,Northam"
page = Nokogiri::HTML(openrh(url)) || ""
if (page.nil?)
openWeather("Northam, WA")
else
ow = {}
ow[:station] = "Northam, WA"
ow[:source] = "AgricWA"
rows = page.css("tr.highlight-row")
rows.each do |r|
key = r.css("td")[0].text
value = r.css("td")[1].text
if (key =~ /Air Temp/i)
ow[:temp] = value || "N/A"
elsif (key =~ /Feels Like/i)
ow[:feels] = value || "N/A"
elsif (key =~ /Humidity/i)
ow[:humidity] = value || "N/A"
elsif (key =~ /Rain from 9am/i)
ow[:rain] = value || "N/A"
elsif (key =~ /Wind @ 3m$/i)
ow[:wind] = value || "N/A"
elsif (key =~ /Last update/i)
ow[:update] = value || "N/A"
end
end
"%s: %s (feels like %s) | humidity: %s | wind: %s | rain: %s [%s - %s]" % [ow[:station],ow[:temp],ow[:feels],ow[:humidity],ow[:wind],ow[:rain],ow[:source],ow[:update]]
end
end
def forecast(m, query=nil)
if (query.nil?)
query = @prefs[m.user.name] || nil
end
if (query)
m.reply bomForecast(query)
else
m.reply "You need to set or provide a location (setw <location>|w <location>)"
end
end
def bomForecast(query)
l = openWeatherCoords(query)
nope = nil
if (!l[:coords].nil?)
base = "http://www.bom.gov.au"
url = "/places/search/?q=%s" % l[:coords]
w = Nokogiri::HTML(open(base+url), nil, "UTF-8") rescue nil
if (!w.nil?)
fc = Nokogiri::HTML(open(base+w.at_css("ul.menu li.forecast a").attr("href")), nil, "UTF-8") rescue nil
if (!fc.nil?)
fcday = fc.at_css("div.day")
ow = {
name: checkText(w.css("ul.breadcrumbs li").last).gsub(/Forecast/,"").gsub(/ Weather/,""),
fchead: checkText(fcday.at_css("h2"), nil).gsub(/Forecast for the rest of /,"").strip.split.first,
fcsumm: checkText(fcday.at_css("p"), nil),
fcmin: checkText(fcday.at_css("div.forecast dl dd.min"),nil),
fcmax: checkText(fcday.at_css("div.forecast dl dd.max"),nil)
}
if (ow[:name].include?("\u00B0".encode('utf-8')))
ow[:name] = checkText(fc.at_css("div.day div.forecast h3")).gsub(/ area/,"")
end
if (!ow[:fcmin].nil? && !ow[:fcmax].nil?)
fcminmax = "(%s-%s) " % [ow[:fcmin].split[0], ow[:fcmax].gsub(/ /,"")]
elsif (ow[:fcmin].nil? && !ow[:fcmax].nil?)
fcminmax = "(Max: %s) " % [ow[:fcmax].gsub(/ /,"")]
else
fcminmax = ""
end
return "%s Forecast for %s: %s%s" % [ow[:name], ow[:fchead], fcminmax, ow[:fcsumm]]
else
nope = 1
end
else
nope = 2
end
else
nope = 3
end
if (!nope.nil?)
return "Unable to retrieve forecast for %s (%s)" % [query, l[:coords] || "N/A"]
end
end
def forecast7day(m, query=nil)
if (query.nil?)
query = @prefs[m.user.name] || nil
end
if (query)
m.reply openForecast7day(query)
else
m.reply "You need to set or provide a location (setw <location>|w <location>)"
end
end
def openForecast7day(query)
url = "http://api.openweathermap.org/data/2.5/forecast/daily?units=metric&mode=json&cnt=7&q=%s" % CGI.escape(query)
jsondata = JSON.load(open(url)) rescue nil
if (jsondata["cod"] == "404")
return "Unable to find 7 day forecast data for \"%s\"" % query
else
msg = "%s, %s Forecast " % [jsondata["city"]["name"],jsondata["city"]["country"]]
days = jsondata["list"]
days.each do |d|
msg += "| %s %s-%sC %s " % [Time.at(d["dt"]).strftime("%a"), d["temp"]["min"].to_i, d["temp"]["max"].to_i, d["weather"].first["description"].capitalize]
end
b = bomForecast7day(jsondata["city"]["name"])
if (b.include?("|"))
return "#{jsondata["city"]["name"]} Forecast "+b+" [BOM]"
else
return msg+" [OpenWeather]"
end
end
end
def bomForecast7day(query)
nope = nil
bomurl = "http://m.bom.gov.au"
url = bomurl+"/search/?q=%s" % CGI.escape(query)
w = Nokogiri::HTML(open(url),nil, "UTF-8") rescue nil
if (!w.nil?)
if (w.at_css("section.results") && w.at_css("section.results ul a"))
w = Nokogiri::HTML(open(bomurl+w.at_css("section.results ul a")[:href]), nil, "UTF-8")
end
if (!w.nil?)
fc = ""
w.css("div.forecast-day").each do |d|
fc += "| %s %s-%s %s " % [ d.at_css("div.text h3").text,
checkText(d.at_css("div.low")).gsub(/Min/,""),
checkText(d.at_css("div.high")).gsub(/Max/,""),
(!checkText(d.at_css("div.text p")).include?("Rainfall") ? checkText(d.at_css("div.text p")) : "") ]
end
return fc
else
nope = 1
end
else
nope = 1
end
if (!nope.nil?)
return "Unable to find 7 day forecast for %s" % query
end
end
def openrh(url)
begin
data = open(url, redirect: false, "Cookie" => ($cookie || ""))
rescue OpenURI::HTTPRedirect => e
$cookie = e.io.meta["set-cookie"]
data = open(url, "Cookie" => $cookie,)
end
data
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment