internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: sopel-irc/sopel

Issue:
Willie treats U+2005 in channel names as a space
The ` ` character, supported in channel names in a few IRCds, doesn't work properly with Willie. This is not a unicode issue, but rather U+2005 is caught by `\s` as a space (suggested by @embolalia) which isn't handled properly as you can see here:

```
<<1388540173.96 :Bui!~bui@Rizon-4D594E89.eu-west-1.compute.amazonaws.com PRIVMSG #hi 123 :Ampharos!
>>1388540173.96 PRIVMSG #hi :Bui!
<<1388540174.02 :irc.sxci.net 401 Ampharos #hi :No such nick/channel
```

It cuts off at the first occurrence of the character. The obvious solution is to straight-out look for the space character instead of using `\s`. Does anyone know where this stuff is parsed?



Discussion:
Maybe we should have a special if for that character?
Checking for space explicitly would effect other whitespace, but I'm not
really sure its relevant

Chances are that `/s` matches other space-like characters too; it isn't just U+2005. Matching space would be the better option, as it's the only character used to separate the channel name.

While IRC is a 8-bit protocol and the white-space character caught by \s might be 0x07 BEL, which is not allowed in channel names, policing that stuff is probably best left to the servers. This fix shouldn't cause any issues so go for it. I think the line you want is probably: https://github.com/embolalia/willie/blob/master/willie/coretasks.py#L112

That line doesn't seem to be it. `\s` [doesn't appear](http://puu.sh/6eQoB.txt) anywhere relevant in the repository, either.


Resolving patch:
diff --git a/willie/irc.py b/willie/irc.py
--- a/willie/irc.py
+++ b/willie/irc.py
@@ -440,10 +440,10 @@ def found_terminator(self):
 
         if ' :' in line:
             argstr, text = line.split(' :', 1)
-            args = argstr.split()
+            args = argstr.split(' ')
             args.append(text)
         else:
-            args = line.split()
+            args = line.split(' ')
             text = args[-1]
 
         self.last_ping_time = datetime.now()


Test patch:
Repository: sopel-irc/sopel

Issue:
Keys in bot.privileges["#somechan"] dict throw KeyError
It's best if I demonstrate:

This is the expected behaviour:

```
>>> data = {"": 0, "Flyte": 17, "Lurk": 0}
>>> data["Flyte"]
17
>>> "Flyte" in data
True
>>> "Flyte" in data.keys()
True
```

But this is the actual behaviour:

```
>>> bot.privileges
{u'#test': {u'': 0, u'swordeh': 4, u'Lurk': 0, u'StoreSt': 0, u'Flyte': 17}}
>>> bot.privileges["#test"]
{u'': 0, u'swordeh': 4, u'Lurk': 0, u'StoreSt': 0, u'Flyte': 17}
>>> bot.privileges["#test"]["Flyte"]
KeyError: 'Flyte'
>>> bot.privileges["#test"][u"Flyte"]
KeyError: u'Flyte'
>>> "Flyte" in bot.privileges["#test"].keys()
True
>>> "Flyte" in bot.privileges["#test"]
False
```

The keys in this dict are invisibly different from the strings that they're represented as. This is proven by:

```
>>> bot.privileges["#test"][bot.privileges["#test"].keys()[4]]
17
```



Discussion:
Perhaps we should modify the `__eq__` function of Nick objects to be able to be compared to their string definition?

The current `__eq__` implementation follows:

```
def __eq__(self, other):
    if isinstance(other, Nick):
        return self._lowered == other._lowered
    return self._lowered == Nick._lower(other)
```

As far as I can tell, that should work. I've made a slight simplification of the Nick class to test with:

```
class Nick(unicode):
    def __new__(cls, nick):
        s = unicode.__new__(cls, nick)
        s._lowered = Nick._lower(nick)
        return s

    @staticmethod
    def _lower(nick):
        return nick.lower()

    def __eq__(self, other):
        if isinstance(other, Nick):
            return self._lowered == other._lowered
        return self._lowered == Nick._lower(other)

if __name__ == "__main__":
    nick = Nick("Flyte")
    assert "Flyte" == nick
```

Which works!

Also:

```
In [1]: from willie.tools import Nick

In [2]: n = Nick("Flyte")

In [3]: "Flyte" == n
Out[3]: True
```

However:

```
In [4]: data = {n: 1}

In [5]: "Flyte" in data
Out[5]: False
```

I guess this is because of:

```
In [6]: "Flyte".__hash__()
Out[6]: 5680656843101458519

In [7]: n.__hash__()
Out[7]: 6052978072309387639
```

The reason is because the string is lowered before hashing:

```
In [1]: from willie.tools import Nick

In [2]: n = Nick("Flyte")

In [3]: data = {n: "blah"}

In [4]: "flyte" in data
Out[4]: True

In [5]: n.__hash__()
Out[5]: 6052978072309387639

In [6]: "flyte".__hash__()
Out[6]: 6052978072309387639
```

This behaviour was introduced in order to fix #203.


Resolving patch:
diff --git a/willie/tools.py b/willie/tools.py
--- a/willie/tools.py
+++ b/willie/tools.py
@@ -249,6 +249,12 @@ def _lower(nick):
         low = low.replace('|', '\\').replace('^', '~')
         return low
 
+    def __repr__(self):
+        return "%s(%r)" % (
+            self.__class__.__name__,
+            self.__str__()
+        )
+
     def __hash__(self):
         return self._lowered.__hash__()
 


Test patch:
Repository: sopel-irc/sopel

Issue:
Tell module errors when you don't give it enough arguments
```
<Flyte> .tell
<Lurk> AttributeError: 'NoneType' object has no attribute 'split' (file "/home/flyte/workspace/willie/ve/local/lib/python2.7/site-packages/willie/modules/tell.py", line 93, in f_remind)
<Flyte> .tell Flyte
<Lurk> ValueError: need more than 1 value to unpack (file "/home/flyte/workspace/willie/ve/local/lib/python2.7/site-packages/willie/modules/tell.py", line 93, in f_remind)
```



Discussion:

Resolving patch:
diff --git a/willie/modules/tell.py b/willie/modules/tell.py
--- a/willie/modules/tell.py
+++ b/willie/modules/tell.py
@@ -88,11 +88,20 @@ def get_user_time(bot, nick):
 def f_remind(bot, trigger):
     """Give someone a message the next time they're seen"""
     teller = trigger.nick
-
     verb = trigger.group(1)
-    tellee, msg = trigger.group(2).split(None, 1)
 
-    tellee = Nick(tellee.rstrip('.,:;'))
+    if not trigger.group(3):
+        bot.reply("%s whom?" % verb)
+        return
+
+    tellee = trigger.group(3).rstrip('.,:;')
+    msg = trigger.group(2).lstrip(tellee).lstrip()
+
+    if not msg:
+        bot.reply("%s %s what?" % (verb, tellee))
+        return
+
+    tellee = Nick(tellee)
 
     if not os.path.exists(bot.tell_filename):
         return


Test patch:
Repository: sopel-irc/sopel

Issue:
[chanlogs] Missing copyright header
@singingwolfboy could you add the copyright header to comply with the [EFL](https://github.com/embolalia/willie/blob/master/COPYING)? Below is the required lay-out:

```
# coding=utf-8
"""
chanlogs.py - Willie Channel Logger Module
Copyright 2014, FIRSTNAME LASTNAME <EMAIL>

Licensed under the Eiffel Forum License 2.

http://willie.dftba.net
"""
```



Discussion:

Resolving patch:
diff --git a/willie/modules/chanlogs.py b/willie/modules/chanlogs.py
--- a/willie/modules/chanlogs.py
+++ b/willie/modules/chanlogs.py
@@ -1,6 +1,11 @@
-#coding: utf8
+# coding=utf-8
 """
-Channel logger
+chanlogs.py - Willie Channel Logger module
+Copyright 2014, David Baumgold <david@davidbaumgold.com>
+
+Licensed under the Eiffel Forum License 2
+
+http://willie.dftba.net
 """
 from __future__ import unicode_literals
 import os


Test patch:
Repository: sopel-irc/sopel

Issue:
[core] idiot-proof Willie: prevent running as root
We should add a check in willie.py to refuse running willie as root.



Discussion:

Resolving patch:
diff --git a/willie.py b/willie.py
--- a/willie.py
+++ b/willie.py
@@ -92,6 +92,14 @@ def main(argv=None):
                               'database configuration options.'))
         opts = parser.parse_args()
 
+        try:
+            if os.getuid() == 0 or os.geteuid() == 0:
+                stderr('Error: Do not run Willie with root privileges.')
+                sys.exit(1)
+        except AttributeError:
+            # Windows don't have os.getuid/os.geteuid
+            pass
+
         if opts.wizard:
             wizard('all', opts.config)
             return


Test patch:
Repository: sopel-irc/sopel

Issue:
Shuts down modules after 2 minutes after a reconnection
Steps to reproduce:
- Run a local irc daemon like miniircd
- Connect the bot to it
- Kill the irc daemon and restart it
- The bot reconnects
- After 2 minutes, the bot says:
  Ping timeout reached after 120 seconds, closing connection
  Calling shutdown for n modules.

This happens because the timeout_check thread is still running for the first connection, and it's the one that triggers the connection closing.



Discussion:
In fact, it looks like from the code that the ping thread is also kept running, although it doesn't do anything besides waiting after the first connection closes.


Resolving patch:
diff --git a/willie/irc.py b/willie/irc.py
--- a/willie/irc.py
+++ b/willie/irc.py
@@ -318,7 +318,7 @@ def handle_connect(self):
         ping_thread.start()
 
     def _timeout_check(self):
-        while True:
+        while self.connected or self.connecting:
             if (datetime.now() - self.last_ping_time).seconds > int(self.config.timeout):
                 stderr('Ping timeout reached after %s seconds, closing connection' % self.config.timeout)
                 self.handle_close()
@@ -327,7 +327,7 @@ def _timeout_check(self):
                 time.sleep(int(self.config.timeout))
 
     def _send_ping(self):
-        while True:
+        while self.connected or self.connecting:
             if self.connected and (datetime.now() - self.last_ping_time).seconds > int(self.config.timeout) / 2:
                 try:
                     self.write(('PING', self.config.host))


Test patch:
Repository: sopel-irc/sopel

Issue:
Configuration error with python 3.3
On the first run of willie 4.4.1, installed from pip, the configuration file can't be created due to the use of raw_input(), function replaced by input() in python3.

here is the error : 

``` bash
Welcome to Willie!
I can't seem to find the configuration file, so let's generate it!

Please answer the following questions to create your configuration file:

Encountered an error while writing the config file. This shouldn't happen. Check permissions.
Traceback (most recent call last):
  File "/home/willie/IRC/bin/willie", line 213, in <module>
    main()
  File "/home/willie/IRC/bin/willie", line 131, in main
    create_config(configpath)
  File "/home/willie/IRC/lib/python3.3/site-packages/willie/config.py", line 450, in create_config
    config._core()
  File "/home/willie/IRC/lib/python3.3/site-packages/willie/config.py", line 298, in _core
    'Willie')
  File "/home/willie/IRC/lib/python3.3/site-packages/willie/config.py", line 228, in interactive_add
    value = raw_input(prompt + ' [%s]: ' % default) or default
NameError: global name 'raw_input' is not defined
```

If someone can reproduce the problem I'll submit a pull request



Discussion:

Resolving patch:
diff --git a/willie/config.py b/willie/config.py
--- a/willie/config.py
+++ b/willie/config.py
@@ -53,6 +53,7 @@
 if sys.version_info.major >= 3:
     unicode = str
     basestring = str
+    raw_input = input
 
 class ConfigurationError(Exception):
     """ Exception type for configuration errors """


Test patch:
Repository: sopel-irc/sopel

Issue:
Remind is case sensitive
[22:20] <Eyore> .in 3SEC stuff
[22:20] @Willie Eyore: Sorry, didn't understand the input.
[22:20] <Eyore> .in 3 SEC stuff
[22:20] @Willie Eyore: Sorry, didn't understand the input.
[22:20] <Eyore> .in 3 S stuff
[22:20] @Willie Eyore: Sorry, didn't understand the input.
[22:20] <Eyore> .in 3 MIN stuff
[22:20] @Willie Eyore: Sorry, didn't understand the input.
[22:20] <Eyore> .in 3 Min stuff
[22:20] @Willie Eyore: Sorry, didn't understand the input.

I'd fix it myself, but looking at the code, I'm not sure where to put the .lower()



Discussion:

Resolving patch:
diff --git a/willie/modules/remind.py b/willie/modules/remind.py
--- a/willie/modules/remind.py
+++ b/willie/modules/remind.py
@@ -125,7 +125,7 @@ def monitor(bot):
 def remind(bot, trigger):
     """Gives you a reminder in the given amount of time."""
     duration = 0
