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

Issue:
adminchannel: Restore op/voice commands
@kwaaak made a very good point the other day in PM on IRC: `adminchannel`'s commands for managing ops and voices were removed (902e715) for seemingly no particular reason.

I haven't decided when to do it (for 6.x or wait for Sopel 7), but I do think those commands should come back. They'll need a bit of tweaking, but nothing major. Milestone 6.6.0 for now, I guess, to be punted if I can think of a reason it should be delayed.


Discussion:

Resolving patch:
diff --git a/sopel/modules/adminchannel.py b/sopel/modules/adminchannel.py
--- a/sopel/modules/adminchannel.py
+++ b/sopel/modules/adminchannel.py
@@ -19,6 +19,74 @@ def default_mask(trigger):
     return '{} {} {} {}'.format(welcome, chan, topic_, arg)
 
 
+@require_chanmsg
+@require_privilege(OP, 'You are not a channel operator.')
+@commands('op')
+def op(bot, trigger):
+    """
+    Command to op users in a room. If no nick is given,
+    Sopel will op the nick who sent the command
+    """
+    if bot.channels[trigger.sender].privileges[bot.nick] < OP:
+        return bot.reply("I'm not a channel operator!")
+    nick = trigger.group(2)
+    channel = trigger.sender
+    if not nick:
+        nick = trigger.nick
+    bot.write(['MODE', channel, "+o", nick])
+
+
+@require_chanmsg
+@require_privilege(OP, 'You are not a channel operator.')
+@commands('deop')
+def deop(bot, trigger):
+    """
+    Command to deop users in a room. If no nick is given,
+    Sopel will deop the nick who sent the command
+    """
+    if bot.channels[trigger.sender].privileges[bot.nick] < OP:
+        return bot.reply("I'm not a channel operator!")
+    nick = trigger.group(2)
+    channel = trigger.sender
+    if not nick:
+        nick = trigger.nick
+    bot.write(['MODE', channel, "-o", nick])
+
+
+@require_chanmsg
+@require_privilege(OP, 'You are not a channel operator.')
+@commands('voice')
+def voice(bot, trigger):
+    """
+    Command to voice users in a room. If no nick is given,
+    Sopel will voice the nick who sent the command
+    """
+    if bot.channels[trigger.sender].privileges[bot.nick] < HALFOP:
+        return bot.reply("I'm not a channel operator!")
+    nick = trigger.group(2)
+    channel = trigger.sender
+    if not nick:
+        nick = trigger.nick
+    bot.write(['MODE', channel, "+v", nick])
+
+
+@require_chanmsg
+@require_privilege(OP, 'You are not a channel operator.')
+@commands('devoice')
+def devoice(bot, trigger):
+    """
+    Command to devoice users in a room. If no nick is given,
+    Sopel will devoice the nick who sent the command
+    """
+    if bot.channels[trigger.sender].privileges[bot.nick] < HALFOP:
+        return bot.reply("I'm not a channel operator!")
+    nick = trigger.group(2)
+    channel = trigger.sender
+    if not nick:
+        nick = trigger.nick
+    bot.write(['MODE', channel, "-v", nick])
+
+
 @require_chanmsg
 @require_privilege(OP, 'You are not a channel operator.')
 @commands('kick')


Test patch:
Repository: sopel-irc/sopel

Issue:
reddit: shortlinks are not handled
Reddit supports (and promotes in the comments page sidebar) a short link format for threads, `http(s)://redd.it/{id}`, where the `id` comes from here in a full-size link: `https://www.reddit.com/r/subreddit/comments/{id}/slugified_thread_title/`.

It should be trivial to add detection and handling of these links in the `reddit` module.

Not assigning to a milestone immediately, because I'd like to release this in a hypothetical version 6.7.0 *if* one happens before 7.0 (but none is planned). It does seem slightly more significant a change than would belong in one of the planned 6.6.x releases, unfortunately.


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
@@ -23,9 +23,11 @@
 
 
 domain = r'https?://(?:www\.|old\.|pay\.|ssl\.|[a-z]{2}\.)?reddit\.com'
-post_url = r'%s/r/(.*?)/comments/([\w-]+)' % domain
+post_url = r'%s/r/.*?/comments/([\w-]+)' % domain
+short_post_url = r'https?://redd.it/([\w-]+)'
 user_url = r'%s/u(ser)?/([\w-]+)' % domain
 post_regex = re.compile(post_url)
