class Maps
Constants
- DIRECTIONS_API_URL
Public Class Methods
Source
# File plugins/maps.rb, line 62 def self.build_api_url(origin, destination, waypoints:, api_key:) params = { origin: origin, destination: destination, key: api_key } params[:waypoints] = waypoints.join("|") unless waypoints.empty? query = params.map { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&") "#{DIRECTIONS_API_URL}?#{query}" end
Source
# File plugins/maps.rb, line 238 def self.directions(route_text, fetch:, api_key: ENV["GOOGLE_MAPS_API_KEY"]) result = fetch_directions(route_text, fetch: fetch, api_key: api_key) return result if result.is_a?(String) format_directions(result[:data], route: result[:route]) end
Source
# File plugins/maps.rb, line 245 def self.directions_brief(route_text, fetch:, api_key: ENV["GOOGLE_MAPS_API_KEY"]) result = fetch_directions(route_text, fetch: fetch, api_key: api_key) return result if result.is_a?(String) format_directions_brief(result[:data], route: result[:route]) end
Source
# File plugins/maps.rb, line 216 def self.fetch_directions(route_text, fetch:, api_key: ENV["GOOGLE_MAPS_API_KEY"]) parsed = parse_route(route_text) return parsed[:error] if parsed[:error] return "Google Maps API key not configured (set GOOGLE_MAPS_API_KEY)" if api_key.nil? || api_key.to_s.strip.empty? url = build_api_url(parsed[:origin], parsed[:destination], waypoints: parsed[:waypoints], api_key: api_key) json = fetch.call(url) data = JSON.parse(json) unless data["status"] == "OK" message = data["error_message"] || data["status"] || "No route found" return message end { data: data, route: parsed } rescue JSON::ParserError "No route found" rescue StandardError "Could not fetch directions" end
Source
# File plugins/maps.rb, line 205 def self.format_directions(data, route: nil) lines = route_data_lines(data, route: route, include_steps: true) return "No route found" unless lines lines.join("\n") end
Source
# File plugins/maps.rb, line 212 def self.format_directions_brief(data, route: nil) format_distance_summary(data, route: route) end
Source
# File plugins/maps.rb, line 177 def self.format_distance_summary(data, route: nil) parts = summary_parts(data, route: route) return "No route found" unless parts line = "#{parts[:path]} | #{parts[:distance]}, #{parts[:duration]}" line += " via #{parts[:road]}" unless parts[:road].empty? line end
Source
# File plugins/maps.rb, line 82 def self.format_duration(total_seconds) total_seconds = total_seconds.to_i hours = total_seconds / 3600 minutes = (total_seconds % 3600) / 60 if hours.positive? && minutes.positive? hour_label = hours == 1 ? "hour" : "hours" minute_label = minutes == 1 ? "min" : "mins" "#{hours} #{hour_label} #{minutes} #{minute_label}" elsif hours.positive? hour_label = hours == 1 ? "hour" : "hours" "#{hours} #{hour_label}" else minute_label = minutes == 1 ? "min" : "mins" "#{minutes} #{minute_label}" end end
Source
# File plugins/maps.rb, line 120 def self.format_leg(leg, leg_number) header = format_leg_header(leg, leg_number) steps = (leg["steps"] || []).each_with_index.map do |step, index| " #{format_step(step, index + 1)}" end ([header] + steps).join("\n") end
Source
# File plugins/maps.rb, line 112 def self.format_leg_header(leg, leg_number) start_name = short_address(leg["start_address"]) end_name = short_address(leg["end_address"]) distance = leg.dig("distance", "text") duration = leg.dig("duration", "text") "Leg #{leg_number}: #{start_name} → #{end_name} — #{distance}, #{duration}" end
Source
# File plugins/maps.rb, line 100 def self.format_step(step, number) instruction = strip_html(step["html_instructions"]) distance = step.dig("distance", "text") duration = step.dig("duration", "text") maneuver = step["maneuver"] parts = ["#{number}. #{instruction}"] parts << "#{distance}, #{duration}" if distance || duration line = parts.compact.join(" — ") line += " [#{maneuver}]" if maneuver && !maneuver.empty? line end
Source
# File plugins/maps.rb, line 39 def self.parse_route(text) text = text.to_s.strip return { error: usage_message } if text.empty? return { error: usage_message } unless text.match?(/\s+to\s+/i) origin_part, rest = text.split(/\s+to\s+/i, 2) origin = strip_quotes(origin_part) return { error: usage_message } if origin.empty? || rest.to_s.strip.empty? via_parts = rest.split(/\s+via\s+/i).map { |part| strip_quotes(part.strip) } destination = via_parts.first waypoints_mentioned = via_parts[1..] || [] waypoints = waypoints_mentioned.reverse return { error: usage_message } if destination.empty? { origin: origin, destination: destination, waypoints: waypoints } end
Source
# File plugins/maps.rb, line 186 def self.route_data_lines(data, route: nil, include_steps: true) metrics = route_metrics(data) return nil unless metrics lines = [] summary = format_distance_summary(data, route: route) lines << summary metrics[:legs].each_with_index do |leg, index| if include_steps lines << format_leg(leg, index + 1) else lines << format_leg_header(leg, index + 1) end end lines end
Source
# File plugins/maps.rb, line 134 def self.route_metrics(data) data = data.is_a?(String) ? JSON.parse(data) : data return nil unless data["status"] == "OK" return nil if data["routes"].nil? || data["routes"].empty? route_data = data["routes"].first legs = route_data["legs"] || [] return nil if legs.empty? total_distance = legs.sum { |leg| leg.dig("distance", "value").to_i } total_duration = legs.sum { |leg| leg.dig("duration", "value").to_i } distance_text = if total_distance >= 1000 km = total_distance / 1000.0 km == km.to_i ? "#{km.to_i} km" : "#{km.round(1)} km" else "#{total_distance} m" end { legs: legs, distance_text: distance_text, duration_text: format_duration(total_duration), road: route_data["summary"].to_s.strip } rescue JSON::ParserError nil end
Source
# File plugins/maps.rb, line 128 def self.route_summary(route) return "" unless route [route[:origin], *route[:waypoints], route[:destination]].join(" → ") end
Source
# File plugins/maps.rb, line 78 def self.short_address(address) address.to_s.split(",").first.to_s.strip end
Source
# File plugins/maps.rb, line 74 def self.strip_html(html) Nokogiri::HTML.fragment(html.to_s).text.gsub(/\s+/, " ").strip end
Source
# File plugins/maps.rb, line 30 def self.strip_quotes(text) value = text.to_s.strip if (match = value.match(/\A["'](.+)["']\z/)) match[1].strip else value end end
Source
# File plugins/maps.rb, line 162 def self.summary_parts(data, route: nil) metrics = route_metrics(data) return nil unless metrics path = route_summary(route) path = "Route" if path.empty? { path: path, distance: metrics[:distance_text], duration: metrics[:duration_text], road: metrics[:road] } end
Source
# File plugins/maps.rb, line 26 def self.usage_message 'Usage: !maps <origin> to <destination> [via <waypoint> ...] — e.g. !maps elgin vale to rosslyn bay via kilkivan — !maps set fuel 8.9 saves your economy' end
Public Instance Methods
Source
# File plugins/distance.rb, line 491 def directions(route_text, fetch: method(:fetch_json), api_key: ENV["GOOGLE_MAPS_API_KEY"], fuel_fetch: method(:fetch_fuel), consumption: nil, network: nil, nick: nil, store: RabbotDb) consumption ||= resolved_maps_consumption(network: network, nick: nick, store: store) JourneyFuel.maps_report( route_text, fetch: fetch, api_key: api_key, fuel_fetch: fuel_fetch, consumption: consumption ) end
Source
# File plugins/distance.rb, line 504 def execute(m, route_text) network = bot.config.server nick = m.user.nick text = directions(route_text, network: network, nick: nick) if text.to_s.include?("\n") themed_flood_safe_reply(m, text) else flood_safe_reply(m, text, with_progress: false) end end
Source
# File plugins/distance.rb, line 487 def fetch_fuel(url) RabbotHttp.fetch(url) end
Source
# File plugins/maps.rb, line 256 def fetch_json(url) URI.parse(url).open("User-Agent" => "Mozilla/5.0").read end
Source
# File plugins/distance.rb, line 515 def resolved_maps_consumption(network:, nick:, store:) return JourneyFuel::DEFAULT_CONSUMPTION_L_PER_100KM unless network && nick && !nick.to_s.strip.empty? DistanceFuelStore.resolve( network: network, nick: nick, store: store, default: JourneyFuel::DEFAULT_CONSUMPTION_L_PER_100KM ) end