internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: python-discord/bot

Issue:
Bot creates multiple Reddit background tasks
Juanita noticed that the bot posted and pinned the overview of the weekly top posts of the Python subreddit multiple times:

![2019-05-06_22-08-41](https://user-images.githubusercontent.com/33516116/57252083-9f5c5f80-704b-11e9-9509-ce48ad654bc2.png)

The reason is that the `on_ready` event may fire multiple times (for instance, when the bot recovers a dropped connection), creating multiple background tasks:

![2019-05-06_22-05-26](https://user-images.githubusercontent.com/33516116/57252130-bdc25b00-704b-11e9-9c58-1cbc909c60d2.png)

The code causing the bug can be found in this on ready: https://github.com/python-discord/bot/blob/7a61e582ccb866776136d969c97d23907ca160e2/bot/cogs/reddit.py#L279

The fix should be straightforward: Keep a reference to the Background task as a class attribute and check if there's already a Task running like we do at other places as well.

Since this both hits the Discord API and the Reddit API, we should probably just fix this right away and not wait until the feature freeze is over. I'll put in a PR soon.



Discussion:

Resolving patch:
diff --git a/bot/cogs/reddit.py b/bot/cogs/reddit.py
--- a/bot/cogs/reddit.py
+++ b/bot/cogs/reddit.py
@@ -31,6 +31,9 @@ def __init__(self, bot: Bot):
         self.prev_lengths = {}
         self.last_ids = {}
 
+        self.new_posts_task = None
+        self.top_weekly_posts_task = None
+
     async def fetch_posts(self, route: str, *, amount: int = 25, params=None):
         """
         A helper method to fetch a certain amount of Reddit posts at a given route.
@@ -280,8 +283,10 @@ async def on_ready(self):
         self.reddit_channel = self.bot.get_channel(Channels.reddit)
 
         if self.reddit_channel is not None:
-            self.bot.loop.create_task(self.poll_new_posts())
-            self.bot.loop.create_task(self.poll_top_weekly_posts())
+            if self.new_posts_task is None:
+                self.new_posts_task = self.bot.loop.create_task(self.poll_new_posts())
+            if self.top_weekly_posts_task is None:
+                self.top_weekly_posts_task = self.bot.loop.create_task(self.poll_top_weekly_posts())
         else:
             log.warning("Couldn't locate a channel for subreddit relaying.")
 


Test patch:
Repository: python-discord/bot

Issue:
make !otn accept uppercase and punctuation
There are weird unicode alternatives for stuff like capital letters and punctuation that will get through the discord "only lowercase and dashes" filters.

When someone uses `!otn a` and tries to add a name with an apostrophe, we currently are transforming those into something that the system will accept. Let's do the same for exclamation points, question marks, and uppercase letters!

* For exclamation points, use ǃ
* For questionmarks, use ？
* For uppercase, use 𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹


Discussion:

Resolving patch:
diff --git a/bot/cogs/off_topic_names.py b/bot/cogs/off_topic_names.py
--- a/bot/cogs/off_topic_names.py
+++ b/bot/cogs/off_topic_names.py
@@ -19,7 +19,7 @@ class OffTopicName(Converter):
 
     @staticmethod
     async def convert(ctx: Context, argument: str):
-        allowed_characters = ("-", "’", "'", "`")
+        allowed_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!?'`"
 
         if not (2 <= len(argument) <= 96):
             raise BadArgument("Channel name must be between 2 and 96 chars long")
@@ -30,11 +30,11 @@ async def convert(ctx: Context, argument: str):
                 "alphanumeric characters, minus signs or apostrophes."
             )
 
-        elif not argument.islower():
-            raise BadArgument("Channel name must be lowercase")
-
-        # Replace some unusable apostrophe-like characters with "’".
-        return argument.replace("'", "’").replace("`", "’")
+        # Replace invalid characters with unicode alternatives.
+        table = str.maketrans(
+            allowed_characters, '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹ǃ？’’'
+        )
+        return argument.translate(table)
 
 
 async def update_names(bot: Bot, headers: dict):


Test patch:
Repository: python-discord/bot

Issue:
Implement a search command for !otn
With hundreds of off-topic names in our list, looking for one by clicking through the paginator with the bot is tedious.

Let's have a `!otn search <name>` command!

#### Implementation Ideas
- Use the text search functionality in postgres 
- Fuzzy search (`fuzzystrmatch` maybe?)
- Ranked list based on similarity to query


Discussion:
Love this idea.

Resolving patch:
diff --git a/bot/cogs/off_topic_names.py b/bot/cogs/off_topic_names.py
--- a/bot/cogs/off_topic_names.py
+++ b/bot/cogs/off_topic_names.py
@@ -1,4 +1,5 @@
 import asyncio
+import difflib
 import logging
 from datetime import datetime, timedelta
 
@@ -141,6 +142,27 @@ async def list_command(self, ctx):
             embed.description = "Hmmm, seems like there's nothing here yet."
             await ctx.send(embed=embed)
 
+    @otname_group.command(name='search', aliases=('s',))
+    @with_role(*MODERATION_ROLES)
+    async def search_command(self, ctx, *, query: str):
+        """
+        Search for an off-topic name.
+        """
+
+        result = await self.bot.api_client.get('bot/off-topic-channel-names')
+        matches = difflib.get_close_matches(query, result, n=10, cutoff=0.35)
+        lines = sorted(f"• {name}" for name in matches)
+        embed = Embed(
+            title=f"Query results",
+            colour=Colour.blue()
+        )
+
+        if matches:
+            await LinePaginator.paginate(lines, ctx, embed, max_size=400, empty=False)
+        else:
+            embed.description = "Nothing found."
+            await ctx.send(embed=embed)
+
 
 def setup(bot: Bot):
     bot.add_cog(OffTopicNames(bot))


Test patch:
Repository: python-discord/bot

Issue:
!reminders list returning all reminders in DB
The reminders cog is currently returning a list of all reminders in the DB rather than those specific to the user.

For example, if I have no reminders created I still get a list of reminders:

![image](https://user-images.githubusercontent.com/5323929/65425255-90e4c980-dddb-11e9-813f-4638c8c9f7e9.png)



Discussion:

Resolving patch:
diff --git a/bot/cogs/reminders.py b/bot/cogs/reminders.py
--- a/bot/cogs/reminders.py
+++ b/bot/cogs/reminders.py
@@ -146,7 +146,7 @@ async def new_reminder(self, ctx: Context, expiration: Duration, *, content: str
             active_reminders = await self.bot.api_client.get(
                 'bot/reminders',
                 params={
-                    'user__id': str(ctx.author.id)
+                    'author__id': str(ctx.author.id)
                 }
             )
 
@@ -184,7 +184,7 @@ async def list_reminders(self, ctx: Context) -> Optional[Message]:
         # Get all the user's reminders from the database.
         data = await self.bot.api_client.get(
             'bot/reminders',
-            params={'user__id': str(ctx.author.id)}
+            params={'author__id': str(ctx.author.id)}
         )
 
         now = datetime.utcnow()


Test patch:
Repository: python-discord/bot

Issue:
The off-topic channel name updating task fails on non-success API response
The background task we create to update off-topic names at midnight UTC fails if it receives a non-success API response. The reason is that our `bot.api_client` will raise the `bot.api.ResponseCodeError` exception on non-success response status codes. This means that the off-topic channel names won't be updated again until either the bot is restarted or the task is started manually again by an admin.

The relevant lines of code:
https://github.com/python-discord/bot/blob/e70c96248bd7b548412811a4f1ffe88bed41f815/bot/cogs/off_topic_names.py#L59-L61

To handle it, we could simply include a `try-except` block and log the exception in the `except` block. I'm not sure if we want to log the entire exception, since the exception text could be a [massive HTML-response generated by cloudflare](https://paste.pythondiscord.com/ohibicedif). Logging the failure with the response code should generally give us enough to determine the cause of the failure.


Discussion:
working on it :)
@Akarys42 Can you make sure you're working from an up-to-date fork? I noticed that your fork is quite some commits behind `master`. That will make sure we don't run into a lot of merge conflicts and other problems.

Resolving patch:
diff --git a/bot/cogs/off_topic_names.py b/bot/cogs/off_topic_names.py
--- a/bot/cogs/off_topic_names.py
+++ b/bot/cogs/off_topic_names.py
@@ -6,6 +6,7 @@
 from discord import Colour, Embed
 from discord.ext.commands import BadArgument, Bot, Cog, Context, Converter, group
 
+from bot.api import ResponseCodeError
 from bot.constants import Channels, MODERATION_ROLES
 from bot.decorators import with_role
 from bot.pagination import LinePaginator
@@ -49,9 +50,13 @@ async def update_names(bot: Bot) -> None:
         seconds_to_sleep = (next_midnight - datetime.utcnow()).seconds + 1
         await asyncio.sleep(seconds_to_sleep)
 
-        channel_0_name, channel_1_name, channel_2_name = await bot.api_client.get(
-            'bot/off-topic-channel-names', params={'random_items': 3}
-        )
+        try:
+            channel_0_name, channel_1_name, channel_2_name = await bot.api_client.get(
+                'bot/off-topic-channel-names', params={'random_items': 3}
+            )
+        except ResponseCodeError as e:
+            log.error(f"Failed to get new off topic channel names: code {e.response.status}")
+            continue
         channel_0, channel_1, channel_2 = (bot.get_channel(channel_id) for channel_id in CHANNELS)
 
         await channel_0.edit(name=f'ot0-{channel_0_name}')


Test patch:
Repository: python-discord/bot

Issue:
Help command raises AttributeError when argument is a cog name
For example, `!help Moderation` returns

```
AttributeError: 'Command' object has no attribute 'instance'
```

Have not looked into the cause of this yet.


Discussion:

Resolving patch:
diff --git a/bot/cogs/help.py b/bot/cogs/help.py
--- a/bot/cogs/help.py
+++ b/bot/cogs/help.py
@@ -110,9 +110,9 @@ def _get_query(self, query: str) -> Union[Command, Cog]:
         cog = self._bot.cogs.get(query)
         if cog:
             return Cog(
-                name=cog.__class__.__name__,
+                name=cog.qualified_name,
                 description=inspect.getdoc(cog),
-                commands=[c for c in self._bot.commands if c.instance is cog]
+                commands=[c for c in self._bot.commands if c.cog is cog]
             )
 
         self._handle_not_found(query)
diff --git a/bot/cogs/watchchannels/watchchannel.py b/bot/cogs/watchchannels/watchchannel.py
--- a/bot/cogs/watchchannels/watchchannel.py
+++ b/bot/cogs/watchchannels/watchchannel.py
@@ -148,8 +148,8 @@ async def start_watchchannel(self) -> None:
                 title=f"Warning: Failed to retrieve user cache for the {self.__class__.__name__} watch channel",
                 text="Could not retrieve the list of watched users from the API and messages will not be relayed.",
                 ping_everyone=True,
-                icon=Icons.token_removed,
-                color=Color.red()
+                icon_url=Icons.token_removed,
+                colour=Color.red()
             )
 
     async def fetch_user_cache(self) -> bool:


Test patch:
Repository: python-discord/bot

Issue:
Tags can't be edited due to using the POST method with the API
Currently, the `!tag edit` subcommand is just an alias of `!tag set`. This means that if we try to edit an existing tag, the bot will use the POST http method to communicate with the API. Since we're not posting a new tag, but editing an existing entry, the API will reject this request. 

Instead of using POST, we should be using PATCH, since we're only partially updating the entry in the database.


Discussion:

Resolving patch:
diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py
--- a/bot/cogs/tags.py
+++ b/bot/cogs/tags.py
@@ -86,7 +86,7 @@ def _command_on_cooldown(tag_name: str) -> bool:
                     max_lines=15
                 )
 
-    @tags_group.command(name='set', aliases=('add', 'edit', 's'))
+    @tags_group.command(name='set', aliases=('add', 's'))
     @with_role(*MODERATION_ROLES)
     async def set_command(
         self,
@@ -95,7 +95,7 @@ async def set_command(
         *,
         tag_content: TagContentConverter,
     ) -> None:
-        """Create a new tag or update an existing one."""
+        """Create a new tag."""
         body = {
             'title': tag_name.lower().strip(),
             'embed': {
@@ -116,6 +116,35 @@ async def set_command(
             colour=Colour.blurple()
         ))
 
+    @tags_group.command(name='edit', aliases=('e', ))
+    @with_role(*MODERATION_ROLES)
+    async def edit_command(
+        self,
+        ctx: Context,
+        tag_name: TagNameConverter,
+        *,
+        tag_content: TagContentConverter,
+    ) -> None:
+        """Edit an existing tag."""
+        body = {
+            'embed': {
+                'title': tag_name,
+                'description': tag_content
+            }
+        }
+
+        await self.bot.api_client.patch(f'bot/tags/{tag_name}', json=body)
+
+        log.debug(f"{ctx.author} successfully edited the following tag in our database: \n"
+                  f"tag_name: {tag_name}\n"
+                  f"tag_content: '{tag_content}'\n")
+
+        await ctx.send(embed=Embed(
+            title="Tag successfully edited",
+            description=f"**{tag_name}** edited in the database.",
+            colour=Colour.blurple()
+        ))
+
     @tags_group.command(name='delete', aliases=('remove', 'rm', 'd'))
     @with_role(Roles.admin, Roles.owner)
     async def delete_command(self, ctx: Context, *, tag_name: TagNameConverter) -> None:


Test patch:
Repository: python-discord/bot

Issue:
Aliasing for !resources command.
Currently the !resources command links to both the [PyDis resource](https://pythondiscord.com/pages/resources/
) page, and the [PyDis tools](https://pythondiscord.com/pages/tools/) page.

I feel that the alias of !tools should be added to the command, or the links should be separated into two commands, to both make it easier for users to access the tools page, but also increase awareness of the page.


Discussion:
I think I would prefer for us to separate them. I'm claiming this and I'll solve it tonight.

Resolving patch:
diff --git a/bot/cogs/alias.py b/bot/cogs/alias.py
--- a/bot/cogs/alias.py
+++ b/bot/cogs/alias.py
@@ -53,6 +53,11 @@ async def site_resources_alias(self, ctx: Context) -> None:
         """Alias for invoking <prefix>site resources."""
         await self.invoke(ctx, "site resources")
 
+    @command(name="tools", hidden=True)
+    async def site_tools_alias(self, ctx: Context) -> None:
+        """Alias for invoking <prefix>site tools."""
+        await self.invoke(ctx, "site tools")
+
     @command(name="watch", hidden=True)
     async def bigbrother_watch_alias(self, ctx: Context, user: Union[Member, User, proxy_user], *, reason: str) -> None:
         """Alias for invoking <prefix>bigbrother watch [user] [reason]."""
diff --git a/bot/cogs/site.py b/bot/cogs/site.py
--- a/bot/cogs/site.py
+++ b/bot/cogs/site.py
@@ -44,17 +44,29 @@ async def site_main(self, ctx: Context) -> None:
     async def site_resources(self, ctx: Context) -> None:
         """Info about the site's Resources page."""
         learning_url = f"{PAGES_URL}/resources"
-        tools_url = f"{PAGES_URL}/tools"
 
-        embed = Embed(title="Resources & Tools")
-        embed.set_footer(text=f"{learning_url} | {tools_url}")
+        embed = Embed(title="Resources")
+        embed.set_footer(text=f"{learning_url}")
         embed.colour = Colour.blurple()
         embed.description = (
             f"The [Resources page]({learning_url}) on our website contains a "
-            "list of hand-selected goodies that we regularly recommend "
-            f"to both beginners and experts. The [Tools page]({tools_url}) "
-            "contains a couple of the most popular tools for programming in "
-            "Python."
+            "list of hand-selected learning resources that we regularly recommend "
+            f"to both beginners and experts."
+        )
+
+        await ctx.send(embed=embed)
+
+    @site_group.command(name="tools")
+    async def site_tools(self, ctx: Context) -> None:
+        """Info about the site's Tools page."""
+        tools_url = f"{PAGES_URL}/tools"
+
+        embed = Embed(title="Tools")
+        embed.set_footer(text=f"{tools_url}")
+        embed.colour = Colour.blurple()
+        embed.description = (
+            f"The [Tools page]({tools_url}) on our website contains a "
+            f"couple of the most popular tools for programming in Python."
         )
 
         await ctx.send(embed=embed)


Test patch:
Repository: python-discord/bot

Issue:
The `unload` method fails for watchchannels if the consume task hasn't started yet
The `unload_cog` method of the `WatchChannel` ABC has a small bug in it. If you try to reload or unload the cog while the message consumption task has not yet started, it will fail because the `bot._consume_task` attribute will still be assigned to `None`:

https://github.com/python-discord/bot/blob/cff9cabfee014e6207e156ee69e8b326ece55800/bot/cogs/watchchannels/watchchannel.py#L335-L339

This will lead to the following exception, which also prevents us from reloading the extensions that have cogs that derive from this ABC completely:

```py
Traceback (most recent call last):
  File "/home/sebastiaan/pydis/repositories/djangobot/bot/cogs/cogs.py", line 232, in reload_command
    self.bot.unload_extension(full_cog)
  File "/home/sebastiaan/.local/share/virtualenvs/djangobot-Py5o4YeS/lib/python3.7/site-packages/discord/ext/commands/bot.py", line 655, in unload_extension
    self._remove_module_references(lib.__name__)
  File "/home/sebastiaan/.local/share/virtualenvs/djangobot-Py5o4YeS/lib/python3.7/site-packages/discord/ext/commands/bot.py", line 534, in _remove_module_references
    self.remove_cog(cogname)
  File "/home/sebastiaan/.local/share/virtualenvs/djangobot-Py5o4YeS/lib/python3.7/site-packages/discord/ext/commands/bot.py", line 520, in remove_cog
    cog._eject(self)
  File "/home/sebastiaan/.local/share/virtualenvs/djangobot-Py5o4YeS/lib/python3.7/site-packages/discord/ext/commands/cog.py", line 414, in _eject
    self.cog_unload()
  File "/home/sebastiaan/pydis/repositories/djangobot/bot/cogs/watchchannels/watchchannel.py", line 338, in cog_unload
    if not self._consume_task.done():
AttributeError: 'NoneType' object has no attribute 'done'
```

So, we need to make sure that a `Task` was assigned in the first place, before trying to access the `done` method of it.


Discussion:

Resolving patch:
diff --git a/bot/cogs/watchchannels/watchchannel.py b/bot/cogs/watchchannels/watchchannel.py
--- a/bot/cogs/watchchannels/watchchannel.py
+++ b/bot/cogs/watchchannels/watchchannel.py
@@ -335,7 +335,7 @@ def _remove_user(self, user_id: int) -> None:
     def cog_unload(self) -> None:
         """Takes care of unloading the cog and canceling the consumption task."""
         self.log.trace(f"Unloading the cog")
-        if not self._consume_task.done():
+        if self._consume_task and not self._consume_task.done():
             self._consume_task.cancel()
             try:
                 self._consume_task.result()


Test patch:
Repository: python-discord/bot

Issue:
Unhandled Docs exception
There is some missing exception handling in the parsing logic of the `!docs get` command.

Here is one example, exact cause is still TBD:

![image](https://user-images.githubusercontent.com/5323929/65977896-bbb8c880-e440-11e9-9ffe-909bf2d0e946.png)




Discussion:
Looking into this

Resolving patch:
diff --git a/bot/cogs/doc.py b/bot/cogs/doc.py
--- a/bot/cogs/doc.py
+++ b/bot/cogs/doc.py
@@ -207,6 +207,9 @@ async def get_symbol_html(self, symbol: str) -> Optional[Tuple[str, str]]:
         symbol_heading = soup.find(id=symbol_id)
         signature_buffer = []
 
+        if symbol_heading is None:
+            return None
+
         # Traverse the tags of the signature header and ignore any
         # unwanted symbols from it. Add all of it to a temporary buffer.
         for tag in symbol_heading.strings:


Test patch:
Repository: python-discord/bot

Issue:
Nickname Changes Not Consistently Logged to #user-log
We noticed this morning that not all user update events are being logged to the #user-log channel for a currently unknown reason.

@SebastiaanZ indicated that he was going to take a look and see if some of the recent changes affected the logging logic.


Discussion:
I've managed to trace the problem to users setting a nickname if they had none or resetting their nickname to their username (i.e., removing their server-specific nickname). What happens is that when people remove or add a nickname, the `old_value` or `new_value` is `None`.

For instance, resetting a nickname results in the following data:
```py
'nick': {
    'old_type': <class 'str'>,
    'new_type': <class 'NoneType'>,
    'old_value': 'Nickname',
    'new_value': None
}
```

Since we only record a change if both the old value and new value are "truthy", we do not record a change when this happens:

```py
                new = value.get("new_value")
                old = value.get("old_value")

                if new and old:
                    changes.append(f"**{key.title()}:** `{old}` **->** `{new}`")
```

Effectively, it means that only changes to a username or changes to a nickname show up, not changes that set or remove a nickname.

I think that instead of recording nickname changes, it would be better to record changes in the `display_name` of the member, since that is ultimately what's relevant to us. In addition, we could continue to track username changes as well, since it's valuable to track those regardless of whether the username is also the current `display_name`.

Resolving patch:
diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py
--- a/bot/cogs/moderation/modlog.py
+++ b/bot/cogs/moderation/modlog.py
@@ -20,7 +20,7 @@
 
 CHANNEL_CHANGES_UNSUPPORTED = ("permissions",)
 CHANNEL_CHANGES_SUPPRESSED = ("_overwrites", "position")
-MEMBER_CHANGES_SUPPRESSED = ("status", "activities", "_client_status")
+MEMBER_CHANGES_SUPPRESSED = ("status", "activities", "_client_status", "nick")
 ROLE_CHANGES_UNSUPPORTED = ("colour", "permissions")
 
 
@@ -498,6 +498,11 @@ async def on_member_update(self, before: discord.Member, after: discord.Member)
                 f"**Discriminator:** `{before.discriminator}` **->** `{after.discriminator}`"
             )
 
+        if before.display_name != after.display_name:
+            changes.append(
+                f"**Display name:** `{before.display_name}` **->** `{after.display_name}`"
+            )
+
         if not changes:
             return
 


Test patch: