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

Issue:
admin: .set command can't handle spaces
I think I once discovered this a long, long time ago and forgot to document it. I vaguely remember hitting this problem way back when I first started using Sopel (at the time, Willie)… But here we are. I hit it again, and this time it's getting an issue so it can be fixed!

When PMing Sopel `.set section.key spam eggs sausage and spam`, the value in `section.key` of the config will become simply `spam`. Because `admin.py` uses `trigger.group(4)` as the value, it ignores anything after the first whitespace.

The `.set` command probably isn't used a whole lot, but it should still work as expected. As-is, it's useless for updating things like `core.default_time_format`, which almost _always_ contain spaces.


Discussion:

Resolving patch:
diff --git a/sopel/modules/admin.py b/sopel/modules/admin.py
--- a/sopel/modules/admin.py
+++ b/sopel/modules/admin.py
@@ -191,9 +191,13 @@ def set_config(bot, trigger):
         bot.say('[{}] section has no option {}.'.format(section_name, option))
         return
 
+    delim = trigger.group(2).find(' ')
+    # Skip preceeding whitespaces, if any.
+    while delim > 0 and delim < len(trigger.group(2)) and trigger.group(2)[delim] == ' ':
+        delim = delim + 1
+
     # Display current value if no value is given.
-    value = trigger.group(4)
-    if not value:
+    if delim == -1 or delim == len(trigger.group(2)):
         if not static_sec and bot.config.parser.has_option(section, option):
             bot.reply("Option %s.%s does not exist." % (section_name, option))
             return
@@ -207,6 +211,7 @@ def set_config(bot, trigger):
         return
 
     # Otherwise, set the value to one given as argument 2.
+    value = trigger.group(2)[delim:]
     if static_sec:
         descriptor = getattr(section.__class__, option)
         try:


Test patch:
Repository: sopel-irc/sopel

Issue:
[dice] Ugly SyntaxErrors for invalid die rolls
People really like trying to break bots… Several users got in on the fun this weekend, trying to roll dice like `0x1d0x3`, `0dNaN`, `1d3d4d5`, `onedone`, and variations on that sort of thing.

The `dice` module, I guess, doesn't catch `SyntaxError` exceptions. If parsing the given dice expression fails, some generic error ("I'm sorry, you have to give me real dice." or something like that) should get output rather than the likes of `Ast.Node 'Call' not implemented`.

Probably an easy fix, which I'll get to soon enough if nobody else beats me to it.


Discussion:

Resolving patch:
diff --git a/sopel/modules/dice.py b/sopel/modules/dice.py
--- a/sopel/modules/dice.py
+++ b/sopel/modules/dice.py
@@ -218,12 +218,20 @@ def _get_pretty_str(dice):
     eval_str = arg_str % (tuple(map(_get_eval_str, dice)))
     pretty_str = arg_str % (tuple(map(_get_pretty_str, dice)))
 
-    # Showing the actual error will hopefully give a better hint of what is
-    # wrong with the syntax than a generic error message.
     try:
         result = eval_equation(eval_str)
-    except Exception as e:
-        bot.reply("SyntaxError, eval(%s), %s" % (eval_str, e))
+    except TypeError:
+        bot.reply("The type of this equation is, apparently, not a string. " +
+            "How did you do that, anyway?")
+    except ValueError:
+        # As it seems that ValueError is raised if the resulting equation would
+        # be too big, give a semi-serious answer to reflect on this.
+        bot.reply("You roll %s: %s = very big" % (
+            trigger.group(2), pretty_str))
+        return
+    except (SyntaxError, eval_equation.Error):
+        bot.reply("I don't know how to process that. " +
+            "Are the dice as well as the algorithms correct?")
         return
 
     bot.reply("You roll %s: %s = %d" % (


Test patch:
Repository: sopel-irc/sopel

Issue:
Errors thrown before connecting finishes do not run shutdown routines
I'll just copy [the original report](https://github.com/sopel-irc/sopel/issues/1369#issuecomment-415956806) from @tbnobody in #1369:

> I found out that several connection errors don't fire the shutdown method. Please see the following log snippet. The first error "Nickname already in use" lead to a clean shutdown. But the second error "Connection Error" didn't fire the shutdown method:
> 
> ```
> Welcome to Sopel. Loading modules...
> 
> 
> MqTTGateway: Bot Setup Called
> MqTTGateway: Perform MqTT Setup
> 
> 
> Registered 25 modules,
> 0 modules failed to load
> 
> 
> Connecting to irc.hackint.org:6697...
> MqTTGateway: connected OK Returned code= 0
> MqTTGateway: Subscribing to topics...
> MqTTGateway: done.
> MqTTGateway: Subscribed: 1 (0,)
> MqTTGateway: Subscribed: 2 (0,)
> MqTTGateway: Subscribed: 3 (0,)
> Connected.
> Nickname already in use!
> Calling shutdown for 2 modules.
> calling bugzilla.shutdown
> Error calling shutdown method for module bugzilla:None
> calling mqttGateway.shutdown
> MqTTGateway: Bot Shutdown Called
> MqTTGateway: Closed!
> Warning: Disconnected. Reconnecting in 20 seconds...
> 
> Welcome to Sopel. Loading modules...
> 
> 
> MqTTGateway: Bot Setup Called
> MqTTGateway: Perform MqTT Setup
> 
> 
> Registered 25 modules,
> 0 modules failed to load
> 
> 
> Connecting to irc.hackint.org:6697...
> MqTTGateway: connected OK Returned code= 0
> MqTTGateway: Subscribing to topics...
> MqTTGateway: done.
> MqTTGateway: Subscribed: 1 (0,)
> MqTTGateway: Subscribed: 2 (0,)
> MqTTGateway: Subscribed: 3 (0,)
> Connection error: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:720)
> Warning: Disconnected. Reconnecting in 20 seconds...
> 
> Welcome to Sopel. Loading modules...
> 
> 
> MqTTGateway: Bot Setup Called
> MqTTGateway: Perform MqTT Setup
> ```

It seems (from my own checks) that Sopel won't fire shutdown methods until after the IRC connection is established. From there, if an error occurs, it's fine (even if the error is nearly immediate, like trying to use a nickname that's already in use). Before the connection finishes, though, nope.

This is a bug because there should never be two "Loading modules" without a "Calling shutdown for _n_ modules" between them. If encountering an error is going to be treated as (basically) a restart, it should _always_ be treated as such no matter what error occurred.


Discussion:

Resolving patch:
diff --git a/sopel/irc.py b/sopel/irc.py
--- a/sopel/irc.py
+++ b/sopel/irc.py
@@ -155,6 +155,7 @@ def run(self, host, port=6667):
             self.initiate_connect(host, port)
         except socket.error as e:
             stderr('Connection error: %s' % e)
+            self.handle_close()
 
     def initiate_connect(self, host, port):
         stderr('Connecting to %s:%s...' % (host, port))


Test patch:
Repository: sopel-irc/sopel

Issue:
Option to specify alternate config directory on first run
When the bot is first run, it executes ```sopel/run_script.py``` at startup.
Said script is hardcoded to create ```~/.sopel``` if it doesn't already exist.
Should there be an option to allow the user to specify an alternate directory to use?

This was observed on d9cfb41 running ```python sopel.py``` without prior configuration.


Discussion:
@alanhuang122 are you thinking something like `sopel -d ~/.sopel-alt`? 

Resolving patch:
diff --git a/sopel/config/__init__.py b/sopel/config/__init__.py
--- a/sopel/config/__init__.py
+++ b/sopel/config/__init__.py
@@ -214,12 +214,12 @@ def _modules(self):
 
 
 def _wizard(section, config=None):
-    dotdir = os.path.expanduser('~/.sopel')
+    dotdir = os.path.dirname(config) if config is not None else DEFAULT_HOMEDIR
     configpath = os.path.join(dotdir, ((config or 'default.cfg') + ('.cfg' if config and not config.endswith('.cfg') else '')))
     if section == 'all':
         _create_config(configpath)
     elif section == 'mod':
-        _check_dir(False)
+        _check_dir(dotdir, False)
         if not os.path.isfile(configpath):
             print("No config file found." +
                   " Please make one before configuring these options.")
@@ -228,15 +228,14 @@ def _wizard(section, config=None):
         config._modules()
 
 
-def _check_dir(create=True):
-    dotdir = os.path.join(os.path.expanduser('~'), '.sopel')
-    if not os.path.isdir(dotdir):
+def _check_dir(path=DEFAULT_HOMEDIR, create=True):
+    if not os.path.isdir(path):
         if create:
-            print('Creating a config directory at ~/.sopel...')
+            print('Creating a config directory at {}...'.format(path))
             try:
-                os.makedirs(dotdir)
+                os.makedirs(path)
             except Exception as e:
-                print('There was a problem creating %s:' % dotdir, file=sys.stderr)
+                print('There was a problem creating %s:' % path, file=sys.stderr)
                 print('%s, %s' % (e.__class__, str(e)), file=sys.stderr)
                 print('Please fix this and then run Sopel again.', file=sys.stderr)
                 sys.exit(1)
@@ -246,7 +245,7 @@ def _check_dir(create=True):
 
 
 def _create_config(configpath):
-    _check_dir()
+    _check_dir(os.path.dirname(configpath))
     print("Please answer the following questions" +
           " to create your configuration file:\n")
     try:


Test patch:
Repository: sopel-irc/sopel

Issue:
.reload doesn't run shutdown routines
When `.reload` is invoked, none of the shutdown routines are ran before the modules are being loaded and initalized again.

The following text is from the output logs when I was testing this (Lines starting with `#` are added as additional info, and is not in the actual output):
```
Welcome to Sopel. Loading modules...




Registered 52 modules,
0 modules failed to load

Connecting to localhost:6667...
Connected.
# .reload is invoked here

Welcome to Sopel. Loading modules...




Registered 52 modules,
0 modules failed to load

# bot is signalled to shut down here
Got quit signal, shutting down.
ERROR:sopel.modules.sopel.irc:ERROR received from server: Closing Link: Sopel[localhost] (Quit: Closing)
Calling shutdown for 6 modules.
calling reddit.shutdown
calling bugzilla.shutdown
calling media.shutdown
calling reddit.shutdown
Error calling shutdown method for module reddit:re.compile('https?://(?:www\\.|np\\.)?reddit\\.com/r/(.*?)/comments/([\\w-]+)')
calling bugzilla.shutdown
calling media.shutdown
Closed!
```

Note that at the second module loading, there are no shutdown routines being called on modules.


Discussion:

Resolving patch:
diff --git a/sopel/bot.py b/sopel/bot.py
--- a/sopel/bot.py
+++ b/sopel/bot.py
@@ -580,6 +580,8 @@ def _shutdown(self):
                         shutdown_method.__module__, e
                     )
                 )
+        # Avoid calling shutdown methods if we already have.
+        self.shutdown_methods = []
 
     def cap_req(self, module_name, capability, arg=None, failure_callback=None,
                 success_callback=None):
diff --git a/sopel/modules/reload.py b/sopel/modules/reload.py
--- a/sopel/modules/reload.py
+++ b/sopel/modules/reload.py
@@ -11,7 +11,7 @@
 import collections
 import sys
 import time
-from sopel.tools import iteritems
+from sopel.tools import stderr, iteritems
 import sopel.loader
 import sopel.module
 import subprocess
@@ -67,6 +67,22 @@ def reload_module_tree(bot, name, seen=None, silent=False):
     old_callables = {}
     for obj_name, obj in iteritems(vars(old_module)):
         if callable(obj):
+            if (getattr(obj, '__name__', None) == 'shutdown' and
+                        obj in bot.shutdown_methods):
+                # If this is a shutdown method, call it first.
+                try:
+                    stderr(
+                        "calling %s.%s" % (
+                            obj.__module__, obj.__name__,
+                        )
+                    )
+                    obj(bot)
+                except Exception as e:
+                    stderr(
+                        "Error calling shutdown method for module %s:%s" % (
+                            obj.__module__, e
+                        )
+                    )
             bot.unregister(obj)
         elif (type(obj) is ModuleType and
               obj.__name__.startswith(name + '.') and


Test patch:
Repository: sopel-irc/sopel

Issue:
reddit: support all reddit subdomains
#1397 adds support for links to `old.reddit.com`, but any two-letter subdomain of `reddit.com` is valid as well. Reddit uses these for internationalization (e.g. `it.reddit.com` -> Italian UI) and also to allow subreddits to add custom styles (a common example is using `np.reddit.com` to trigger a "no participation" stylesheet that hides voting arrows).

Sopel's reddit module should support these subdomains.

There are also really old three-letter subdomains (`pay`, `ssl`) that still work. Most three-letter combos I tried redirect to `www.reddit.com/r/combohere` though.


Discussion:

Resolving patch:
diff --git a/sopel/modules/reddit.py b/sopel/modules/reddit.py
--- a/sopel/modules/reddit.py
+++ b/sopel/modules/reddit.py
@@ -22,7 +22,7 @@
     unescape = HTMLParser().unescape
 
 
-domain = r'https?://(?:www\.|np\.|old\.)?reddit\.com'
+domain = r'https?://(?:www\.|old\.|pay\.|ssl\.|[a-z]{2}\.)?reddit\.com'
 post_url = r'%s/r/(.*?)/comments/([\w-]+)' % domain
 user_url = r'%s/u(ser)?/([\w-]+)' % domain
 post_regex = re.compile(post_url)


Test patch:
Repository: sopel-irc/sopel

Issue:
User objects aren't updated on NICK events
````
<Reptarsaurus> Okay, I just tested....I am doing users = chans[chan].users.values() after
               a nick change, and 'for user in users: print(user.nick)' is showing the
               nick pre-change.
````

This report from Sopel's IRC channel told me something was off in the code that keeps track of users, and indeed something is. Nowhere in the `coretasks` handler for `NICK` events does Sopel update the user object's `nick` property:

https://github.com/sopel-irc/sopel/blob/d4c4c16f850b63776b156fc8c9d3812287b15882/sopel/coretasks.py#L275-L284

`Channel.rename_user()` doesn't touch it either:

https://github.com/sopel-irc/sopel/blob/d4c4c16f850b63776b156fc8c9d3812287b15882/sopel/tools/target.py#L76-L80

I can't find any place in the code where this property gets updated, ever, and that's a bug. Thanks to Reptarsaurus for stumbling onto this, and it should be fixed in the next Sopel version.


Discussion:

Resolving patch:
diff --git a/sopel/tools/target.py b/sopel/tools/target.py
--- a/sopel/tools/target.py
+++ b/sopel/tools/target.py
@@ -76,6 +76,7 @@ def add_user(self, user):
     def rename_user(self, old, new):
         if old in self.users:
             self.users[new] = self.users.pop(old)
+            self.users[new].nick = new
         if old in self.privileges:
             self.privileges[new] = self.privileges.pop(old)
 


Test patch:
Repository: sopel-irc/sopel

Issue:
config: default_timezone has no default
I need to check for side-effects, but there shouldn't be any reason that `core.default_timezone` can't default to `'UTC'`. It currently defaults to `None`, which caused errors when I was testing #1162 just now.


Discussion:

Resolving patch:
diff --git a/sopel/config/core_section.py b/sopel/config/core_section.py
--- a/sopel/config/core_section.py
+++ b/sopel/config/core_section.py
@@ -103,7 +103,7 @@ class CoreSection(StaticSection):
                                              default='%Y-%m-%d - %T%Z')
     """The default format to use for time in messages."""
 
-    default_timezone = ValidatedAttribute('default_timezone')
+    default_timezone = ValidatedAttribute('default_timezone', default='UTC')
     """The default timezone to use for time in messages."""
 
     enable = ListAttribute('enable')


Test patch:
Repository: sopel-irc/sopel

Issue:
[wiktionary] Relax case sensitivity a bit?
Looking up anything other than the exact capitalization in Wiktionary's database appears to fail. It might be worthwhile, for user experience, to always try the all-lowercase version of a query if no results are returned for it as entered.

That said, the MediaWiki API would allow querying automatically for several variants (using `action=query&titles=variant1|Variant2|etc.`) and then fetching the first one that exists for display, if making multiple requests is OK in the plugin.


Discussion:

Resolving patch:
diff --git a/sopel/modules/wiktionary.py b/sopel/modules/wiktionary.py
--- a/sopel/modules/wiktionary.py
+++ b/sopel/modules/wiktionary.py
@@ -99,8 +99,11 @@ def wiktionary(bot, trigger):
 
     _etymology, definitions = wikt(word)
     if not definitions:
-        bot.say("Couldn't get any definitions for %s." % word)
-        return
+        # Cast word to lower to check in case of mismatched user input
+        _etymology, definitions = wikt(word.lower())
+        if not definitions:
+            bot.say("Couldn't get any definitions for %s." % word)
+            return
 
     result = format(word, definitions)
     if len(result) < 150:


Test patch:
Repository: sopel-irc/sopel

Issue:
Config filename is mis-parsed when running configuration wizard from command line
~~`-w` / `--configure-all` and~~ `--configure-modules` does not drop the file extension `.cfg` from the specified file when searching for a config file to use (unlike just launching the bot, where as the extension does seem to be dropped, or at least accounted for when finding the config file).

A user stopped into #sopel (2019-01-29, ~14:15 UTC-5) who had a `default.cfg` and `custom_config.cfg`. They primarily used `custom_config.cfg` and when attempting to configure modules for said `custom_config.cfg`, the used the command `sopel -c custom_config.cfg --configure-modules`. Sopel could not find `custom_config.cfg.cfg`, and without error/warning, edited `default.cfg`. Launching the bot with `sopel -c custom_config.cfg` still used the correct file though, so any changes made with the configuration wizard were never propogated to the config file that was actually being loaded.

**EDIT**: the `--configure-all` flag will just make a new config file like `custom_config.cfg.cfg`


Discussion:

Resolving patch:
diff --git a/sopel/config/__init__.py b/sopel/config/__init__.py
--- a/sopel/config/__init__.py
+++ b/sopel/config/__init__.py
@@ -212,7 +212,7 @@ def _modules(self):
 
 def _wizard(section, config=None):
     dotdir = os.path.expanduser('~/.sopel')
-    configpath = os.path.join(dotdir, (config or 'default') + '.cfg')
+    configpath = os.path.join(dotdir, ((config or 'default.cfg') + ('.cfg' if config and not config.endswith('.cfg') else '')))
     if section == 'all':
         _create_config(configpath)
     elif section == 'mod':


Test patch:
Repository: sopel-irc/sopel

Issue:
How do I let Sopel invite a user?
^



Discussion:

Resolving patch:
diff --git a/sopel/modules/invite.py b/sopel/modules/invite.py
new file mode 100644
--- /dev/null
+++ b/sopel/modules/invite.py
@@ -0,0 +1,76 @@
+# coding=utf-8
+"""
+invite.py - Sopel invite module
+Copyright © 2016, João Vanzuita, https://github.com/converge
+Copyright © 2019, dgw, https://github.com/dgw
+Licensed under the Eiffel Forum License 2.
+
+https://sopel.chat
+"""
+from __future__ import unicode_literals, absolute_import, print_function, division
+
+from sopel import module, tools
+
+
+MIN_PRIV = module.HALFOP
+
+
+def invite_handler(bot, sender, user, channel):
+    """Common control logic for invite commands received from anywhere."""
+    sender = tools.Identifier(sender)
+    user = tools.Identifier(user)
+    channel = tools.Identifier(channel)
+
+    # Sanity checks, in case someone reuses this function from outside the plugin
+    if not sender.is_nick():
+        raise ValueError("Invite sender must be a nick, not a channel.")
+    if not user.is_nick():
+        raise ValueError("User to invite must be a nick, not a channel.")
+    if channel.is_nick():
+        raise ValueError("Target channel name must not be a nick.")
+
+    # Sopel must be in the target channel
+    if channel not in bot.channels or bot.nick not in bot.channels[channel].privileges:
+        return bot.reply("I'm not in {}!".format(channel))
+
+    privs = bot.channels[channel].privileges
+
+    # Sopel must have sufficient privileges in the target channel to send invites
+    if privs[bot.nick] < MIN_PRIV:
+        return bot.reply("I don't have permission to invite anyone into {}.".format(channel))
+
+    # The sender must be in the target channel
+    if sender not in privs:
+        return bot.reply("You're not in {}.".format(channel))
+
+    # The sender must have sufficient privileges in the target channel to send invites
+    if privs[sender] < MIN_PRIV:
+        return bot.reply("You don't have permission to invite anyone into {}.".format(channel))
+
+    # Sopel and the sender both passed permission checks.
+    # DDDDOOOO IIIITTTT
+    bot.write(['INVITE', user, channel])
+
+
+@module.commands('invite')
+@module.example('.invite jenny')
+@module.example('.invite converge #sopel')
+def invite(bot, trigger):
+    """
+    Invite the given user to the current channel, or (with optional
+    second argument) another channel that Sopel is in.
+    """
+    if not trigger.group(3):
+        return bot.reply("Whom should I invite?")
+    user = trigger.group(3)
+
+    if trigger.group(4):
+        channel = trigger.group(4)
+    else:
+        if trigger.is_privmsg:
+            return bot.say(
+                "Channel is required ({}invite user #channel) when inviting from queries."
+                .format(bot.config.core.help_prefix))
+        channel = trigger.sender
+
+    invite_handler(bot, trigger.nick, user, channel)


Test patch: