diff --git a/plugins/Weather.py b/plugins/Weather.py
index 158a60843..3a289c454 100644
--- a/plugins/Weather.py
+++ b/plugins/Weather.py
@@ -33,18 +33,53 @@
This plugin does weather-related stuff.
"""
+import urllib
import plugins
import re
import sets
+import conf
import utils
import webutils
import privmsgs
+import registry
import callbacks
+unitAbbrevs = utils.abbrev(['fahrenheit', 'celsius', 'centigrade', 'kelvin'])
+unitAbbrevs['c'] = 'celsius'
+unitAbbrevs['ce'] = 'celsius'
+
+class WeatherUnit(registry.String):
+ def set(self, s):
+ original = getattr(self, 'value', self.default)
+ registry.String.set(self, s)
+ s = self.value.lower()
+ if s not in unitAbbrevs:
+ setattr(self, 'value', original)
+ raise registry.InvalidRegistryValue,\
+ 'Unit must be one of fahrenheit, celsius, or kelvin.'
+
+class WeatherCommand(registry.String):
+ def set(self, s):
+ original = getattr(self, 'value', self.default)
+ registry.String.set(self, s)
+ m = Weather.commands
+ if self.value not in m:
+ setattr(self, 'value', original)
+ raise registry.InvalidRegistryValue,\
+ 'Command must be one of %s' % utils.commaAndify(m)
+
+conf.registerPlugin('Weather')
+conf.registerChannelValue(conf.supybot.plugins.Weather, 'preferredUnit',
+ WeatherUnit('fahrenheit', """Sets the default temperature unit to use when
+ reporting the weather."""))
+conf.registerChannelValue(conf.supybot.plugins.Weather, 'weatherCommand',
+ WeatherCommand('cnn', """Sets the default command to use when retrieving
+ the weather."""))
class Weather(callbacks.Privmsg):
+ commands = ['ham', 'cnn']
threaded = True
def callCommand(self, method, irc, msg, *L):
try:
@@ -52,6 +87,34 @@ class Weather(callbacks.Privmsg):
except webutils.WebError, e:
irc.error(str(e))
+ def _getTemp(self, temp, deg, unit, chan):
+ default = self.registryValue('preferredUnit', chan).lower()
+ default = unitAbbrevs[default]
+ unit = unit.lower()
+ if unitAbbrevs[unit] == default:
+ return deg.join([str(temp), unit.upper()])
+ try:
+ temp = int(temp)
+ except ValueError:
+ return deg.join([temp, unit])
+ if unit == 'f':
+ temp = (temp - 32) * 5 / 9
+ if default == 'kelvin':
+ temp = temp + 273.15
+ unit = 'K'
+ deg = ' '
+ else:
+ unit = 'C'
+ elif unit == 'c':
+ if default == 'kelvin':
+ temp = temp + 273.15
+ unit = 'K'
+ deg = ' '
+ elif default == 'fahrenheit':
+ temp = temp * 9 / 5 + 32
+ unit = 'F'
+ return deg.join([str(temp), unit])
+
_cityregex = re.compile(
r'
'
r'(.*?), (.*?),(.*?) | ', re.I)
@@ -86,8 +149,8 @@ class Weather(callbacks.Privmsg):
'nu', 'on', 'pe', 'qc', 'sk', 'yk'])
# Certain countries are expected to use a standard abbreviation
# The weather we pull uses weird codes. Map obvious ones here.
- _countryMap = {'uk': 'gb'}
- def weather(self, irc, msg, args):
+ _hamCountryMap = {'uk': 'gb'}
+ def ham(self, irc, msg, args):
"""
Returns the approximate weather conditions for a given city.
@@ -115,8 +178,8 @@ class Weather(callbacks.Privmsg):
else:
country = state
state = ''
- if country in self._countryMap.keys():
- country = self._countryMap[country]
+ if country in self._hamCountryMap.keys():
+ country = self._hamCountryMap[country]
url = 'http://www.hamweather.net/cgi-bin/hw3/hw3.cgi?' \
'pass=&dpp=&forecast=zandh&config=&' \
'place=%s&state=%s&country=%s' % (city, state, country)
@@ -134,9 +197,9 @@ class Weather(callbacks.Privmsg):
else:
zip = privmsgs.getArgs(args)
zip = zip.replace(',', '')
- zip = zip.lower().split()
+ zip = zip.lower()
url = 'http://www.hamweather.net/cgi-bin/hw3/hw3.cgi?' \
- 'config=&forecast=zandh&pands=%s&Submit=GO' % args[0]
+ 'config=&forecast=zandh&pands=%s&Submit=GO' % zip
html = webutils.getUrl(url)
if 'was not found' in html:
irc.error('No such location could be found.')
@@ -158,6 +221,8 @@ class Weather(callbacks.Privmsg):
temp = self._tempregex.search(html)
if temp is not None:
temp = temp.group(1)
+ (temp, deg, unit) = (temp[:-2], temp[-2], temp[-1])
+ temp = self._getTemp(temp, deg, unit, msg.args[0])
conds = self._condregex.search(html)
if conds is not None:
conds = conds.group(1)
@@ -181,6 +246,67 @@ class Weather(callbacks.Privmsg):
else:
irc.error('The format of the page was odd.')
+ _cnnUrl = 'http://weather.cnn.com/weather/search?wsearch='
+ _fTemp = re.compile(r'(-?\d+°F)', re.I | re.S)
+ _conds = re.compile(r'align="center">([^<]+)', re.I|re.S)
+ _humidity = re.compile(r'Rel. Humidity: (\d+%)', re.I | re.S)
+ _wind = re.compile(r'Wind: ([^<]+)', re.I | re.S)
+ _location = re.compile(r'([^<]+)', re.I | re.S)
+ # Certain countries are expected to use a standard abbreviation
+ # The weather we pull uses weird codes. Map obvious ones here.
+ _cnnCountryMap = {'uk': 'en', 'de': 'ge'}
+ def cnn(self, irc, msg, args):
+ """
+
+ Returns the approximate weather conditions for a given city.
+ """
+ if len(args) > 1:
+ #If we received more than 1 argument, then we got a city with a
+ #multi-word name. ie ['Garden', 'City', 'KS'] instead of
+ #['Liberal', 'KS']. We join it together with a + to pass
+ #to our query
+ state = args.pop().lower()
+ city = ' '.join(args)
+ city = city.rstrip(',').lower()
+ if state in self._cnnCountryMap:
+ state = self._cnnCountryMap[state]
+ loc = ' '.join([city, state])
+ else:
+ #We received a single argument. Zipcode or station id.
+ zip = privmsgs.getArgs(args)
+ loc = zip.replace(',', '').lower()
+ url = '%s%s' % (self._cnnUrl, urllib.quote(loc))
+ try:
+ text = webutils.getUrl(url)
+ except webutils.WebError, e:
+ irc.error(str(e))
+ return
+ if "No search results" in text or "does not match a zip code" in text:
+ irc.error('No such location could be found.')
+ return
+ location = self._location.search(text)
+ temp = self._fTemp.search(text)
+ conds = self._conds.search(text)
+ humidity = self._humidity.search(text)
+ wind = self._wind.search(text)
+ if location and temp:
+ location = location.group(1)
+ location = location.split('-')[-1].strip()
+ temp = temp.group(1)
+ (temp, deg, unit) = (temp[:-2], temp[-2], temp[-1])
+ temp = self._getTemp(temp, deg, unit, msg.args[0])
+ resp = 'The current temperature in %s is %s.' % (location, temp)
+ resp = [resp]
+ if conds is not None:
+ resp.append('Conditions: %s.' % conds.group(1))
+ if humidity is not None:
+ resp.append('Humidity: %s.' % humidity.group(1))
+ if wind is not None:
+ resp.append('Wind: %s.' % wind.group(1))
+ resp = map(utils.htmlToText, resp)
+ irc.reply(' '.join(resp))
+ else:
+ irc.error('Could not find weather information.')
Class = Weather
diff --git a/test/test_Weather.py b/test/test_Weather.py
index 419c3b1df..88cc774bc 100644
--- a/test/test_Weather.py
+++ b/test/test_Weather.py
@@ -34,19 +34,29 @@ from testsupport import *
if network:
class WeatherTest(PluginTestCase, PluginDocumentation):
plugins = ('Weather',)
- def testWeather(self):
- self.assertNotError('weather Columbus, OH')
- self.assertNotError('weather 43221')
- self.assertNotRegexp('weather Paris, FR', 'Virginia')
- self.assertError('weather alsdkfjasdl, asdlfkjsadlfkj')
- self.assertNotError('weather London, uk')
- self.assertNotError('weather London, UK')
- self.assertNotError('weather Munich, de')
- self.assertNotError('weather Tucson, AZ')
- self.assertError('weather hell')
+ def testHam(self):
+ self.assertNotError('ham Columbus, OH')
+ self.assertNotError('ham 43221')
+ self.assertNotRegexp('ham Paris, FR', 'Virginia')
+ self.assertError('ham alsdkfjasdl, asdlfkjsadlfkj')
+ self.assertNotError('ham London, uk')
+ self.assertNotError('ham London, UK')
+ self.assertNotError('ham Munich, de')
+ self.assertNotError('ham Tucson, AZ')
+ self.assertError('ham hell')
+
+ def testCnn(self):
+ self.assertNotError('cnn Columbus, OH')
+ self.assertNotError('cnn 43221')
+ self.assertNotRegexp('cnn Paris, FR', 'Virginia')
+ self.assertError('cnn alsdkfjasdl, asdlfkjsadlfkj')
+ self.assertNotError('cnn London, uk')
+ self.assertNotError('cnn London, UK')
+ self.assertNotError('cnn Munich, de')
+ self.assertNotError('cnn Tucson, AZ')
def testNoEscapingWebError(self):
- self.assertNotRegexp('weather "buenos aires"', 'WebError')
+ self.assertNotRegexp('ham "buenos aires"', 'WebError')
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: