robut 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|