1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#-- 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://api.local.yahoo.com/MapsService/V1/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://www.nws.noaa.gov/forecasts/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,"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 <location> => 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)
Thread.new {
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
Thread.exit
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}
|