Skip to content
This repository
file 126 lines (109 sloc) 4.766 kb
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
# Description:
# None
#
# Dependencies:
# None
#
# Configuration:
# HUBOT_WUNDERGROUND_API_KEY Sign up at http://www.wunderground.com/weather/api/.
# HUBOT_WUNDERGROUND_USE_METRIC Set to arbitrary value to use forecasts with metric system units
#
# Commands:
# hubot weather me <location> - short-term forecast
# hubot radar me <location> - recent radar image
# hubot satellite me <location> - get a recent satellite image
# hubot weathercam me <location> - get a weather webcam image near location
#
# Notes:
# location can be zip code, ICAO/IATA airport code, state/city (CA/San_Franciso).
#
# Author:
# alexdean

module.exports = (robot) ->
  robot.respond /weather (me|at|for|in)? ?(.*)$/i, (msg) ->
    location = msg.match[2]
    get_data robot, msg, location, 'forecast', location.replace(/\s/g, '_'), send_forecast, 60*60*2

  robot.respond /radar (me|at|for|in)? ?(.*)$/i, (msg) ->
    location = msg.match[2]
    get_data robot, msg, location, 'radar', location.replace(/\s/g, '_'), send_radar, 60*10

  robot.respond /satellite (me|at|for|in)? ?(.*)$/i, (msg) ->
    location = msg.match[2]
    get_data robot, msg, location, 'satellite', location.replace(/\s/g, '_'), send_satellite, 60*10

  robot.respond /weathercam (me|at|for|in)? ?(.*)$/i, (msg) ->
    location = msg.match[2]
    get_data robot, msg, location, 'webcams', location.replace(/\s/g, '_'), send_webcam, 60*30

# check cache, get data, store data, invoke callback.
get_data = (robot, msg, location, service, query, cb, lifetime, stack=0) ->
  # what redis key to use
  cache_key = key_for service, location
  robot.brain.data.wunderground or= {}

  data = robot.brain.data.wunderground[cache_key]
  if data? and ttl(data) <= 0
    #console.log 'needs refresh'
    robot.brain.data.wunderground[cache_key] = data = null
  if data?
    #console.log 'cache is valid'
    cb msg, location, data
  else
    if not process.env.HUBOT_WUNDERGROUND_API_KEY?
      msg.send "HUBOT_WUNDERGROUND_API_KEY is not set. Sign up at http://www.wunderground.com/weather/api/."
      return
    # get new data
    msg
      .http("http://api.wunderground.com/api/#{process.env.HUBOT_WUNDERGROUND_API_KEY}/#{service}/q/#{encodeURIComponent query}.json")
      .get() (err, res, body) ->
        # check for a non-200 response. cache it for some short amount of time && msg.send 'unavailable'
        data = JSON.parse(body)

        # probably an unknown place
        if data.response?.error?
          msg.send data.response.error.description

        # ambiguous place, multiple matches
        else if data.response?.results?
          alts = for key,item of data.response.results
            alternative_place item
          # we don't seem to have array.filter
          alts = for key,item of alts when item isnt ''
            item
          # if there's only 1 place, let's just get it.
          # stack: guard against infinite recursion
          if alts.length == 1 && stack == 0
            get_data robot, msg, location, service, alts[0], cb, lifetime, 1
          else
            msg.send "Possible matches for '#{location}'.\n - #{alts.join('\n - ')}"

        # looks good
        else
          robot.brain.data.wunderground[cache_key] = data
          robot.brain.data.wunderground[cache_key].retrieved = new Date
          robot.brain.data.wunderground[cache_key].lifetime = lifetime
          cb msg, location, robot.brain.data.wunderground[cache_key]

send_forecast = (msg, location, data) ->
  report = data.forecast.txt_forecast.forecastday[0]
  useMetric = process.env.HUBOT_WUNDERGROUND_USE_METRIC?
  msg.send "#{report.title} in #{location}: #{if useMetric then report.fcttext_metric else report.fcttext} (#{formatted_ttl data})"
send_radar = (msg, location, data) ->
  msg.send "#{data.radar.image_url}#.png"

send_satellite = (msg, location, data) ->
  msg.send "#{data.satellite.image_url}#.png"

send_webcam = (msg, location, data) ->
  cam = msg.random data.webcams
  if cam?
    msg.send "#{cam.handle} in #{cam.city}, #{cam.state} (#{formatted_ttl data})"
    msg.send "#{cam.CURRENTIMAGEURL}#.png"
  else
    msg.send "No webcams near #{location}. (#{formatted_ttl data})"

# quick normalization to reduce caching of redundant data
key_for = (service, query) ->
  "#{service}-#{query.toLowerCase()}"

formatted_ttl = (data) ->
  parseInt(ttl(data)/1000)

# how long till our cached data expires?
ttl = (data) ->
  now = new Date
  if not data.lifetime? or not data.retrieved?
    -1
  else
    retrieved = Date.parse(data.retrieved)
    data.lifetime * 1000 - (now.getTime() - retrieved)

alternative_place = (item) ->
  return '' if item.country != 'US' || item.state == "" || item.city == ""
  return "#{item.state}/#{item.city.replace(/\s/g, '_')}"
Something went wrong with that request. Please try again.