+short_post_regex = re.compile(short_post_url)
 user_regex = re.compile(user_url)
 spoiler_subs = [
     'stevenuniverse',
@@ -35,15 +37,18 @@
 
 def setup(bot):
     bot.register_url_callback(post_regex, rpost_info)
+    bot.register_url_callback(short_post_regex, rpost_info)
     bot.register_url_callback(user_regex, redditor_info)
 
 
 def shutdown(bot):
     bot.unregister_url_callback(post_regex)
+    bot.unregister_url_callback(short_post_regex)
     bot.unregister_url_callback(user_regex)
 
 
 @rule('.*%s.*' % post_url)
+@rule('.*%s.*' % short_post_url)
 def rpost_info(bot, trigger, match=None):
     match = match or trigger
     try:
@@ -52,10 +57,10 @@ def rpost_info(bot, trigger, match=None):
             client_id='6EiphT6SSQq7FQ',
             client_secret=None,
         )
-        s = r.submission(id=match.group(2))
+        s = r.submission(id=match.group(1))
     except Exception:
         r = praw.Reddit(user_agent=USER_AGENT)
-        s = r.get_submission(submission_id=match.group(2))
+        s = r.get_submission(submission_id=match.group(1))
 
     message = ('[REDDIT] {title} {link}{nsfw} | {points} points ({percent}) | '
                '{comments} comments | Posted by {author} | '


Test patch:
Repository: sopel-irc/sopel

Issue:
bot.hostmask
While bot.users.get(bot.nick).hostmask is nice to use, I think it would be slightly better to have a more direct approach like bot.hostmask .


Discussion:
Doesn't this duplicate #1322?
I mean, OK, I get where you're going with this (not wanting to lose this in the intent of that other ticket, which is to allow using the full line length).

Would be pretty easy to do just by making the "attribute" `bot.hostmask` a getter (via decorator, already used in other places) for `bot.users.get(bot.nick).hostmask`.

Resolving patch:
diff --git a/sopel/bot.py b/sopel/bot.py
--- a/sopel/bot.py
+++ b/sopel/bot.py
@@ -141,6 +141,17 @@ def __init__(self, config, daemon=False):
             self.config.core.host_blocks = []
         self.setup()
 
+    @property
+    def hostmask(self):
+        """str: the current hostmask for the bot :class:`sopel.tools.target.User`
+
+        Bot must be connected and in at least one channel.
+        """
+        if not self.users or not self.users.contains(self.nick):
+            raise KeyError("'hostmask' not available: bot must be connected and in at least one channel.")
+
+        return self.users.get(self.nick).hostmask
+
     # Backwards-compatibility aliases to attributes made private in 6.2. Remove
     # these in 7.0
     times = property(lambda self: getattr(self, '_times'))


Test patch:
Repository: sopel-irc/sopel

Issue:
.dice denial of service
If I spam sopel with `.dice 0xff^0xff^0xff`, sopel's replies to arbitrary commands (e.g., .`version`)  become indefinitely delayed. 

.version: `Sopel v. 6.6.5 (Python 3.4.8) at commit: 824ae7484b9e04310079a27d4d6ec40edeafdfd1`

environment: gentoo, ran from source, no special configuration

logs: nothing relevant in the logs

.py denial of service
Similar to #1550; spam `.py [0]*100000`.


Discussion:
Confirmed, thanks for the report. @HumorBaby is debugging as I type this.
The culprit is the flooding penalty logic:
https://github.com/sopel-irc/sopel/blob/824ae7484b9e04310079a27d4d6ec40edeafdfd1/sopel/bot.py#L323-L328


 If the delay between rolls (technically, between `bot.say` calls) is >3 seconds, all is well. If `bot.say` is called more frequently than every 3 seconds, the flood penalty is tripped, and it is based on the last message received to be sent.

The DoS behavior described above does not need spam of `.dice ...` calls. In fact, as long as `.dice ...` is called any time before 3 seconds after the last command (e.g., `.version`), the bot will essentially lock up.

Here is the crux of whats happening:
The result of `.dice 0xff^0xff^0xff` is a ~64kb string (of digits, of course). When a penalty is applied on the response to the aforementioned `.dice ...` call, `penalty = float(max(0, len(text) - 40)) / 70` evaluates to `penalty = 2235` seconds (~37 minutes). The thread will then sleep for the `wait = 0.8  + penalty` and the `sending` lock never gets released.

For most commands, this is not an issue, because `len(text) / 70 ~< 2` seconds, and so there is no major lag. But here, because the text is not truncated, `len(text)` blows up the final `penalty`.

The same applies to #1551 
Confirmed, thanks for the report. @HumorBaby is debugging as I type this.

Resolving patch:
diff --git a/sopel/bot.py b/sopel/bot.py
--- a/sopel/bot.py
+++ b/sopel/bot.py
@@ -313,7 +313,7 @@ def say(self, text, recipient, max_messages=1):
                 elapsed = time.time() - self.stack[recipient_id][-1][0]
                 if elapsed < 3:
                     penalty = float(max(0, len(text) - 40)) / 70
-                    wait = 0.8 + penalty
+                    wait = min(0.8 + penalty, 2)  # Never wait more than 2 seconds
                     if elapsed < wait:
                         time.sleep(wait - elapsed)
 


Test patch:
Repository: sopel-irc/sopel

Issue:
admin: admins can usurp owners
I believe  `admin` and `owner` are meant to be two separate permission levels for the bot. There are `@require_admin` and `@require_owner` for commands, implying that there are certain functions for an `owner` that should not be available to `admin`s. 

That being said, if the `admin` plugin is enabled, an `admin` can simply:
<details>
<summary>Not a spoiler :sweat_smile: </summary>

<pre>.set core.owner <i>I-Am-gRoot</i></pre> 
</details>
bypassing the <code>@require_owner</code>, and potentially locking them out of their own bot.


Discussion:
Good ol' privilege escalation vulnerabilities… Where would we be without them?

The obvious, easy solution is to hard-code into `admin.py` a block on changing this specific setting if the user isn't owner. I'd like not to do that, though. I already don't like the hard-coded logic around preventing the output of passwords when getting existing setting values.

Buuuuuut, fixing it another way would involve API-breaking. That is, settings would somehow need a way to reject being changed by lowly admins (or at all) at runtime, and that would require changing the object model (I think). Fine for Sopel 8—as I try to avoid adding anything more to the overstuffed Sopel 7 milestone 😆—but not something we can shove into a 6.6.x patch release.

Don't really want to leave this backdoor open until 8.x happens, though. That's over a year off.

How about fixing it the easy way for 6.6.x (as now milestoned)? We can open a follow-up issue to design a settings API change after that's done and released.

Resolving patch:
diff --git a/sopel/modules/admin.py b/sopel/modules/admin.py
--- a/sopel/modules/admin.py
+++ b/sopel/modules/admin.py
@@ -220,6 +220,13 @@ def set_config(bot, trigger):
         bot.reply("%s.%s = %s" % (section_name, option, value))
         return
 
+    # 'core.owner' cannot be set by the bot. Any changes to this setting must be
+    # made manually in the config file.
+    if section_name == 'core' and option == 'owner':
+        bot.say('\'core.owner\' cannot be set by the bot. '
+                'This setting must be changed manually in the configuration file.')
+        return
+
     # Otherwise, set the value to one given as argument 2.
     value = trigger.group(2)[delim:]
     if static_sec:


Test patch:
Repository: sopel-irc/sopel

Issue:
etymology: Empty argument not handled
Doing `.ety` with no or an empty argument doesn't behave properly.

On Python 2 (my old personal instance), it causes `TypeError: None object cannot be quoted (file "/usr/lib/python2.7/urllib.py", line 1255, in quote)`

On Python 3 ("official" Sopel instance on freenode), the command looks up the etymology of "None" (and gets no results, because of the capital letter).

This is a good, easy issue for first-time contributors to fix.


Discussion:
As a pointer for future new contributors: There's no check for empty argument, despite this check for whether the word is too long: https://github.com/sopel-irc/sopel/blob/f3906680ed72aabbf14be144ebe01ce24b91f081/sopel/modules/etymology.py#L43-L44 Before that is the place to add checking for empty argument.
Hey, a newbie here! I want to help here, so I'll start now.
@AlfredOnGitHub It's all yours!

Resolving patch:
diff --git a/sopel/modules/etymology.py b/sopel/modules/etymology.py
--- a/sopel/modules/etymology.py
+++ b/sopel/modules/etymology.py
@@ -40,6 +40,9 @@ def etymology(word):
     # @@ <nsh> sbp, would it be possible to have a flag for .ety to get 2nd/etc
     # entries? - http://swhack.com/logs/2006-07-19#T15-05-29
 
+    if not word:
+        raise ValueError("No word to look for.")
+
     if len(word) > 25:
         raise ValueError("Word too long: %s[…]" % word[:10])
 


Test patch:
Repository: sopel-irc/sopel

Issue:
clock: Defined config section is not used
The `clock.tz` and `clock.time_format` values defined in the `clock` module's config section are not used for formatting its output. It relies on `sopel.tools.time` to find the correct values or fall back on `core.default_timezone`/`core.default_time_format`.

I haven't decided whether to fix the module or just remove its configuration and always use the `core` section's values. But it was the source of a rather long head-scratching session with @SleepingPanda tonight, and I definitely want to document this issue's existence.


Discussion:

Resolving patch:
diff --git a/sopel/modules/clock.py b/sopel/modules/clock.py
--- a/sopel/modules/clock.py
+++ b/sopel/modules/clock.py
@@ -10,49 +10,15 @@
 from __future__ import unicode_literals, absolute_import, print_function, division
 
 from sopel import module, tools
-from sopel.config.types import StaticSection, ValidatedAttribute
 from sopel.tools.time import (
     format_time,
     get_channel_timezone,
     get_nick_timezone,
     get_timezone,
-    validate_format,
     validate_timezone
 )
 
 
-class TimeSection(StaticSection):
-    tz = ValidatedAttribute(
-        'tz',
-        parse=validate_timezone,
-        serialize=validate_timezone,
-        default='UTC')
-    """Default time zone (see https://sopel.chat/tz)"""
-    time_format = ValidatedAttribute(
-        'time_format',
-        parse=validate_format,
-        default='%Y-%m-%d - %T%Z')
-    """Default time format (see http://strftime.net)"""
-
-
-def configure(config):
-    """
-    | name | example | purpose |
-    | ---- | ------- | ------- |
-    | tz | America/Chicago | Preferred time zone (see <https://sopel.chat/tz>); defaults to UTC |
-    | time\\_format | %Y-%m-%d - %T%Z | Preferred time format (see <http://strftime.net>) |
-    """
-    config.define_section('clock', TimeSection)
-    config.clock.configure_setting(
-        'tz', 'Preferred time zone (https://sopel.chat/tz)')
-    config.clock.configure_setting(
-        'time_format', 'Preferred time format (http://strftime.net)')
-
-
-def setup(bot):
-    bot.config.define_section('clock', TimeSection)
-
-
 @module.commands('t', 'time')
 @module.example('.t America/New_York')
 @module.example('.t Exirel')


Test patch:
Repository: sopel-irc/sopel

Issue:
remind: .in does not reply() with errors
Generally (there are likely other exceptions; I haven't exhaustively searched all plugins while on vacation) Sopel uses `bot.reply()` for errors, to get the attention of the user who issued the failed command so they can (hopefully) fix the problem and try again.

The `.in` command doesn't, though. It uses `bot.say()`. We should fix that.


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
@@ -198,10 +198,10 @@ def remind_monitoring(bot):
 def remind_in(bot, trigger):
     """Gives you a reminder in the given amount of time."""
     if not trigger.group(2):
-        bot.say("Missing arguments for reminder command.")
+        bot.reply("Missing arguments for reminder command.")
         return module.NOLIMIT
     if trigger.group(3) and not trigger.group(4):
-        bot.say("No message given for reminder.")
+        bot.reply("No message given for reminder.")
         return module.NOLIMIT
     duration = 0
     message = filter(None, re.split(r'(\d+(?:\.\d+)? ?(?:(?i)' + PERIODS + ')) ?',
@@ -419,10 +419,10 @@ def remind_at(bot, trigger):
     dd-mm-YY, or dd-mm. The separator can be ``.``, ``-``, or ``/``.
     """
     if not trigger.group(2):
-        bot.say("No arguments given for reminder command.")
+        bot.reply("No arguments given for reminder command.")
         return module.NOLIMIT
     if trigger.group(3) and not trigger.group(4):
-        bot.say("No message given for reminder.")
+        bot.reply("No message given for reminder.")
         return module.NOLIMIT
 
     match = REGEX_AT.match(trigger.group(2))


Test patch:
Repository: sopel-irc/sopel

Issue:
Lame/no welcome message at startup
At time of writing, Sopel prints a very basic "Welcome to Sopel. Loading modules..." message at startup. #1597 will modify the startup sequence, eliminate the "Welcome" bit, and print only a message about loading plugins (which is what we're calling "modules" from 7.0 onward).

Sopel should still print some kind of welcome message when it starts, and many CLI utilities print not only their name, but also their version and the version of Python (or whatever runtime, e.g. NodeJS) they're running on. I _love_ it when utilities do this, and I think Sopel should do the same.

This would be an easy thing to implement along with the logging rework that we'll need to address #1337. Or, it's a good "Easyfix" for a first-time contributor.
Lame/no welcome message at startup
At time of writing, Sopel prints a very basic "Welcome to Sopel. Loading modules..." message at startup. #1597 will modify the startup sequence, eliminate the "Welcome" bit, and print only a message about loading plugins (which is what we're calling "modules" from 7.0 onward).

Sopel should still print some kind of welcome message when it starts, and many CLI utilities print not only their name, but also their version and the version of Python (or whatever runtime, e.g. NodeJS) they're running on. I _love_ it when utilities do this, and I think Sopel should do the same.

This would be an easy thing to implement along with the logging rework that we'll need to address #1337. Or, it's a good "Easyfix" for a first-time contributor.


Discussion:
~~Currently blocked on #1678.~~ That's been merged.
~~Currently blocked on #1678.~~ That's been merged.

Resolving patch:
diff --git a/sopel/cli/run.py b/sopel/cli/run.py
--- a/sopel/cli/run.py
+++ b/sopel/cli/run.py
@@ -45,6 +45,9 @@
 def run(settings, pid_file, daemon=False):
     delay = 20
 
+    # Acts as a welcome message, showing the program and platform version at start
+    print_version()
+
     if not settings.core.ca_certs:
         tools.stderr(
             'Could not open CA certificates file. SSL will not work properly!')


Test patch:
Repository: sopel-irc/sopel

Issue:
reddit: suppress submission links on media matches
Mostly I'd like to avoid clients that support expanding inline media showing the same picture/video thumbnail multiple times in a row:

![example of double expanded image link](https://user-images.githubusercontent.com/164140/69605334-700c4080-0fe5-11ea-88f5-862edf83738f.png)

But, there are other reasons, too. This is similar to how e.g. the `wikipedia` plugin will not output a link to the article if it was triggered by an article link. It makes better use of space, and it helps avoid multiple bots triggering each other.


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
@@ -86,7 +86,7 @@ def image_info(bot, trigger, match):
     except IndexError:
         # Fail silently if the image link can't be mapped to a submission
         return NOLIMIT
-    return say_post_info(bot, trigger, oldest.id)
+    return say_post_info(bot, trigger, oldest.id, False)
 
 
 @url(video_url)
@@ -96,7 +96,7 @@ def video_info(bot, trigger, match):
         'https://www.reddit.com/video/{}'.format(match.group(1)),
         timeout=(10.0, 4.0)).headers['Location']
     try:
-        return say_post_info(bot, trigger, re.match(post_url, url).group(1))
+        return say_post_info(bot, trigger, re.match(post_url, url).group(1), False)
     except AttributeError:
         # Fail silently if we can't map the video link to a submission
         return NOLIMIT
@@ -109,7 +109,7 @@ def rpost_info(bot, trigger, match):
     return say_post_info(bot, trigger, match.group(1))
 
 
-def say_post_info(bot, trigger, id_):
+def say_post_info(bot, trigger, id_, show_link=True):
     try:
         s = bot.memory['reddit_praw'].submission(id=id_)
 
@@ -118,7 +118,9 @@ def say_post_info(bot, trigger, id_):
                    'Created at {created}')
 
         subreddit = s.subreddit.display_name
-        if s.is_self:
+        if not show_link:
+            link = 'to r/{}'.format(subreddit)
+        elif s.is_self:
             link = '(self.{})'.format(subreddit)
         else:
             link = '({}) to r/{}'.format(s.url, subreddit)


Test patch:
Repository: sopel-irc/sopel

Issue:
db: get_uri() assumes SQLite
@RustyBower This slipped by me completely in the whole DB-overhaul process. Obviously this function is useless for instances not using SQLite, in its current state. Not that there will be any of those among upgrade instances (at least, not immediately), but…

https://github.com/sopel-irc/sopel/blob/b33173ca5f52905f9ca45c0890e9340779c0c06d/sopel/db.py#L189-L191

Honestly, is this function even still useful? Should we just deprecate it and move on?


Discussion:
I'll take a look :) This probably isn't useful, but while I'm under the hood, I can build some of the API calls for generating sessions and such for plugins

Resolving patch:
diff --git a/sopel/db.py b/sopel/db.py
--- a/sopel/db.py
+++ b/sopel/db.py
@@ -188,7 +188,7 @@ def execute(self, *args, **kwargs):
 
     def get_uri(self):
         """Returns a URL for the database, usable to connect with SQLAlchemy."""
-        return 'sqlite:///{}'.format(self.filename)
+        return self.url
 
     # NICK FUNCTIONS
 


Test patch: