robut 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -1
- data/Gemfile +4 -1
- data/README.rdoc +82 -23
- data/lib/rexml_patches.rb +26 -0
- data/lib/robut/connection.rb +20 -11
- data/lib/robut/plugin.rb +88 -3
- data/lib/robut/plugin/alias.rb +109 -0
- data/lib/robut/plugin/calc.rb +8 -2
- data/lib/robut/plugin/echo.rb +8 -3
- data/lib/robut/plugin/help.rb +24 -0
- data/lib/robut/plugin/later.rb +13 -9
- data/lib/robut/plugin/lunch.rb +14 -3
- data/lib/robut/plugin/meme.rb +48 -15
- data/lib/robut/plugin/ping.rb +7 -3
- data/lib/robut/plugin/rdio.rb +16 -6
- data/lib/robut/plugin/rdio/server.rb +4 -3
- data/lib/robut/plugin/say.rb +13 -4
- data/lib/robut/plugin/sayings.rb +7 -1
- data/lib/robut/plugin/twss.rb +8 -2
- data/lib/robut/plugin/weather.rb +126 -0
- data/lib/robut/storage/yaml_store.rb +18 -12
- data/lib/robut/version.rb +1 -1
- data/test/fixtures/bad_location.xml +1 -0
- data/test/fixtures/las_vegas.xml +1 -0
- data/test/fixtures/seattle.xml +1 -0
- data/test/fixtures/tacoma.xml +1 -0
- data/test/unit/connection_test.rb +10 -5
- data/test/unit/plugin/alias_test.rb +75 -0
- data/test/unit/plugin/help_test.rb +45 -0
- data/test/unit/plugin/say_test.rb +5 -0
- data/test/unit/plugin/weather_test.rb +100 -0
- data/test/unit/plugin_test.rb +30 -0
- data/test/unit/storage/yaml_store_test.rb +13 -8
- metadata +54 -39
- data/lib/robut/plugin/base.rb +0 -70
- data/test/unit/plugin/base_test.rb +0 -16
data/lib/robut/plugin/calc.rb
CHANGED
@@ -2,15 +2,21 @@ require 'calc'
|
|
2
2
|
|
3
3
|
# A simple calculator. This delegates all calculations to the 'calc'
|
4
4
|
# gem.
|
5
|
-
class Robut::Plugin::Calc
|
5
|
+
class Robut::Plugin::Calc
|
6
|
+
include Robut::Plugin
|
6
7
|
|
8
|
+
# Returns a description of how to use this plugin
|
9
|
+
def usage
|
10
|
+
"#{at_nick} calc <calculation> - replies with the result of <calculation>"
|
11
|
+
end
|
12
|
+
|
7
13
|
# Perform the calculation specified in +message+, and send the
|
8
14
|
# result back.
|
9
15
|
def handle(time, sender_nick, message)
|
10
16
|
if sent_to_me?(message) && words(message).first == 'calc'
|
11
17
|
calculation = words(message, 'calc').join(' ')
|
12
18
|
begin
|
13
|
-
reply("#{calculation} = #{Calc.evaluate(calculation)}")
|
19
|
+
reply("#{calculation} = #{::Calc.evaluate(calculation)}")
|
14
20
|
rescue
|
15
21
|
reply("Can't calculate that.")
|
16
22
|
end
|
data/lib/robut/plugin/echo.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
1
|
# A plugin that tells robut to repeat whatever he's told.
|
3
|
-
class Robut::Plugin::Echo
|
2
|
+
class Robut::Plugin::Echo
|
3
|
+
include Robut::Plugin
|
4
4
|
|
5
5
|
# Responds with +message+ if the command sent to robut is 'echo'.
|
6
6
|
def handle(time, sender_nick, message)
|
@@ -10,5 +10,10 @@ class Robut::Plugin::Echo < Robut::Plugin::Base
|
|
10
10
|
reply(phrase) unless phrase.empty?
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
|
+
# Returns a description of how to use this plugin
|
15
|
+
def usage
|
16
|
+
"#{at_nick} echo <message> - replies to the channel with <message>"
|
17
|
+
end
|
18
|
+
|
14
19
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# When asked for help, responds with a list of commands supported by
|
2
|
+
# all loaded plugins
|
3
|
+
class Robut::Plugin::Help
|
4
|
+
include Robut::Plugin
|
5
|
+
|
6
|
+
# Responds with a list of commands supported by all loaded plugins.
|
7
|
+
def handle(time, sender_nick, message)
|
8
|
+
words = words(message)
|
9
|
+
if sent_to_me?(message) && words.first == 'help'
|
10
|
+
reply("Supported commands:")
|
11
|
+
Robut::Plugin.plugins.each do |plugin|
|
12
|
+
plugin_instance = plugin.new(connection, private_sender)
|
13
|
+
Array(plugin_instance.usage).each do |command_usage|
|
14
|
+
reply(command_usage)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a description of how to use this plugin
|
21
|
+
def usage
|
22
|
+
"#{at_nick} help - displays this message"
|
23
|
+
end
|
24
|
+
end
|
data/lib/robut/plugin/later.rb
CHANGED
@@ -12,11 +12,17 @@
|
|
12
12
|
# @robut in <number> <mins|hrs|secs> [command]
|
13
13
|
#
|
14
14
|
# Where command is the message you want to send to @robut in the future. For
|
15
|
-
# the time denominations it also recognizes minute, minutes, hour, hours,
|
15
|
+
# the time denominations it also recognizes minute, minutes, hour, hours,
|
16
16
|
# second, seconds.
|
17
17
|
#
|
18
|
-
class Robut::Plugin::Later
|
18
|
+
class Robut::Plugin::Later
|
19
|
+
include Robut::Plugin
|
19
20
|
|
21
|
+
# Returns a description of how to use this plugin
|
22
|
+
def usage
|
23
|
+
"#{at_nick} in <number> <mins|hrs|secs> <command> - sends <command> to #{nick} after the specified interval"
|
24
|
+
end
|
25
|
+
|
20
26
|
# Passes +message+ back through the plugin chain if we've been given
|
21
27
|
# a time to execute it later.
|
22
28
|
def handle(time, sender_nick, message)
|
@@ -26,17 +32,15 @@ class Robut::Plugin::Later < Robut::Plugin::Base
|
|
26
32
|
count = $1.to_i
|
27
33
|
scale = $2
|
28
34
|
future_message = at_nick + ' ' + $3
|
29
|
-
|
35
|
+
|
30
36
|
sleep_time = count * scale_multiplier(scale)
|
31
|
-
|
37
|
+
|
32
38
|
reply("Ok, see you in #{count} #{scale}")
|
33
|
-
|
39
|
+
|
34
40
|
connection = self.connection
|
35
41
|
threader do
|
36
42
|
sleep sleep_time
|
37
|
-
|
38
|
-
plugins = Robut::Plugin.plugins.map { |p| p.new(connection, private_sender) }
|
39
|
-
connection.handle_message(plugins, Time.now, sender_nick, future_message)
|
43
|
+
fake_message(Time.now, sender_nick, future_message)
|
40
44
|
end
|
41
45
|
return true
|
42
46
|
end
|
@@ -69,5 +73,5 @@ class Robut::Plugin::Later < Robut::Plugin::Base
|
|
69
73
|
yield
|
70
74
|
end
|
71
75
|
end
|
72
|
-
|
76
|
+
|
73
77
|
end
|
data/lib/robut/plugin/lunch.rb
CHANGED
@@ -1,9 +1,20 @@
|
|
1
1
|
# Where should we go to lunch today?
|
2
|
-
class Robut::Plugin::Lunch
|
2
|
+
class Robut::Plugin::Lunch
|
3
|
+
include Robut::Plugin
|
4
|
+
|
5
|
+
# Returns a description of how to use this plugin
|
6
|
+
def usage
|
7
|
+
[
|
8
|
+
"lunch? / food? - #{nick} will suggest a place to go eat",
|
9
|
+
"#{at_nick} lunch places - lists all the lunch places #{nick} knows about",
|
10
|
+
"#{at_nick} new lunch place <place> - tells #{nick} about a new place to eat",
|
11
|
+
"#{at_nick} remove lunch place <place> - tells #{nick} not to suggest <place> anymore"
|
12
|
+
]
|
13
|
+
end
|
3
14
|
|
4
15
|
# Replies with a random string selected from +places+.
|
5
16
|
def handle(time, sender_nick, message)
|
6
|
-
words = words(message)
|
17
|
+
words = words(message)
|
7
18
|
phrase = words.join(' ')
|
8
19
|
# lunch?
|
9
20
|
if phrase =~ /(lunch|food)\?/i
|
@@ -53,5 +64,5 @@ class Robut::Plugin::Lunch < Robut::Plugin::Base
|
|
53
64
|
def places=(v)
|
54
65
|
store["lunch_places"] = v
|
55
66
|
end
|
56
|
-
|
67
|
+
|
57
68
|
end
|
data/lib/robut/plugin/meme.rb
CHANGED
@@ -1,30 +1,63 @@
|
|
1
|
-
require '
|
1
|
+
require 'cgi'
|
2
2
|
|
3
|
-
# A simple plugin that wraps
|
4
|
-
|
5
|
-
|
3
|
+
# A simple plugin that wraps memecaptain.
|
4
|
+
class Robut::Plugin::Meme
|
5
|
+
include Robut::Plugin
|
6
|
+
|
7
|
+
MEMES = {
|
8
|
+
'bear_grylls' => 'http://memecaptain.com/bear_grylls.jpg',
|
9
|
+
'insanity_wolf' => 'http://memecaptain.com/insanity_wolf.jpg',
|
10
|
+
'most_interesting' => 'http://memecaptain.com/most_interesting.jpg',
|
11
|
+
'philosoraptor' => 'http://memecaptain.com/philosoraptor.jpg',
|
12
|
+
'scumbag_steve' => 'http://memecaptain.com/scumbag_steve.jpg',
|
13
|
+
'town_crier' => 'http://memecaptain.com/town_crier.jpg',
|
14
|
+
'troll_face' => 'http://memecaptain.com/troll_face.jpg',
|
15
|
+
'y_u_no' => 'http://memecaptain.com/y_u_no.jpg',
|
16
|
+
'yao_ming' => 'http://memecaptain.com/yao_ming.jpg',
|
17
|
+
'business_cat' => 'http://memecaptain.com/business_cat.jpg',
|
18
|
+
'all_the_things' => 'http://memecaptain.com/all_the_things.jpg',
|
19
|
+
'fry' => 'http://memecaptain.com/fry.png',
|
20
|
+
'sap' => 'http://memecaptain.com/sap.jpg'
|
21
|
+
}
|
22
|
+
|
23
|
+
# Returns a description of how to use this plugin
|
24
|
+
def usage
|
25
|
+
[
|
26
|
+
"#{at_nick} meme list - lists all the memes that #{nick} knows about",
|
27
|
+
"#{at_nick} meme <meme> <line1>;<line2> - responds with a link to a generated <meme> image using <line1> and <line2>"
|
28
|
+
]
|
29
|
+
end
|
6
30
|
|
7
31
|
# This plugin is activated when robut is sent a message starting
|
8
32
|
# with the name of a meme. The list of generators can be discovered
|
9
33
|
# by running
|
10
|
-
#
|
11
|
-
# meme list
|
34
|
+
#
|
35
|
+
# @robut meme list
|
12
36
|
#
|
13
37
|
# from the command line. Example:
|
14
38
|
#
|
15
|
-
# @robut
|
39
|
+
# @robut meme all_the_things write; all the plugins
|
16
40
|
#
|
17
41
|
# Send message to the specified meme generator. If the meme requires
|
18
42
|
# more than one line of text, lines should be separated with a semicolon.
|
19
43
|
def handle(time, sender_nick, message)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
44
|
+
return unless sent_to_me?(message)
|
45
|
+
words = words(message)
|
46
|
+
command = words.shift.downcase
|
47
|
+
return unless command == 'meme'
|
48
|
+
meme = words.shift
|
49
|
+
|
50
|
+
if meme == 'list'
|
51
|
+
reply("Memes available: #{MEMES.keys.join(', ')}")
|
52
|
+
elsif MEMES[meme]
|
53
|
+
url = CGI.escape(MEMES[meme])
|
54
|
+
line1, line2 = words.join(' ').split(';').map { |line| CGI.escape(line.strip)}
|
55
|
+
meme_url = "http://memecaptain.com/i?u=#{url}&tt=#{line1}"
|
56
|
+
meme_url += "&tb=#{line2}" if line2
|
57
|
+
reply(meme_url)
|
58
|
+
else
|
59
|
+
reply("Meme not found: #{meme}")
|
27
60
|
end
|
28
61
|
end
|
29
|
-
|
62
|
+
|
30
63
|
end
|
data/lib/robut/plugin/ping.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
|
-
|
2
1
|
# A simple plugin that replies with "pong" when messaged with ping
|
3
|
-
class Robut::Plugin::Ping
|
2
|
+
class Robut::Plugin::Ping
|
3
|
+
include Robut::Plugin
|
4
|
+
|
5
|
+
def usage
|
6
|
+
"#{at_nick} ping - responds 'pong'"
|
7
|
+
end
|
4
8
|
|
5
9
|
# Responds "pong" if +message+ is "ping"
|
6
10
|
def handle(time, sender_nick, message)
|
7
11
|
words = words(message)
|
8
12
|
reply("pong") if sent_to_me?(message) && words.length == 1 && words.first.downcase == 'ping'
|
9
13
|
end
|
10
|
-
|
14
|
+
|
11
15
|
end
|
data/lib/robut/plugin/rdio.rb
CHANGED
@@ -5,12 +5,13 @@ require 'robut/plugin/rdio/server'
|
|
5
5
|
# HipChat. +key+ and +secret+ must be set before we can deal with any
|
6
6
|
# Rdio commands. Additionally, you must call +start_server+ in your
|
7
7
|
# Chatfile to start the Rdio web server.
|
8
|
-
class Robut::Plugin::Rdio
|
8
|
+
class Robut::Plugin::Rdio
|
9
|
+
include Robut::Plugin
|
9
10
|
|
10
11
|
class << self
|
11
12
|
# Your Rdio API Key
|
12
13
|
attr_accessor :key
|
13
|
-
|
14
|
+
|
14
15
|
# Your Rdio API app secret
|
15
16
|
attr_accessor :secret
|
16
17
|
|
@@ -30,7 +31,7 @@ class Robut::Plugin::Rdio < Robut::Plugin::Base
|
|
30
31
|
#
|
31
32
|
# ./rdio-call --consumer-key=YOUR_CONSUMER_KEY --consumer-secret=YOUR_CONSUMER_SECRET getPlaybackToken domain=YOUR_DOMAIN
|
32
33
|
attr_accessor :token
|
33
|
-
|
34
|
+
|
34
35
|
# The domain associated with +token+. Defaults to localhost.
|
35
36
|
attr_accessor :domain
|
36
37
|
end
|
@@ -44,6 +45,15 @@ class Robut::Plugin::Rdio < Robut::Plugin::Base
|
|
44
45
|
Server.domain = self.domain || "localhost"
|
45
46
|
end
|
46
47
|
|
48
|
+
# Returns a description of how to use this plugin
|
49
|
+
def usage
|
50
|
+
[
|
51
|
+
"#{at_nick} play <song> - queues <song> for playing",
|
52
|
+
"#{at_nick} play album <album> - queues <album> for playing",
|
53
|
+
"#{at_nick} play track <track> - queues <track> for playing"
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
47
57
|
# Queues songs into the Rdio web player. @nick play search query
|
48
58
|
# will queue the first search result matching 'search query' into
|
49
59
|
# the web player. It can be an artist, album, or song.
|
@@ -69,8 +79,8 @@ class Robut::Plugin::Rdio < Robut::Plugin::Base
|
|
69
79
|
# 'track', it only searches tracks, same for 'album'. Otherwise,
|
70
80
|
# matches both albums and tracks.
|
71
81
|
def search(words)
|
72
|
-
api = Rdio::Api.new(self.class.key, self.class.secret)
|
73
|
-
|
82
|
+
api = ::Rdio::Api.new(self.class.key, self.class.secret)
|
83
|
+
|
74
84
|
if words[1] == "album"
|
75
85
|
query_string = words[2..-1].join(' ')
|
76
86
|
results = api.search(query_string, "Album")
|
@@ -82,5 +92,5 @@ class Robut::Plugin::Rdio < Robut::Plugin::Base
|
|
82
92
|
results = api.search(query_string, "Album,Track")
|
83
93
|
end
|
84
94
|
end
|
85
|
-
|
95
|
+
|
86
96
|
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'sinatra'
|
2
2
|
require 'json'
|
3
3
|
|
4
|
-
class Robut::Plugin::Rdio
|
5
|
-
|
4
|
+
class Robut::Plugin::Rdio
|
5
|
+
include Robut::Plugin
|
6
|
+
|
6
7
|
# A simple server to communicate new Rdio sources to the Web
|
7
8
|
# Playback API. The client will update
|
8
9
|
# Robut::Plugin::Rdio::Server.queue with any new sources, and a call
|
@@ -10,7 +11,7 @@ class Robut::Plugin::Rdio < Robut::Plugin::Base
|
|
10
11
|
class Server < Sinatra::Base
|
11
12
|
|
12
13
|
set :root, File.dirname(__FILE__)
|
13
|
-
|
14
|
+
|
14
15
|
class << self
|
15
16
|
# A list of items that haven't been fetched by the web playback
|
16
17
|
# API yet.
|
data/lib/robut/plugin/say.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
# This is a simple plugin the envokes the "say" command on whatever is passed
|
3
2
|
# Example:
|
4
3
|
#
|
@@ -6,15 +5,25 @@
|
|
6
5
|
#
|
7
6
|
# *Requires that the "say" command is installed and in the path
|
8
7
|
#
|
9
|
-
class Robut::Plugin::Say
|
8
|
+
class Robut::Plugin::Say
|
9
|
+
include Robut::Plugin
|
10
10
|
|
11
|
+
# Returns a description of how to use this plugin
|
12
|
+
def usage
|
13
|
+
"#{at_nick} say <words> - uses Mac OS X's 'say' command to speak <words>"
|
14
|
+
end
|
15
|
+
|
11
16
|
# Pipes +message+ through the +say+ command
|
12
17
|
def handle(time, sender_nick, message)
|
13
18
|
words = words(message)
|
14
19
|
if sent_to_me?(message) && words.first == "say"
|
15
|
-
phrase = words[1..-1].join(' ')
|
20
|
+
phrase = clean(words[1..-1].join(' '))
|
16
21
|
system("say #{phrase}")
|
17
22
|
end
|
18
23
|
end
|
19
|
-
|
24
|
+
|
25
|
+
def clean(str)
|
26
|
+
str.gsub("'", "").gsub(/[^A-Za-z0-9\s]+/, " ").gsub(/\s+/, ' ').strip
|
27
|
+
end
|
28
|
+
|
20
29
|
end
|
data/lib/robut/plugin/sayings.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# A simple regex => response plugin.
|
2
|
-
class Robut::Plugin::Sayings
|
2
|
+
class Robut::Plugin::Sayings
|
3
|
+
include Robut::Plugin
|
3
4
|
|
4
5
|
class << self
|
5
6
|
# A list of arrays. The first element is a regex, the second is
|
@@ -13,6 +14,11 @@ class Robut::Plugin::Sayings < Robut::Plugin::Base
|
|
13
14
|
end
|
14
15
|
self.sayings = []
|
15
16
|
|
17
|
+
# Returns a description of how to use this plugin
|
18
|
+
def usage
|
19
|
+
"#{at_nick} <words> - if <words> matches a regular expression defined in the Chatfile, responds with the specified response"
|
20
|
+
end
|
21
|
+
|
16
22
|
# For each element in sayings, creates a regex out of the first
|
17
23
|
# element, tries to match +message+ to it, and replies with the
|
18
24
|
# second element if it found a match. Robut::Plugin::Sayings will
|
data/lib/robut/plugin/twss.rb
CHANGED
@@ -2,12 +2,18 @@ require 'twss'
|
|
2
2
|
|
3
3
|
# A simple plugin that feeds everything said in the room through the
|
4
4
|
# twss gem. Requires the 'twss' gem, obviously.
|
5
|
-
class Robut::Plugin::TWSS
|
5
|
+
class Robut::Plugin::TWSS
|
6
|
+
include Robut::Plugin
|
7
|
+
|
8
|
+
# Returns a description of how to use this plugin
|
9
|
+
def usage
|
10
|
+
"<words> - responds with \"That's what she said!\" if #{nick} thinks <words> is a valid TWSS"
|
11
|
+
end
|
6
12
|
|
7
13
|
# Responds "That's what she said!" if the TWSS gem returns true for
|
8
14
|
# +message+. Strips out any reference to our nick in +message+
|
9
15
|
# before it stuffs +message+ into the gem.
|
10
16
|
def handle(time, sender_nick, message)
|
11
|
-
reply("That's what she said!") if TWSS(words(message).join(" "))
|
17
|
+
reply("That's what she said!") if ::TWSS.classify(words(message).join(" "))
|
12
18
|
end
|
13
19
|
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
# What's the current weather forecast?
|
5
|
+
class Robut::Plugin::Weather
|
6
|
+
include Robut::Plugin
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :default_location
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns a description of how to use this plugin
|
13
|
+
def usage
|
14
|
+
[
|
15
|
+
"#{at_nick} weather - returns the weather in the default location for today",
|
16
|
+
"#{at_nick} weather tomorrow - returns the weather in the default location for tomorrow",
|
17
|
+
"#{at_nick} weather <location> - returns the weather for <location> today",
|
18
|
+
"#{at_nick} weather <location> Tuesday - returns the weather for <location> Tuesday"
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
def handle(time, sender_nick, message)
|
23
|
+
# ignore messages that don't end in ?
|
24
|
+
return unless message[message.length - 1, 1] == "?"
|
25
|
+
message = message[0..message.length - 2]
|
26
|
+
|
27
|
+
words = words(message)
|
28
|
+
i = words.index("weather")
|
29
|
+
|
30
|
+
# ignore messages that don't have "weather" in them
|
31
|
+
return if i.nil?
|
32
|
+
|
33
|
+
location = i.zero? ? self.class.default_location : words[0..i-1].join(" ")
|
34
|
+
if location.nil?
|
35
|
+
reply "I don't have a default location!"
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
day_of_week = nil
|
40
|
+
day_string = words[i+1..-1].join(" ").downcase
|
41
|
+
if day_string != ""
|
42
|
+
day_of_week = parse_day(day_string)
|
43
|
+
if day_of_week.nil?
|
44
|
+
reply "I don't recognize the date: \"#{day_string}\""
|
45
|
+
return
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if bad_location?(location)
|
50
|
+
reply "I don't recognize the location: \"#{location}\""
|
51
|
+
return
|
52
|
+
end
|
53
|
+
|
54
|
+
if day_of_week
|
55
|
+
reply forecast(location, day_of_week)
|
56
|
+
else
|
57
|
+
reply current_conditions(location)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_day(day)
|
62
|
+
day_map = {
|
63
|
+
"monday" => "Mon",
|
64
|
+
"mon" => "Mon",
|
65
|
+
"tuesday" => "Tue",
|
66
|
+
"tue" => "Tue",
|
67
|
+
"tues" => "Tue",
|
68
|
+
"wed" => "Wed",
|
69
|
+
"wednesday" => "Wed",
|
70
|
+
"thurs" => "Thu",
|
71
|
+
"thu" => "Thu",
|
72
|
+
"thursday" => "Thu",
|
73
|
+
"friday" => "Fri",
|
74
|
+
"fri" => "Fri",
|
75
|
+
"saturday" => "Sat",
|
76
|
+
"sat" => "Sat",
|
77
|
+
"sunday" => "Sun",
|
78
|
+
"sun" => "Sun",
|
79
|
+
}
|
80
|
+
if day_map.has_key?(day)
|
81
|
+
return day_map[day]
|
82
|
+
end
|
83
|
+
|
84
|
+
if day == "tomorrow"
|
85
|
+
return (Time.now + 60*60*24).strftime("%a")
|
86
|
+
end
|
87
|
+
|
88
|
+
if day == "today"
|
89
|
+
return Time.now.strftime("%a")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def current_conditions(location)
|
94
|
+
doc = weather_data(location)
|
95
|
+
condition = doc.search("current_conditions condition").first["data"]
|
96
|
+
temperature = doc.search("current_conditions temp_f").first["data"]
|
97
|
+
normalized_location = doc.search("forecast_information city").first["data"]
|
98
|
+
"Weather for #{normalized_location}: #{condition}, #{temperature}F"
|
99
|
+
end
|
100
|
+
|
101
|
+
def forecast(location, day_of_week)
|
102
|
+
doc = weather_data(location)
|
103
|
+
forecast = doc.search("forecast_conditions").detect{|el| c = el.children.detect{|c| c.name == "day_of_week"}; c && c["data"] == day_of_week}
|
104
|
+
return "Can't find a forecast for #{day_of_week}" if forecast.nil?
|
105
|
+
|
106
|
+
condition = forecast.children.detect{|c| c.name == "condition"}["data"]
|
107
|
+
high = forecast.children.detect{|c| c.name == "high"}["data"]
|
108
|
+
low = forecast.children.detect{|c| c.name == "low"}["data"]
|
109
|
+
normalized_location = doc.search("forecast_information city").first["data"]
|
110
|
+
"Forecast for #{normalized_location} on #{day_of_week}: #{condition}, High: #{high}F, Low: #{low}F"
|
111
|
+
end
|
112
|
+
|
113
|
+
def weather_data(location = "")
|
114
|
+
@weather_data ||= {}
|
115
|
+
@weather_data[location] ||= begin
|
116
|
+
url = "http://www.google.com/ig/api?weather=#{URI.escape(location)}"
|
117
|
+
Nokogiri::XML(open(url))
|
118
|
+
end
|
119
|
+
@weather_data[location]
|
120
|
+
end
|
121
|
+
|
122
|
+
def bad_location?(location = "")
|
123
|
+
weather_data(location).search("forecast_information").empty?
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|