weather2.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import requests
  2. import re
  3. import json
  4. from event import Event
  5. try:
  6. from basemodule import BaseModule
  7. except ImportError:
  8. from modules.basemodule import BaseModule
  9. class Weather2(BaseModule):
  10. def post_init(self):
  11. weather2 = Event('__.weather2__')
  12. weather2.define(msg_definition='^\.[Ww]eather')
  13. weather2.subscribe(self)
  14. forecast = Event('__.forecast__')
  15. forecast.define(msg_definition='^\.[Ff]orecast')
  16. forecast.subscribe(self)
  17. self.bot.register_event(forecast, self)
  18. self.bot.register_event(weather2, self)
  19. self.api_key = "1fe31b3b4cfdab66"
  20. def forecast(self, url, channel):
  21. q = requests.get(url)
  22. try:
  23. q.raise_for_status()
  24. except requests.exceptions.HTTPError:
  25. self.say(channel, "Encountered an error with the WUnderground API")
  26. return None
  27. results = q.json()
  28. # we're only doing two days for now
  29. counter = 0
  30. phrase = ""
  31. for item in results['forecast']['txt_forecast']['forecastday']:
  32. if counter > 3:
  33. break
  34. phrase = phrase + item['title'] + ": " + item['fcttext'] + " "
  35. counter += 1
  36. print phrase
  37. return phrase[:-1] # super hackish way to remove the trailing comma
  38. def api_request(self, location, channel, command="conditions"):
  39. """location is a search string after the .weather command. This function
  40. will determine whether it is a zip code or a named location and return an
  41. appropriate API call"""
  42. #note for future: if wanted, add further detection of conditions or forecast searching
  43. query = None
  44. try:
  45. # test if is a zipcode or a single city name
  46. a = float(location.split()[0])
  47. if a and len(location.split()[0]) < 5:
  48. self.say(channel,"valid zipcode required, numbnuts")
  49. return None
  50. zipcode = re.match('[0-9]{5}', location)
  51. query = '/q/'+zipcode.string
  52. except ValueError: # it's a city name (or broken encoding on numbers or something)
  53. #create autocomplete search
  54. q = requests.get('http://autocomplete.wunderground.com/aq', params={'query':location})
  55. try:
  56. q.raise_for_status()
  57. except requests.exceptions.HTTPError:
  58. self.say(channel, "Encountered an error contacting the WUnderground API")
  59. return None
  60. results = q.json()
  61. try:
  62. #attempt to grab the 'l' field from the first result
  63. #assuming it exists, this field will be what we use to search the conditions api
  64. query = results['RESULTS'][0]['l']
  65. except (IndexError, KeyError):
  66. #in case there were no results, let channel know
  67. self.say(channel, "No results found")
  68. return None
  69. if query:
  70. #return the full URL of the query we want to make
  71. return 'http://api.wunderground.com/api/'+self.api_key+'/' + command + query+'.json'
  72. return None
  73. def get_conditions(self, query, channel):
  74. """given a fully formed query to the wundeground API, format an output string"""
  75. r = requests.get(query)
  76. try:
  77. r.raise_for_status()
  78. except requests.exceptions.HTTPError:
  79. self.say(channel, "Encountered an error contacting the WUnderground API")
  80. return
  81. weather = r.json()
  82. try:
  83. #grab the relevant data we want for formatting
  84. location = weather['current_observation']['display_location']['full']
  85. conditions = weather['current_observation']['weather']
  86. temp_f = str(weather['current_observation']['temp_f'])
  87. temp_c = str(weather['current_observation']['temp_c'])
  88. humidity = weather['current_observation']['relative_humidity']
  89. except KeyError:
  90. self.say(channel, "Unable to get weather data from results. Sorry.")
  91. return
  92. #return the formatted string of weather data
  93. return location + ': ' + conditions + ', ' + temp_f + 'F (' + temp_c + 'C). Humidity: ' + humidity
  94. def handle(self, event):
  95. #split the line beginning with .weather into 2 parts, the command and the search string
  96. weather_line = event.msg.split(None, 1)
  97. if len(weather_line) > 1:
  98. if event.msg.startswith(".forecast"):
  99. self.say(event.channel, "forecast " + weather_line[1] + ": " + self.forecast(self.api_request(weather_line[1], event.channel, "forecast"), event.channel))
  100. return
  101. #if we're sure there's actually a search string, then continue
  102. query = self.api_request(weather_line[1], event.channel)
  103. if not query:
  104. return
  105. weather = self.get_conditions(query, event.channel)
  106. if not weather:
  107. return
  108. self.say(event.channel, weather)
  109. else:
  110. #chastise the user for being silly and not actually searching for a location
  111. self.say(event.channel, "It would help if you supplied an actual location to search for.")