lita-onewheel-forecast-io 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,177 @@
1
+ module ForecastIo
2
+ module IrcHandlers
3
+ #-# Handlers
4
+ def handle_irc_forecast(response)
5
+ location = geo_lookup(response.user, response.match_data[1])
6
+ forecast = get_forecast_io_results(response.user, location)
7
+ response.reply location.location_name + ' ' + forecast_text(forecast)
8
+ end
9
+
10
+ def handle_irc_ansirain(response)
11
+ location = geo_lookup(response.user, response.match_data[1])
12
+ forecast = get_forecast_io_results(response.user, location)
13
+ response.reply location.location_name + ' ' + ansi_rain_forecast(forecast)
14
+ end
15
+
16
+ def handle_irc_ascii_rain(response)
17
+ location = geo_lookup(response.user, response.match_data[1])
18
+ forecast = get_forecast_io_results(response.user, location)
19
+ response.reply location.location_name + ' ' + ascii_rain_forecast(forecast)
20
+ end
21
+
22
+ def handle_irc_all_the_things(response)
23
+ location = geo_lookup(response.user, response.match_data[1])
24
+ forecast = get_forecast_io_results(response.user, location)
25
+ response.reply location.location_name + ' ' + forecast_text(forecast)
26
+ response.reply location.location_name + ' ' + ansi_rain_forecast(forecast)
27
+ response.reply location.location_name + ' ' + ansi_rain_intensity_forecast(forecast)
28
+ response.reply location.location_name + ' ' + ansi_temp_forecast(forecast)
29
+ response.reply location.location_name + ' ' + ansi_wind_direction_forecast(forecast)
30
+ response.reply location.location_name + ' ' + do_the_sun_thing(forecast)
31
+ response.reply location.location_name + ' ' + do_the_cloud_thing(forecast)
32
+ response.reply location.location_name + ' ' + do_the_daily_rain_thing(forecast)
33
+ end
34
+
35
+ def handle_irc_ansirain_intensity(response)
36
+ location = geo_lookup(response.user, response.match_data[1])
37
+ forecast = get_forecast_io_results(response.user, location)
38
+ response.reply location.location_name + ' ' + ansi_rain_intensity_forecast(forecast)
39
+ end
40
+
41
+ def handle_irc_ansitemp(response)
42
+ location = geo_lookup(response.user, response.match_data[1])
43
+ forecast = get_forecast_io_results(response.user, location)
44
+ response.reply location.location_name + ' ' + ansi_temp_forecast(forecast)
45
+ end
46
+
47
+ def handle_irc_ascii_temp(response)
48
+ location = geo_lookup(response.user, response.match_data[1])
49
+ forecast = get_forecast_io_results(response.user, location)
50
+ response.reply location.location_name + ' ' + ascii_temp_forecast(forecast)
51
+ end
52
+
53
+ def handle_irc_daily_temp(response)
54
+ location = geo_lookup(response.user, response.match_data[1])
55
+ forecast = get_forecast_io_results(response.user, location)
56
+ response.reply location.location_name + ' ' + ansi_temp_forecast(forecast, 48)
57
+ end
58
+
59
+ def handle_irc_conditions(response)
60
+ location = geo_lookup(response.user, response.match_data[1])
61
+ forecast = get_forecast_io_results(response.user, location)
62
+ response.reply location.location_name + ' ' + conditions(forecast)
63
+ end
64
+
65
+ def handle_irc_ansiwind(response)
66
+ location = geo_lookup(response.user, response.match_data[1])
67
+ forecast = get_forecast_io_results(response.user, location)
68
+ response.reply location.location_name + ' ' + ansi_wind_direction_forecast(forecast)
69
+ end
70
+
71
+ def handle_irc_ascii_wind(response)
72
+ location = geo_lookup(response.user, response.match_data[1])
73
+ forecast = get_forecast_io_results(response.user, location)
74
+ response.reply location.location_name + ' ' + ascii_wind_direction_forecast(forecast)
75
+ end
76
+
77
+ def handle_irc_alerts(response)
78
+ location = geo_lookup(response.user, response.match_data[1])
79
+ forecast = get_forecast_io_results(response.user, location)
80
+ alerts = get_alerts(forecast)
81
+ response.reply alerts
82
+ end
83
+
84
+ def handle_irc_ansisun(response)
85
+ location = geo_lookup(response.user, response.match_data[1])
86
+ forecast = get_forecast_io_results(response.user, location)
87
+ response.reply location.location_name + ' ' + do_the_sun_thing(forecast)
88
+ end
89
+
90
+ def handle_irc_ansicloud(response)
91
+ location = geo_lookup(response.user, response.match_data[1])
92
+ forecast = get_forecast_io_results(response.user, location)
93
+ response.reply location.location_name + ' ' + do_the_cloud_thing(forecast)
94
+ end
95
+
96
+ def handle_irc_seven_day(response)
97
+ location = geo_lookup(response.user, response.match_data[1])
98
+ forecast = get_forecast_io_results(response.user, location)
99
+ response.reply location.location_name + ' ' + do_the_seven_day_thing(forecast)
100
+ end
101
+
102
+ def handle_irc_daily_rain(response)
103
+ location = geo_lookup(response.user, response.match_data[1])
104
+ forecast = get_forecast_io_results(response.user, location)
105
+ response.reply location.location_name + ' ' + do_the_daily_rain_thing(forecast)
106
+ end
107
+
108
+ def handle_irc_seven_day_rain(response)
109
+ location = geo_lookup(response.user, response.match_data[1])
110
+ forecast = get_forecast_io_results(response.user, location)
111
+ response.reply location.location_name + ' ' + do_the_seven_day_rain_thing(forecast)
112
+ end
113
+
114
+ def handle_irc_daily_wind(response)
115
+ location = geo_lookup(response.user, response.match_data[1])
116
+ forecast = get_forecast_io_results(response.user, location)
117
+ response.reply location.location_name + ' ' + do_the_daily_wind_thing(forecast)
118
+ end
119
+
120
+ def handle_irc_daily_humidity(response)
121
+ location = geo_lookup(response.user, response.match_data[1])
122
+ forecast = get_forecast_io_results(response.user, location)
123
+ response.reply location.location_name + ' ' + do_the_daily_humidity_thing(forecast)
124
+ end
125
+
126
+ def handle_irc_ansi_humidity(response)
127
+ location = geo_lookup(response.user, response.match_data[1])
128
+ forecast = get_forecast_io_results(response.user, location)
129
+ response.reply location.location_name + ' 48hr humidity ' + ansi_humidity_forecast(forecast)
130
+ end
131
+
132
+ def handle_irc_ansiozone(response)
133
+ location = geo_lookup(response.user, response.match_data[1])
134
+ forecast = get_forecast_io_results(response.user, location)
135
+ response.reply location.location_name + ' ' + do_the_ozone_thing(forecast)
136
+ end
137
+
138
+ def handle_irc_ansi_pressure(response)
139
+ location = geo_lookup(response.user, response.match_data[1])
140
+ forecast = get_forecast_io_results(response.user, location)
141
+ response.reply location.location_name + ' ' + do_the_pressure_thing(forecast)
142
+ end
143
+
144
+ def handle_irc_daily_pressure(response)
145
+ location = geo_lookup(response.user, response.match_data[1])
146
+ forecast = get_forecast_io_results(response.user, location)
147
+ response.reply location.location_name + ' ' + do_the_daily_pressure_thing(forecast)
148
+ end
149
+
150
+ def handle_irc_set_scale(response)
151
+ key = response.user.name + '-scale'
152
+ user_requested_scale = response.match_data[1].to_s.downcase
153
+ reply = check_and_set_scale(key, user_requested_scale)
154
+ response.reply reply
155
+ end
156
+
157
+ def handle_irc_sunrise(response)
158
+ location = geo_lookup(response.user, response.match_data[1])
159
+ forecast = get_forecast_io_results(response.user, location)
160
+ response.reply location.location_name + ' sunrise: ' + do_the_sunrise_thing(forecast)
161
+ end
162
+
163
+ def handle_irc_sunset(response)
164
+ location = geo_lookup(response.user, response.match_data[1])
165
+ forecast = get_forecast_io_results(response.user, location)
166
+ response.reply location.location_name + ' sunset: ' + do_the_sunset_thing(forecast)
167
+ end
168
+
169
+ def handle_irc_neareststorm(response)
170
+ location = geo_lookup(response.user, response.match_data[1])
171
+ forecast = get_forecast_io_results(response.user, location)
172
+ nearest_storm_distance, nearest_storm_bearing = do_the_nearest_storm_thing(forecast)
173
+
174
+ response.reply "The nearest storm is #{get_distance(nearest_storm_distance, get_scale(response.user))} to the #{get_cardinal_direction_from_bearing(nearest_storm_bearing)} of you."
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,13 @@
1
+ class Location
2
+ attr_accessor :location_name, :latitude, :longitude
3
+
4
+ def initialize (location_name, latitude, longitude)
5
+ self.location_name = location_name
6
+ self.latitude = latitude
7
+ self.longitude = longitude
8
+ end
9
+
10
+ def to_s
11
+ "Location: #{self.location_name} #{self.latitude},#{self.longitude}"
12
+ end
13
+ end
@@ -0,0 +1,165 @@
1
+ require 'geocoder'
2
+ require 'rest_client'
3
+ require 'magic_eightball'
4
+ require_relative 'location'
5
+ require_relative 'constants'
6
+ require_relative 'irc_handlers'
7
+ require_relative 'forecasts'
8
+ require_relative 'utils'
9
+
10
+ module Lita
11
+ module Handlers
12
+ class OnewheelForecastIo < Handler
13
+ config :api_key
14
+ config :api_uri
15
+ config :colors
16
+
17
+ include ::ForecastIo::Constants
18
+ include ::ForecastIo::IrcHandlers
19
+ include ::ForecastIo::Forecasts
20
+ include ::ForecastIo::Utils
21
+
22
+ # Temperature routes
23
+ route(/^ansitemp\s*$/i, :handle_irc_ansitemp)
24
+ route(/^ansitemp\s+(.+)/i, :handle_irc_ansitemp,
25
+ help: {'!ansitemp [location]' => 'The 24h temperature scale for [location].'})
26
+ route(/^dailytemp\s*$/i, :handle_irc_daily_temp)
27
+ route(/^dailytemp\s+(.+)/i, :handle_irc_daily_temp,
28
+ help: { '!dailytemp [location]' => '48h temperature scale for [location].'})
29
+ route(/^7day\s*$/i, :handle_irc_seven_day)
30
+ route(/^7day\s+(.+)/i, :handle_irc_seven_day,
31
+ help: { '!7day [location]' => '7 day temperature scale, featuring highs and lows.'})
32
+ route(/^weekly\s*$/i, :handle_irc_seven_day)
33
+ route(/^weekly\s+(.+)/i, :handle_irc_seven_day,
34
+ help: { '!weekly [location]' => 'Alias for !7day.'})
35
+ route(/^asciitemp\s*$/i, :handle_irc_ascii_temp)
36
+ route(/^asciitemp\s+(.+)/i, :handle_irc_ascii_temp,
37
+ help: { '!asciitemp [location]' => 'Like ansitemp, but with less ansi.'})
38
+
39
+ # General forecast routes
40
+ route(/^forecastallthethings\s*$/i, :handle_irc_all_the_things)
41
+ route(/^forecastallthethings\s+(.+)/i, :handle_irc_all_the_things,
42
+ help: { '!forecastallthethings [location]' => 'A huge dump of most available info for [location].'})
43
+ route(/^forecast\s*$/i, :handle_irc_forecast)
44
+ route(/^forecast\s+(.+)/i, :handle_irc_forecast,
45
+ help: { '!forecast [location]' => 'Text forcast of the location selected.'})
46
+ route(/^weather\s*$/i, :handle_irc_forecast)
47
+ route(/^weather\s+(.+)/i, :handle_irc_forecast,
48
+ help: { '!weather [location]' => 'Alias for !forecast.'})
49
+ route(/^condi*t*i*o*n*s*\s*$/i, :handle_irc_conditions)
50
+ route(/^condi*t*i*o*n*s*\s+(.+)/i, :handle_irc_conditions,
51
+ help: { '!cond[itions] [location]' => 'A single-line summary of the conditions at [location].'})
52
+
53
+ # One-offs
54
+ route(/^rain\s*$/i, :is_it_raining)
55
+ route(/^rain\s+(.+)/i, :is_it_raining,
56
+ help: { '!rain [location]' => 'Magic Eightball response to whether or not it is raining in [location] right now.'})
57
+ route(/^snow\s*$/i, :is_it_snowing)
58
+ route(/^snow\s+(.+)/i, :is_it_snowing,
59
+ help: { '!snow [location]' => 'Magic Eightball response to whether or not it is snowing in [location] right now.'})
60
+ route(/^geo\s*$/i, :handle_geo_lookup)
61
+ route(/^geo\s+(.+)/i, :handle_geo_lookup,
62
+ help: { '!geo [location]' => 'A simple geo-lookup returning GPS coords.'})
63
+ route(/^alerts\s*$/i, :handle_irc_alerts)
64
+ route(/^alerts\s+(.+)/i, :handle_irc_alerts,
65
+ help: { '!alerts [location]' => 'NOAA alerts for [location].'})
66
+ route(/^neareststorm\s*$/i, :handle_irc_neareststorm)
67
+ route(/^neareststorm\s+(.+)$/i, :handle_irc_neareststorm,
68
+ help: { '!neareststorm [location]' => 'Nearest storm distance for [location].'})
69
+
70
+ # State Commands
71
+ route(/^set scale (c|f|k)/i, :handle_irc_set_scale,
72
+ help: { '!set scale [c|f|k]' => 'Set the scale to your chosen degrees.'})
73
+ route(/^set scale$/i, :handle_irc_set_scale,
74
+ help: { '!set scale' => 'Toggle between C and F scales.'})
75
+
76
+ # Humidity
77
+ route(/^ansihumidity\s*$/i, :handle_irc_ansi_humidity)
78
+ route(/^ansihumidity\s+(.+)/i, :handle_irc_ansi_humidity,
79
+ help: { '!ansihumidity [location]' => '48h humidity report for [location].'})
80
+ route(/^dailyhumidity\s*$/i, :handle_irc_daily_humidity)
81
+ route(/^dailyhumidity\s+(.+)/i, :handle_irc_daily_humidity,
82
+ help: { '!dailyhumidity [location]' => '7 day humidity report.'})
83
+
84
+ # Rain related. Where we all started.
85
+ route(/^ansirain\s*$/i, :handle_irc_ansirain)
86
+ route(/^ansirain\s+(.+)/i, :handle_irc_ansirain,
87
+ help: { '!ansirain [location]' => '60m rain chance report for [location].'})
88
+ route(/^ansisnow\s*$/i, :handle_irc_ansirain)
89
+ route(/^ansisnow\s+(.+)/i, :handle_irc_ansirain,
90
+ help: { '!ansisnow [location]' => 'Alias for !ansirain.'})
91
+ route(/^dailyrain\s*$/i, :handle_irc_daily_rain)
92
+ route(/^dailyrain\s+(.+)/i, :handle_irc_daily_rain,
93
+ help: { '!dailyrain [location]' => '48h rain chance report for [location].'})
94
+ route(/^dailysnow\s*$/i, :handle_irc_daily_rain)
95
+ route(/^dailysnow\s+(.+)/i, :handle_irc_daily_rain,
96
+ help: { '!dailysnow [location]' => 'Alias for !dailyrain.'})
97
+ route(/^7dayrain\s*$/i, :handle_irc_seven_day_rain)
98
+ route(/^7dayrain\s+(.+)/i, :handle_irc_seven_day_rain,
99
+ help: { '!7dayrain [location]' => '7 day rain chance report for [location].'})
100
+ route(/^weeklyrain\s*$/i, :handle_irc_seven_day_rain)
101
+ route(/^weeklyrain\s+(.+)/i, :handle_irc_seven_day_rain,
102
+ help: { '!weeklyrain [location]' => 'Alias for !7dayrain.'})
103
+ route(/^weeklysnow\s*$/i, :handle_irc_seven_day_rain)
104
+ route(/^weeklysnow\s+(.+)/i, :handle_irc_seven_day_rain,
105
+ help: { '!weeklysnow [location]' => 'Alias for !7dayrain.'})
106
+ route(/^ansiintensity\s*$/i, :handle_irc_ansirain_intensity)
107
+ route(/^ansiintensity\s+(.+)/i, :handle_irc_ansirain_intensity,
108
+ help: { '!ansiintensity [location]' => '60m rain intensity report for [location].'})
109
+ route(/^asciirain\s*$/i, :handle_irc_ascii_rain)
110
+ route(/^asciirain\s+(.+)/i, :handle_irc_ascii_rain,
111
+ help: { '!asciirain [location]' => '60m rain chance report for [location], ascii style!'})
112
+
113
+ # don't start singing.
114
+ route(/^sunrise\s*$/i, :handle_irc_sunrise)
115
+ route(/^sunrise\s+(.+)/i, :handle_irc_sunrise,
116
+ help: { '!sunrise [location]' => 'Get today\'s sunrise time for [location].'})
117
+ route(/^sunset\s*$/i, :handle_irc_sunset)
118
+ route(/^sunset\s+(.+)/i, :handle_irc_sunset,
119
+ help: { '!sunset [location]' => 'Get today\'s sunset time for [location].'})
120
+ route(/^ansisun\s*$/i, :handle_irc_ansisun)
121
+ route(/^ansisun\s+(.+)/i, :handle_irc_ansisun,
122
+ help: { '!ansisun [location]' => '7 day chance-of-sun report for [location].'})
123
+
124
+ # Mun!
125
+
126
+ # Wind
127
+ route(/^ansiwind\s*$/i, :handle_irc_ansiwind)
128
+ route(/^ansiwind\s+(.+)/i, :handle_irc_ansiwind,
129
+ help: { '!ansiwind [location]' => '24h wind speed/direction report for [location].'})
130
+ route(/^asciiwind\s*$/i, :handle_irc_ascii_wind)
131
+ route(/^asciiwind\s+(.+)/i, :handle_irc_ascii_wind,
132
+ help: { '!asciiwind [location]' => '24h wind speed/direction report for [location], ascii style.'})
133
+ route(/^dailywind\s*$/i, :handle_irc_daily_wind)
134
+ route(/^dailywind\s+(.+)/i, :handle_irc_daily_wind,
135
+ help: { '!dailywind [location]' => '7 day wind speed/direction report for [location].'})
136
+
137
+ # Cloud cover
138
+ route(/^ansicloud\s*$/i, :handle_irc_ansicloud)
139
+ route(/^ansicloud\s+(.+)/i, :handle_irc_ansicloud,
140
+ help: { '!ansicloud [location]' => '24h cloud cover report for [location].'})
141
+
142
+ # oooOOOoooo
143
+ route(/^ansiozone\s*$/i, :handle_irc_ansiozone)
144
+ route(/^ansiozone\s+(.+)/i, :handle_irc_ansiozone,
145
+ help: { '!ansiozone [location]' => '24h ozone level report for [location].'})
146
+
147
+ # Pressure
148
+ route(/^ansipressure\s*$/i, :handle_irc_ansi_pressure)
149
+ route(/^ansipressure\s+(.+)/i, :handle_irc_ansi_pressure,
150
+ help: { '!ansipressure [location]' => '48h barometric pressure report for [location].'})
151
+ route(/^ansibarometer\s*$/i, :handle_irc_ansi_pressure)
152
+ route(/^ansibarometer\s+(.+)/i, :handle_irc_ansi_pressure,
153
+ help: { '!ansibarometer [location]' => 'Alias for !ansipressure.'})
154
+ route(/^dailypressure\s*$/i, :handle_irc_daily_pressure)
155
+ route(/^dailypressure\s+(.+)/i, :handle_irc_daily_pressure,
156
+ help: { '!dailypressure [location]' => '7 day barometric pressure report for [location].'})
157
+ route(/^dailybarometer\s*$/i, :handle_irc_daily_pressure)
158
+ route(/^dailybarometer\s+(.+)/i, :handle_irc_daily_pressure,
159
+ help: { '!dailybarometer [location]' => 'Alias for !dailypressure.'})
160
+
161
+ end
162
+
163
+ Lita.register_handler(OnewheelForecastIo)
164
+ end
165
+ end
@@ -0,0 +1,396 @@
1
+ module ForecastIo
2
+ module Utils
3
+ REDIS_KEY = 'forecast_io'
4
+
5
+ # Return an eightball response based on the current chance of rain.
6
+ # If it's snowing, it's a hard no.
7
+ def is_it_raining(response)
8
+ geocoded = geo_lookup response.user, response.match_data[1]
9
+ forecast = get_forecast_io_results response.user, geocoded
10
+
11
+ response.reply get_eightball_response get_chance_of('rain', forecast['currently'])
12
+ end
13
+
14
+ # Return an eightball response based on the current chance of snow.
15
+ # If it's raining, it's a hard no.
16
+ def is_it_snowing(response)
17
+ geocoded = geo_lookup response.user, response.match_data[1]
18
+ forecast = get_forecast_io_results response.user, geocoded
19
+
20
+ response.reply get_eightball_response get_chance_of('snow', forecast['currently'])
21
+ end
22
+
23
+ def get_eightball_response(chance)
24
+ case chance
25
+ when 0..0.2
26
+ MagicEightball.reply :no
27
+ when 0.201..0.7
28
+ MagicEightball.reply :maybe
29
+ when 0.701..1
30
+ MagicEightball.reply :yes
31
+ end
32
+ end
33
+
34
+ def get_chance_of(rain_or_snow, currently)
35
+ # This is a fallthrough so we'll reply no to rain if it's snowing, and vice versa.
36
+ chance = 0
37
+
38
+ if currently['precipType'] == rain_or_snow # If we match the specified string ['rain', 'snow']
39
+ chance = currently['precipProbability'] # Set the probability for 8-ball reckoning.
40
+ end
41
+
42
+ chance # Probably superfluous.
43
+ end
44
+
45
+ # Geographical stuffs
46
+ # Now with moar caching!
47
+ def optimistic_geo_wrapper(query)
48
+ Lita.logger.debug "Optimistically geo wrapping #{query}!"
49
+ geocoded = nil
50
+ result = ::Geocoder.search(query)
51
+ Lita.logger.debug "Geocoder result: '#{result.inspect}'"
52
+ if result[0]
53
+ geocoded = result[0].data
54
+ end
55
+ geocoded
56
+ end
57
+
58
+ def geo_lookup(user, query)
59
+ Lita.logger.debug "Performing geolookup for '#{user.name}' for '#{query}'"
60
+ if query.nil? or query.empty?
61
+ Lita.logger.debug "No query specified, pulling from redis #{REDIS_KEY}, #{user.name}"
62
+ serialized_geocoded = redis.hget(REDIS_KEY, user.name)
63
+ unless serialized_geocoded == 'null' or serialized_geocoded.nil?
64
+ geocoded = JSON.parse(serialized_geocoded)
65
+ end
66
+ Lita.logger.debug "Cached location: #{geocoded.inspect}"
67
+ end
68
+
69
+ Lita.logger.debug "q & g #{query.inspect} #{geocoded.inspect}"
70
+ if (query.nil? or query.empty?) and geocoded.nil?
71
+ query = 'Portland, OR'
72
+ end
73
+
74
+ unless geocoded
75
+ Lita.logger.debug "Redis hget failed, performing lookup for #{query}"
76
+ geocoded = optimistic_geo_wrapper query
77
+ Lita.logger.debug "Geolocation found. '#{geocoded.inspect}' failed, performing lookup"
78
+ redis.hset(REDIS_KEY, user.name, geocoded.to_json)
79
+ end
80
+
81
+ Lita.logger.debug "geocoded: '#{geocoded}'"
82
+
83
+ loc = Location.new(
84
+ geocoded['formatted_address'],
85
+ geocoded['geometry']['location']['lat'],
86
+ geocoded['geometry']['location']['lng']
87
+ )
88
+
89
+ Lita.logger.debug "loc: '#{loc}'"
90
+
91
+ loc
92
+ end
93
+
94
+ # Wrapped for testing.
95
+ def gimme_some_weather(url)
96
+ # HTTParty.get url
97
+ response = RestClient.get(url)
98
+ JSON.parse(response.to_str)
99
+ end
100
+
101
+ def set_scale(user)
102
+ key = user.name + '-scale'
103
+ if scale = redis.hget(REDIS_KEY, key)
104
+ @scale = scale
105
+ end
106
+ end
107
+
108
+ def get_scale(user)
109
+ key = user.name + '-scale'
110
+ scale = redis.hget(REDIS_KEY, key)
111
+ if scale.nil?
112
+ scale = 'f'
113
+ end
114
+ scale
115
+ end
116
+
117
+ def check_and_set_scale(key, user_requested_scale)
118
+ persisted_scale = redis.hget(REDIS_KEY, key)
119
+
120
+ if %w(c f k).include? user_requested_scale
121
+ scale_to_set = user_requested_scale
122
+ else
123
+ # Toggle mode
124
+ scale_to_set = get_other_scale(persisted_scale)
125
+ end
126
+
127
+ if persisted_scale == scale_to_set
128
+ reply = "Scale is already set to #{scale_to_set}!"
129
+ else
130
+ redis.hset(REDIS_KEY, key, scale_to_set)
131
+ reply = "Scale set to #{scale_to_set}"
132
+ end
133
+
134
+ reply
135
+ end
136
+
137
+
138
+ def get_forecast_io_results(user, location)
139
+ if ! config.api_uri or ! config.api_key
140
+ Lita.logger.error "Configuration missing! '#{config.api_uri}' '#{config.api_key}'"
141
+ return
142
+ end
143
+ uri = config.api_uri + config.api_key + '/' + "#{location.latitude},#{location.longitude}"
144
+ Lita.logger.debug uri
145
+ set_scale(user)
146
+ forecast = gimme_some_weather uri
147
+ end
148
+
149
+ def handle_geo_lookup(response)
150
+ location = geo_lookup(response.user, response.match_data[1])
151
+ response.reply "#{location.latitude}, #{location.longitude}"
152
+ end
153
+
154
+ def forecast_text(forecast)
155
+ forecast_str = "weather is currently #{get_temperature forecast['currently']['temperature']} " +
156
+ "and #{forecast['currently']['summary'].downcase}. Winds out of the #{get_cardinal_direction_from_bearing forecast['currently']['windBearing']} at #{get_speed(forecast['currently']['windSpeed'])}. "
157
+
158
+ if forecast['minutely']
159
+ minute_forecast = forecast['minutely']['summary'].to_s.downcase.chop
160
+ forecast_str += "It will be #{minute_forecast}, and #{forecast['hourly']['summary'].to_s.downcase.chop}. "
161
+ end
162
+
163
+ forecast_str += "There are also #{forecast['currently']['ozone'].to_s} ozones."
164
+ end
165
+
166
+ def fix_time(unixtime, data_offset)
167
+ unixtime - determine_time_offset(data_offset)
168
+ end
169
+
170
+ def determine_time_offset(data_offset)
171
+ system_offset_seconds = Time.now.utc_offset
172
+ data_offset_seconds = data_offset * 60 * 60
173
+ system_offset_seconds - data_offset_seconds
174
+ end
175
+
176
+ # Utility functions
177
+
178
+ ###
179
+ # get_colored_string
180
+ # Returns the dot_str colored based on our range_hash.
181
+ # range_hash is one of our color hashes, e.g. get_wind_range_colors
182
+ # key is used to index each element in data_limited to get our value to compare with the range_hash.
183
+ ##
184
+ def get_colored_string(data_limited, key, dot_str, range_hash)
185
+ color = nil
186
+ prev_color = nil
187
+ collect_str = ''
188
+ colored_str = ''
189
+
190
+ data_limited.each_with_index do |data, index|
191
+ range_hash.keys.each do |range_hash_key|
192
+ if range_hash_key.cover? data[key] # Super secred cover sauce
193
+ color = range_hash[range_hash_key]
194
+ if index == 0
195
+ prev_color = color
196
+ end
197
+ end
198
+ end
199
+
200
+ # If the color changed, let's update the collect_str
201
+ unless color == prev_color
202
+ colored_str += "\x03" + colors[prev_color] + collect_str
203
+ collect_str = ''
204
+ end
205
+
206
+ collect_str += dot_str[index]
207
+ prev_color = color
208
+ end
209
+
210
+ # And get the last one.
211
+ colored_str += "\x03" + colors[color] + collect_str + "\x03"
212
+ end
213
+
214
+ # this method lets us condense rain forcasts into smaller sets
215
+ # it averages the values contained in a chunk of data perportionate the the limit set
216
+ # then returns a new array of hashes containing those averaged values
217
+ def condense_data(data, limit)
218
+ return if limit >= data.length
219
+ chunk_length = (data.length / limit.to_f).round
220
+ results = []
221
+ data.each_slice(chunk_length) do |chunk|
222
+ chunk_results = {}
223
+ condensed_chunk = collect_values(chunk)
224
+ condensed_chunk.each do |k, v|
225
+ if v[0].class == Fixnum || v[0].class == Float
226
+ new_val = v.inject{ |sum,val| sum + val} / v.size
227
+ elsif v[0].class == String
228
+ new_val = v[0]
229
+ end
230
+ chunk_results[k] = new_val
231
+ end
232
+ results << chunk_results
233
+ end
234
+ results
235
+ end
236
+
237
+ # this method is simply to transform an array of hashes into a hash of arrays
238
+ # kudos to Phrogz for the info here: http://stackoverflow.com/questions/5490952/merge-array-of-hashes-to-get-hash-of-arrays-of-values
239
+ def collect_values(hashes)
240
+ {}.tap{ |r| hashes.each{ |h| h.each{ |k,v| (r[k]||=[]) << v } } }
241
+ end
242
+
243
+ def get_dot_str(chars, data, min, differential, key)
244
+ str = ''
245
+ data.each do |datum|
246
+ percentage = get_percentage(datum[key], differential, min)
247
+ str += get_dot(percentage, chars)
248
+ end
249
+ str
250
+ end
251
+
252
+ def get_percentage(number, differential, min)
253
+ if differential == 0
254
+ percentage = number
255
+ else
256
+ percentage = (number.to_f - min) / (differential)
257
+ end
258
+ percentage
259
+ end
260
+
261
+ # °℃℉
262
+ def get_dot(probability, char_array)
263
+ if probability < 0 or probability > 1
264
+ Lita.logger.error "get_dot Probably a probability problem: #{probability} should be between 0 and 1."
265
+ return '?'
266
+ end
267
+
268
+ if probability == 0
269
+ return char_array[0]
270
+ elsif probability <= 0.10
271
+ return char_array[1]
272
+ elsif probability <= 0.25
273
+ return char_array[2]
274
+ elsif probability <= 0.50
275
+ return char_array[3]
276
+ elsif probability <= 0.75
277
+ return char_array[4]
278
+ elsif probability <= 1.00
279
+ return char_array[5]
280
+ end
281
+ end
282
+
283
+ def get_temperature(temp_f)
284
+ if @scale == 'c'
285
+ celcius(temp_f).to_s + '°C'
286
+ elsif @scale == 'k'
287
+ kelvin(temp_f).to_s + 'K'
288
+ else
289
+ temp_f.to_s + '°F'
290
+ end
291
+ end
292
+
293
+ def get_speed(speed_imperial)
294
+ if @scale == 'c'
295
+ kilometers(speed_imperial).to_s + ' kph'
296
+ else
297
+ speed_imperial.to_s + ' mph'
298
+ end
299
+ end
300
+
301
+ def get_distance(distance_imperial, scale)
302
+ if scale == 'c'
303
+ kilometers(distance_imperial).to_s + ' km'
304
+ else
305
+ distance_imperial.to_s + ' mi'
306
+ end
307
+ end
308
+
309
+ def get_humidity(humidity_decimal)
310
+ (humidity_decimal * 100).round(0).to_s + '%'
311
+ end
312
+
313
+ def celcius(degrees_f)
314
+ (0.5555555556 * (degrees_f.to_f - 32)).round(2)
315
+ end
316
+
317
+ def kelvin(degrees_f)
318
+ ((degrees_f.to_f + 459.67) * 5/9).round(2)
319
+ end
320
+
321
+ def kilometers(speed_imperial)
322
+ (speed_imperial * 1.6).round(2)
323
+ end
324
+
325
+ def get_cardinal_direction_from_bearing(bearing)
326
+ case bearing
327
+ when 0..25
328
+ 'N'
329
+ when 26..65
330
+ 'NE'
331
+ when 66..115
332
+ 'E'
333
+ when 116..155
334
+ 'SE'
335
+ when 156..205
336
+ 'S'
337
+ when 206..245
338
+ 'SW'
339
+ when 246..295
340
+ 'W'
341
+ when 296..335
342
+ 'NW'
343
+ when 336..360
344
+ 'N'
345
+ end
346
+ end
347
+
348
+ # This is a little weird, because the arrows are 180° rotated. That's because the wind bearing is "out of the N" not "towards the N".
349
+ def ansi_wind_arrows
350
+ case robot.config.robot.adapter
351
+ when :slack
352
+ {'N' => ':arrow_down:',
353
+ 'NE' => ':arrow_lower_left:',
354
+ 'E' => ':arrow_left:',
355
+ 'SE' => ':arrow_upper_left:',
356
+ 'S' => ':arrow_up:',
357
+ 'SW' => ':arrow_upper_right:',
358
+ 'W' => ':arrow_right:',
359
+ 'NW' => ':arrow_lower_right:'
360
+ }
361
+ else
362
+ {'N' => '↓',
363
+ 'NE' => '↙',
364
+ 'E' => '←',
365
+ 'SE' => '↖',
366
+ 'S' => '↑',
367
+ 'SW' => '↗',
368
+ 'W' => '→',
369
+ 'NW' => '↘'
370
+ }
371
+ end
372
+ end
373
+
374
+ def ascii_wind_arrows
375
+ { 'N' => 'v',
376
+ 'NE' => ',',
377
+ 'E' => '<',
378
+ 'SE' => "\\",
379
+ 'S' => '^',
380
+ 'SW' => '/',
381
+ 'W' => '>',
382
+ 'NW' => '.'
383
+ }
384
+ end
385
+
386
+ # A bit optimistic, but I really like the Cs.
387
+ def get_other_scale(scale)
388
+ if scale.downcase == 'c'
389
+ 'f'
390
+ else
391
+ 'c'
392
+ end
393
+ end
394
+
395
+ end
396
+ end