current.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // This file is subject to a 1-clause BSD license.
  2. // Its contents can be found in the enclosed LICENSE file.
  3. package weather
  4. import (
  5. "fmt"
  6. "strings"
  7. "time"
  8. "github.com/monkeybird/autimaat/app/util"
  9. "github.com/monkeybird/autimaat/irc"
  10. "github.com/monkeybird/autimaat/irc/cmd"
  11. "github.com/monkeybird/autimaat/irc/proto"
  12. )
  13. const CurrentWeatherURL = "https://api.wunderground.com/api/%s/conditions/lang:%s/q/%s.json"
  14. // cmdCurrentWeather yields current weather data for a given location.
  15. func (p *plugin) cmdCurrentWeather(w irc.ResponseWriter, r *irc.Request, params cmd.ParamList) {
  16. p.m.Lock()
  17. defer p.m.Unlock()
  18. if len(p.config.WundergroundApiKey) == 0 {
  19. proto.PrivMsg(w, r.Target, TextNoWeather)
  20. return
  21. }
  22. loc := newLocation(r)
  23. key := strings.ToLower(loc.String())
  24. if resp, ok := p.currentWeatherCache[key]; ok {
  25. // If the cached result is younger than the timeout, print its
  26. // contents for the user and exit. Otherwise, consider it stale,
  27. // delete it and re-fetch.
  28. if time.Since(resp.Timestamp) <= CacheTimeout {
  29. sendCurrentWeather(w, r, resp)
  30. return
  31. }
  32. delete(p.currentWeatherCache, key)
  33. }
  34. // Fetch new response.
  35. var resp currentWeatherResponse
  36. resp.Timestamp = time.Now()
  37. if !p.fetch(CurrentWeatherURL, key, &resp) {
  38. return
  39. }
  40. // It is possible we received location suggestions, instead of weather
  41. // data. Present these suggestions to the user and exit. Do not cache
  42. // the response.
  43. if len(resp.Response.Results) > 0 {
  44. sendLocations(w, r, resp.Response.Results)
  45. return
  46. }
  47. sendCurrentWeather(w, r, &resp)
  48. p.currentWeatherCache[key] = &resp
  49. }
  50. // sendCurrentWeather formats a response for the user who invoked the
  51. // weather request and sends it back to them.
  52. func sendCurrentWeather(w irc.ResponseWriter, r *irc.Request, cwr *currentWeatherResponse) {
  53. co := &cwr.CurrentObservation
  54. if strings.TrimSpace(co.DisplayLocation.City) == "" {
  55. proto.PrivMsg(w, r.Target, TextNoResult, r.SenderName)
  56. return
  57. }
  58. location := util.Bold(co.DisplayLocation.City)
  59. if len(co.DisplayLocation.Country) > 0 {
  60. if len(co.DisplayLocation.State) > 0 {
  61. location += fmt.Sprintf(" (%s, %s)",
  62. co.DisplayLocation.State, co.DisplayLocation.Country)
  63. } else {
  64. location += fmt.Sprintf(" (%s)", co.DisplayLocation.Country)
  65. }
  66. }
  67. proto.PrivMsg(w, r.Target, TextCurrentWeatherDisplay,
  68. r.SenderName,
  69. location,
  70. int(co.TempC),
  71. co.Weather,
  72. co.PressureMB,
  73. co.RelativeHumidity,
  74. co.WindKPH,
  75. co.WindDir,
  76. )
  77. }
  78. // currentWeatherResponse defines an API response.
  79. type currentWeatherResponse struct {
  80. Timestamp time.Time
  81. // This is filled if an ambiguous location name is provided to
  82. // the API. It will contain location suggestions for specific
  83. // places.
  84. Response struct {
  85. Results []location `json:"results"`
  86. Error struct {
  87. Description string `json:"description"`
  88. } `json:"error"`
  89. } `json:"response"`
  90. // This is filled with actual weather data for a specific location.
  91. // It is only filled if the Response.Results field is empty.
  92. CurrentObservation struct {
  93. DisplayLocation location `json:"display_location"`
  94. Weather string `json:"weather"`
  95. TempC float32 `json:"temp_c"`
  96. RelativeHumidity string `json:"relative_humidity"`
  97. WindDir string `json:"wind_dir"`
  98. WindKPH float32 `json:"wind_kph"`
  99. PressureMB string `json:"pressure_mb"`
  100. FeelslikeC string `json:"feelslike_c"`
  101. } `json:"current_observation"`
  102. }