-    message = filter(None, re.split('(\d+(?:\.\d+)? ?(?:' + periods + ')) ?',
+    message = filter(None, re.split('(\d+(?:\.\d+)? ?(?:(?i)' + periods + ')) ?',
                                     trigger.group(2))[1:])
     reminder = ''
     stop = False
@@ -133,7 +133,7 @@ def remind(bot, trigger):
         grp = re.match('(\d+(?:\.\d+)?) ?(.*) ?', piece)
         if grp and not stop:
             length = float(grp.group(1))
-            factor = scaling.get(grp.group(2), 60)
+            factor = scaling.get(grp.group(2).lower(), 60)
             duration += length * factor
         else:
             reminder = reminder + piece


Test patch:
Repository: sopel-irc/sopel

Issue:
[search].duck is horribly broken.
It appears we're scraping the page wrong, since ".duck wikipedia" returns an ad page.



Discussion:

Resolving patch:
diff --git a/willie/modules/search.py b/willie/modules/search.py
--- a/willie/modules/search.py
+++ b/willie/modules/search.py
@@ -127,6 +127,8 @@ def duck_search(query):
     query = query.replace('!', '')
     uri = 'http://duckduckgo.com/html/?q=%s&kl=uk-en' % query
     bytes = web.get(uri)
+    if 'web-result"' in bytes: #filter out the adds on top of the page
+        bytes = bytes.split('web-result"')[1]
     m = r_duck.search(bytes)
     if m:
         return web.decode(m.group(1))


Test patch:
Repository: sopel-irc/sopel

Issue:
Make .seen persist over bot restarts
Currently, when a Willie-based bot is restarted, it loses all the info about who it saw. It is quite inconvenient, as restarts are required quite often, especially on networks where there are lots of netsplits going on and the bot loses its nick to them all the time. The proposed solution would be to keep a persistent DB containing the relevant info from which old records may or may not be auto-deleted at regular intervals.



Discussion:
Relabeling this. Not a simple tweak, and should probably wait for the 5.0 db

@embolalia do we want this for 5.0?

Sure. I wouldn't hold 5.0 up for it, but anything that gets done before I release 5.0 can go in it.


Resolving patch:
diff --git a/willie/modules/seen.py b/willie/modules/seen.py
--- a/willie/modules/seen.py
+++ b/willie/modules/seen.py
@@ -11,11 +11,9 @@
 
 import time
 import datetime
-from willie.tools import Ddict, Identifier, get_timezone, format_time
+from willie.tools import Identifier, get_timezone, format_time
 from willie.module import commands, rule, priority
 
-seen_dict = Ddict(dict)
-
 
 @commands('seen')
 def seen(bot, trigger):
@@ -23,11 +21,11 @@ def seen(bot, trigger):
     if not trigger.group(2):
         bot.say(".seen <nick> - Reports when <nick> was last seen.")
         return
-    nick = Identifier(trigger.group(2).strip())
-    if nick in seen_dict:
-        timestamp = seen_dict[nick]['timestamp']
-        channel = seen_dict[nick]['channel']
-        message = seen_dict[nick]['message']
+    nick = trigger.group(2).strip()
+    timestamp = bot.db.get_nick_value(nick, 'seen_timestamp')
+    if timestamp:
+        channel = bot.db.get_nick_value(nick, 'seen_channel')
+        message = bot.db.get_nick_value(nick, 'seen_message')
 
         tz = get_timezone(bot.db, bot.config, None, trigger.nick,
                           trigger.sender)
@@ -42,14 +40,13 @@ def seen(bot, trigger):
             msg += " in another channel."
         bot.say(str(trigger.nick) + ': ' + msg)
     else:
-        bot.say("Sorry, I haven't seen %s around." % nick)
+        bot.say("Sorry, I haven't seen {} around.".format(nick))
 
 
 @rule('(.*)')
 @priority('low')
 def note(bot, trigger):
     if not trigger.is_privmsg:
-        nick = Identifier(trigger.nick)
-        seen_dict[nick]['timestamp'] = time.time()
-        seen_dict[nick]['channel'] = trigger.sender
-        seen_dict[nick]['message'] = trigger
+        bot.db.set_nick_value(trigger.nick, 'seen_timestamp', time.time())
+        bot.db.set_nick_value(trigger.nick, 'seen_channel', trigger.sender)
+        bot.db.set_nick_value(trigger.nick, 'seen_message', trigger)


Test patch:
Repository: sopel-irc/sopel

Issue:
Strip leading . on .tld queries
`.tld .py` should be the same as `.tld py`



Discussion:

Resolving patch:
diff --git a/willie/modules/tld.py b/willie/modules/tld.py
--- a/willie/modules/tld.py
+++ b/willie/modules/tld.py
@@ -24,13 +24,16 @@
 def gettld(bot, trigger):
     """Show information about the given Top Level Domain."""
     page = web.get(uri)
+    tld = trigger.group(2)
+    if tld[0] == '.':
+        tld = tld[1:]
     search = r'(?i)<td><a href="\S+" title="\S+">\.{0}</a></td>\n(<td><a href=".*</a></td>\n)?<td>([A-Za-z0-9].*?)</td>\n<td>(.*)</td>\n<td[^>]*>(.*?)</td>\n<td[^>]*>(.*?)</td>\n'
-    search = search.format(trigger.group(2))
+    search = search.format(tld)
     re_country = re.compile(search)
     matches = re_country.findall(page)
     if not matches:
         search = r'(?i)<td><a href="\S+" title="(\S+)">\.{0}</a></td>\n<td><a href=".*">(.*)</a></td>\n<td>([A-Za-z0-9].*?)</td>\n<td[^>]*>(.*?)</td>\n<td[^>]*>(.*?)</td>\n'
-        search = search.format(trigger.group(2))
+        search = search.format(tld)
         re_country = re.compile(search)
         matches = re_country.findall(page)
     if matches:
@@ -47,7 +50,7 @@ def gettld(bot, trigger):
         bot.reply(reply)
     else:
         search = r'<td><a href="\S+" title="\S+">.{0}</a></td>\n<td><span class="flagicon"><img.*?\">(.*?)</a></td>\n<td[^>]*>(.*?)</td>\n<td[^>]*>(.*?)</td>\n<td[^>]*>(.*?)</td>\n<td[^>]*>(.*?)</td>\n<td[^>]*>(.*?)</td>\n'
-        search = search.format(unicode(trigger.group(2)))
+        search = search.format(unicode(tld))
         re_country = re.compile(search)
         matches = re_country.findall(page)
         if matches:
@@ -62,5 +65,5 @@ def gettld(bot, trigger):
                 dict_val["notes"] = dict_val["notes"][:400] + "..."
             reply = "%s (%s, %s). IDN: %s, DNSSEC: %s, SLD: %s" % (dict_val["country"], dict_val["expl"], dict_val["notes"], dict_val["idn"], dict_val["dnssec"], dict_val["sld"])
         else:
-            reply = "No matches found for TLD: {0}".format(unicode(trigger.group(2)))
+            reply = "No matches found for TLD: {0}".format(unicode(tld))
         bot.reply(reply)


Test patch:
Repository: sopel-irc/sopel

Issue:
.at without an argument gives TypeError
```
<embolalia> .at
<Sopel> TypeError: expected string or buffer (file "/opt/rh/python33/root/usr/lib/python3.3/site-packages/sopel/modules/remind.py", line 165, in at)
```

.at without an argument gives TypeError
```
<embolalia> .at
<Sopel> TypeError: expected string or buffer (file "/opt/rh/python33/root/usr/lib/python3.3/site-packages/sopel/modules/remind.py", line 165, in at)
```



Discussion:

Resolving patch:
diff --git a/sopel/modules/remind.py b/sopel/modules/remind.py
--- a/sopel/modules/remind.py
+++ b/sopel/modules/remind.py
@@ -126,6 +126,12 @@ def monitor(bot):
 @example('.in 3h45m Go to class')
 def remind(bot, trigger):
     """Gives you a reminder in the given amount of time."""
+    if not trigger.group(2):
+        bot.say("Missing arguments for reminder command.")
+        return NOLIMIT
+    if trigger.group(3) and not trigger.group(4):
+        bot.say("No message given for reminder.")
+        return NOLIMIT
     duration = 0
     message = filter(None, re.split('(\d+(?:\.\d+)? ?(?:(?i)' + periods + ')) ?',
                                     trigger.group(2))[1:])
@@ -161,6 +167,12 @@ def at(bot, trigger):
     are those from the tzdb; a list of valid options is available at
     http://dft.ba/-tz . The seconds and timezone are optional.
     """
+    if not trigger.group(2):
+        bot.say("No arguments given for reminder command.")
+        return NOLIMIT
+    if trigger.group(3) and not trigger.group(4):
+        bot.say("No message given for reminder.")
+        return NOLIMIT
     regex = re.compile(r'(\d+):(\d+)(?::(\d+))?([^\s\d]+)? (.*)')
     match = regex.match(trigger.group(2))
     if not match:


Test patch:
Repository: sopel-irc/sopel

Issue:
The example() decorator is improperly "fixing" the example text.
Version: 6.0.0

Code excerpt

``` python
@commands('abort')
@example(".abort")
```

Help output:

``` text
08:49  bgallew: .help abort
08:49  DevEgo: bgallew: Abort any/all pending power management commands.
08:49  DevEgo: bgallew: e.g. \.bort
```

If you update the prefix setting to be, say, '.|!', it's even more broken.

FTR, this affects Sopel's built-ins, too:

``` text
08:54  bgallew: .help help
08:54  DevEgo: bgallew: Shows a command's documentation, and possibly an example.
08:54  DevEgo: bgallew: e.g. \.elp tell
```



Discussion:

Resolving patch:
diff --git a/sopel/loader.py b/sopel/loader.py
--- a/sopel/loader.py
+++ b/sopel/loader.py
@@ -142,7 +142,7 @@ def clean_callable(func, config):
     puts them in func._docs, and sets defaults"""
     nick = config.core.nick
     prefix = config.core.prefix
-    help_prefix = config.core.prefix
+    help_prefix = config.core.help_prefix
     func._docs = {}
     doc = trim_docstring(func.__doc__)
     example = None


Test patch:
Repository: sopel-irc/sopel

Issue:
Remove feedparser dependency
The weather module needlessly uses `feedparser` for some things, which adds a needless (python3-incompatible) dependency. It should be done with straight XML processing, instead.



Discussion:

Resolving patch:
diff --git a/sopel/modules/weather.py b/sopel/modules/weather.py
--- a/sopel/modules/weather.py
+++ b/sopel/modules/weather.py
@@ -12,7 +12,6 @@
 from sopel import web
 from sopel.module import commands, example, NOLIMIT
 
-import feedparser
 import xmltodict
 
 
@@ -36,10 +35,10 @@ def woeid_search(query):
 
 def get_cover(parsed):
     try:
-        condition = parsed.entries[0]['yweather_condition']
+        condition = parsed['channel']['item']['yweather:condition']
     except KeyError:
         return 'unknown'
-    text = condition['text']
+    text = condition['@text']
     # code = int(condition['code'])
     # TODO parse code to get those little icon thingies.
     return text
@@ -47,8 +46,8 @@ def get_cover(parsed):
 
 def get_temp(parsed):
     try:
-        condition = parsed.entries[0]['yweather_condition']
-        temp = int(condition['temp'])
+        condition = parsed['channel']['item']['yweather:condition']
+        temp = int(condition['@temp'])
     except (KeyError, ValueError):
         return 'unknown'
     f = round((temp * 1.8) + 32, 2)
@@ -57,7 +56,7 @@ def get_temp(parsed):
 
 def get_humidity(parsed):
     try:
-        humidity = parsed['feed']['yweather_atmosphere']['humidity']
+        humidity = parsed['channel']['yweather:atmosphere']['@humidity']
     except (KeyError, ValueError):
         return 'unknown'
     return "Humidity: %s%%" % humidity
@@ -65,11 +64,11 @@ def get_humidity(parsed):
 
 def get_wind(parsed):
     try:
-        wind_data = parsed['feed']['yweather_wind']
-        kph = float(wind_data['speed'])
+        wind_data = parsed['channel']['yweather:wind']
+        kph = float(wind_data['@speed'])
         m_s = float(round(kph / 3.6, 1))
         speed = int(round(kph / 1.852, 0))
-        degrees = int(wind_data['direction'])
+        degrees = int(wind_data['@direction'])
     except (KeyError, ValueError):
         return 'unknown'
 
@@ -144,9 +143,10 @@ def weather(bot, trigger):
         return bot.reply("I don't know where that is.")
 
     query = web.urlencode({'w': woeid, 'u': 'c'})
-    url = 'http://weather.yahooapis.com/forecastrss?' + query
-    parsed = feedparser.parse(url)
-    location = parsed['feed']['title']
+    raw = web.get('http://weather.yahooapis.com/forecastrss?' + query, 
+                  dont_decode=True)
+    parsed = xmltodict.parse(raw).get('rss')
+    location = parsed.get('channel').get('title')
 
     cover = get_cover(parsed)
     temp = get_temp(parsed)
@@ -171,7 +171,7 @@ def update_woeid(bot, trigger):
 
     bot.db.set_nick_value(trigger.nick, 'woeid', woeid)
 
-    neighborhood = first_result.get('neighborhood').text or ''
+    neighborhood = first_result.get('neighborhood') or ''
     if neighborhood:
         neighborhood += ','
     city = first_result.get('city') or ''


Test patch: