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

Issue:
Verify command shouldn't log tracebacks for DM failure.
Sentry Issue: [BOT-11](https://sentry.io/organizations/python-discord/issues/1526042507/?referrer=github_integration)

It currently uses `log.exception()` for all exceptions. `Forbidden` should be a special case and use a lower log level. A better approach may be to only handle `Forbidden` and re-raise all other exceptions (to defer it to the default command error handler). A `finally` block can be used to still delete the message afterwards.

```py
        try:
            await ctx.author.send(WELCOME_MESSAGE)
        except Exception:
            # Catch the exception, in case they have DMs off or something
            log.exception(f"Unable to send welcome message to user {ctx.author}.")
```

```
Forbidden: 403 FORBIDDEN (error code: 50007): Cannot send messages to this user
  File "bot/cogs/verification.py", line 129, in accept_command
    await ctx.author.send(WELCOME_MESSAGE)
  File "discord/abc.py", line 856, in send
    data = await state.http.send_message(channel.id, content, tts=tts, embed=embed, nonce=nonce)
  File "discord/http.py", line 216, in request
    raise Forbidden(r, data)

Unable to send welcome message to user <REDACTED>.
```


Discussion:

Resolving patch:
diff --git a/bot/cogs/verification.py b/bot/cogs/verification.py
--- a/bot/cogs/verification.py
+++ b/bot/cogs/verification.py
@@ -1,7 +1,8 @@
 import logging
+from contextlib import suppress
 from datetime import datetime
 
-from discord import Colour, Message, NotFound, Object
+from discord import Colour, Forbidden, Message, NotFound, Object
 from discord.ext import tasks
 from discord.ext.commands import Cog, Context, command
 
@@ -127,17 +128,13 @@ async def accept_command(self, ctx: Context, *_) -> None:  # We don't actually c
         await ctx.author.add_roles(Object(Roles.verified), reason="Accepted the rules")
         try:
             await ctx.author.send(WELCOME_MESSAGE)
-        except Exception:
-            # Catch the exception, in case they have DMs off or something
-            log.exception(f"Unable to send welcome message to user {ctx.author}.")
-
-        log.trace(f"Deleting the message posted by {ctx.author}.")
-
-        try:
-            self.mod_log.ignore(Event.message_delete, ctx.message.id)
-            await ctx.message.delete()
-        except NotFound:
-            log.trace("No message found, it must have been deleted by another bot.")
+        except Forbidden:
+            log.info(f"Sending welcome message failed for {ctx.author}.")
+        finally:
+            log.trace(f"Deleting accept message by {ctx.author}.")
+            with suppress(NotFound):
+                self.mod_log.ignore(Event.message_delete, ctx.message.id)
+                await ctx.message.delete()
 
     @command(name='subscribe')
     @in_channel(Channels.bot)


Test patch:
Repository: python-discord/bot

Issue:
Reduce logging level for tag cooldowns
Sentry Issue: [BOT-19](https://sentry.io/organizations/python-discord/issues/1527377135/?referrer=github_integration)

```
<REDACTED> tried to get the 'ask' tag, but the tag is on cooldown. Cooldown ends in 2.8 seconds.
```


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
@@ -116,8 +116,10 @@ def _command_on_cooldown(tag_name: str) -> bool:
 
         if _command_on_cooldown(tag_name):
             time_left = Cooldowns.tags - (time.time() - self.tag_cooldowns[tag_name]["time"])
-            log.warning(f"{ctx.author} tried to get the '{tag_name}' tag, but the tag is on cooldown. "
-                        f"Cooldown ends in {time_left:.1f} seconds.")
+            log.info(
+                f"{ctx.author} tried to get the '{tag_name}' tag, but the tag is on cooldown. "
+                f"Cooldown ends in {time_left:.1f} seconds."
+            )
             return
 
         await self._get_tags()


Test patch:
Repository: python-discord/bot

Issue:
Filters should not apply to staff members
Currently, we have certain channels whitelisted for certain filters and some filters apply to all members, even Owners cannot post filetypes that aren't on the whitelist!

Please change this so that absolutely all filters will ignore all staff members. It is not necessary to whitelist staff channels or to keep any other kind of exceptions to the filters once this very simple exception has been added, so please clean up any such exceptions while you're at it.


Discussion:

Resolving patch:
diff --git a/bot/cogs/antimalware.py b/bot/cogs/antimalware.py
--- a/bot/cogs/antimalware.py
+++ b/bot/cogs/antimalware.py
@@ -4,7 +4,7 @@
 from discord.ext.commands import Cog
 
 from bot.bot import Bot
-from bot.constants import AntiMalware as AntiMalwareConfig, Channels, URLs
+from bot.constants import AntiMalware as AntiMalwareConfig, Channels, STAFF_ROLES, URLs
 
 log = logging.getLogger(__name__)
 
@@ -18,7 +18,13 @@ def __init__(self, bot: Bot):
     @Cog.listener()
     async def on_message(self, message: Message) -> None:
         """Identify messages with prohibited attachments."""
-        if not message.attachments:
+        # Return when message don't have attachment and don't moderate DMs
+        if not message.attachments or not message.guild:
+            return
+
+        # Check if user is staff, if is, return
+        # Since we only care that roles exist to iterate over, check for the attr rather than a User/Member instance
+        if hasattr(message.author, "roles") and any(role.id in STAFF_ROLES for role in message.author.roles):
             return
 
         embed = Embed()


Test patch:
Repository: python-discord/bot

Issue:
Formatting issue for empty topic in channel update log
The old and new topics should each be in separate in-line code blocks. The arrow should be between and outside the two code blocks.

![bild](https://user-images.githubusercontent.com/1515135/75304983-a2525780-57f9-11ea-8056-eb066e1dfa2b.png)



Discussion:

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
@@ -215,7 +215,10 @@ async def on_guild_channel_update(self, before: GUILD_CHANNEL, after: GuildChann
                 new = value["new_value"]
                 old = value["old_value"]
 
-                changes.append(f"**{key.title()}:** `{old}` **→** `{new}`")
+                # Discord does not treat consecutive backticks ("``") as an empty inline code block, so the markdown
+                # formatting is broken when `new` and/or `old` are empty values. "None" is used for these cases so
+                # formatting is preserved.
+                changes.append(f"**{key.title()}:** `{old or 'None'}` **→** `{new or 'None'}`")
 
             done.append(key)
 


Test patch:
Repository: python-discord/bot

Issue:
!zen command not working as intended
if you do
```
!zen explicit
```
it returns 
```
**The Zen of Python (line 10):**
Unless explicitly silenced.
```

while I do believe that the intent was to have it return line two
```
**The Zen of Python (line 1):**
Explicit is better than implicit.
```



Discussion:
I think the issue could be solved by simply checking if the exact query word is inside one of the zen lines (`explicit` in that case) and returning it, if it is not found, try the difflib matching. 
Which is what it is doing:
https://github.com/python-discord/bot/blob/190ff0fd2422459674320fa0e096921e38872f2d/bot/cogs/utils.py#L234
Even if we check for word in string, `explicit` will still be found in two lines and not one, so perhaps we need a better way to handle this case
The current matching strategy is a bit simplistic in more than one way, but the specific case eivl mentions is more difficult to tackle. We could prioritize matches that contain the word as-is (except for capitalization), which would solve the "explicit" versus "explicitly" problem when looking explicitly for "explicit".

The next bit is that the current fuzzy matching tactic will always return a match; there's no lower bound/threshold below which it simply doesn't return anything. I don't really mind that, but we could add such a lower bound to prevent things like random slurs from returning a line anyway.

Resolving patch:
diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py
--- a/bot/cogs/utils.py
+++ b/bot/cogs/utils.py
@@ -236,7 +236,17 @@ async def zen(self, ctx: Context, *, search_value: Union[int, str, None] = None)
             await ctx.send(embed=embed)
             return
 
-        # handle if it's a search string
+        # Try to handle first exact word due difflib.SequenceMatched may use some other similar word instead
+        # exact word.
+        for i, line in enumerate(zen_lines):
+            for word in line.split():
+                if word.lower() == search_value.lower():
+                    embed.title += f" (line {i}):"
+                    embed.description = line
+                    await ctx.send(embed=embed)
+                    return
+
+        # handle if it's a search string and not exact word
         matcher = difflib.SequenceMatcher(None, search_value.lower())
 
         best_match = ""


Test patch:
Repository: python-discord/bot

Issue:
Pep command cannot fetch PEP 0.
The [PEP 0](https://www.python.org/dev/peps/) is an index of all the other PEPs;
instead of being in the github repo directly, it's generated automatically through a script present there.
~~If implemented, scraping the site for it seems to be the better option as generating it would require the whole up to date repo.~~
The header is a constant in the pep0 package and can be gotten from there in case of future changes 
 or as suggested in the comment below https://github.com/python/peps/blob/master/pep0/constants.py#L10-L20


Discussion:
Or looking at the current output of pep, it could be hardcoded as it'll always be active and the other things are static

Resolving patch:
diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py
--- a/bot/cogs/utils.py
+++ b/bot/cogs/utils.py
@@ -40,6 +40,8 @@
 Namespaces are one honking great idea -- let's do more of those!
 """
 
+ICON_URL = "https://www.python.org/static/opengraph-icon-200x200.png"
+
 
 class Utils(Cog):
     """A selection of utilities which don't have a clear category."""
@@ -59,6 +61,10 @@ async def pep_command(self, ctx: Context, pep_number: str) -> None:
             await ctx.invoke(self.bot.get_command("help"), "pep")
             return
 
+        # Handle PEP 0 directly because it's not in .rst or .txt so it can't be accessed like other PEPs.
+        if pep_number == 0:
+            return await self.send_pep_zero(ctx)
+
         possible_extensions = ['.txt', '.rst']
         found_pep = False
         for extension in possible_extensions:
@@ -82,7 +88,7 @@ async def pep_command(self, ctx: Context, pep_number: str) -> None:
                     description=f"[Link]({self.base_pep_url}{pep_number:04})",
                 )
 
-                pep_embed.set_thumbnail(url="https://www.python.org/static/opengraph-icon-200x200.png")
+                pep_embed.set_thumbnail(url=ICON_URL)
 
                 # Add the interesting information
                 fields_to_check = ("Status", "Python-Version", "Created", "Type")
@@ -278,6 +284,19 @@ async def vote(self, ctx: Context, title: str, *options: str) -> None:
         for reaction in options:
             await message.add_reaction(reaction)
 
+    async def send_pep_zero(self, ctx: Context) -> None:
+        """Send information about PEP 0."""
+        pep_embed = Embed(
+            title=f"**PEP 0 - Index of Python Enhancement Proposals (PEPs)**",
+            description=f"[Link](https://www.python.org/dev/peps/)"
+        )
+        pep_embed.set_thumbnail(url=ICON_URL)
+        pep_embed.add_field(name="Status", value="Active")
+        pep_embed.add_field(name="Created", value="13-Jul-2000")
+        pep_embed.add_field(name="Type", value="Informational")
+
+        await ctx.send(embed=pep_embed)
+
 
 def setup(bot: Bot) -> None:
     """Load the Utils cog."""


Test patch:
Repository: python-discord/bot

Issue:
Disallow editing duration of expired infractions
Currently the command assumes that an infraction is still active if a duration is being edited. It tries to cancel the previous infraction but it will fail with a warning if the infraction already expired. Relevant code can be found here:
https://github.com/python-discord/bot/blob/582ddbb1ca8bab2cb883781911f5f35962330995/bot/cogs/moderation/management.py#L130-L142


Discussion:
Sentry issue: [BOT-2G](https://sentry.io/organizations/python-discord/issues/1586219048/?referrer=github_integration)

Resolving patch:
diff --git a/bot/cogs/moderation/management.py b/bot/cogs/moderation/management.py
--- a/bot/cogs/moderation/management.py
+++ b/bot/cogs/moderation/management.py
@@ -100,7 +100,12 @@ async def infraction_edit(
         confirm_messages = []
         log_text = ""
 
-        if isinstance(duration, str):
+        if duration is not None and not old_infraction['active']:
+            if reason is None:
+                await ctx.send(":x: Cannot edit the expiration of an expired infraction.")
+                return
+            confirm_messages.append("expiry unchanged (infraction already expired)")
+        elif isinstance(duration, str):
             request_data['expires_at'] = None
             confirm_messages.append("marked as permanent")
         elif duration is not None:


Test patch:
Repository: python-discord/bot

Issue:
Add ability to remove bot tag responses.
With the new fuzzy tags, there's a chance an user may think something which is not a tag is one; resulting in a similar tag being fetched 
(`!t def` as an example resulting in a tag through the whole page)

Adding the ability to remove them (perhaps when it was fuzzy matched) would solve this and perhaps some others when a tag's purpose is misremembered.


Discussion:
What's the proposed mechanism to accomplish this?
I was thinking about deleting it on detecting the trash can reaction, like eval's output for example.
I'm fine with that approach. Simple and familiar. Let's just always do it rather than only when using fuzzy matching.
I'll work on it.

Resolving patch:
diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py
--- a/bot/cogs/tags.py
+++ b/bot/cogs/tags.py
@@ -11,6 +11,7 @@
 from bot.bot import Bot
 from bot.converters import TagNameConverter
 from bot.pagination import LinePaginator
+from bot.utils.messages import wait_for_deletion
 
 log = logging.getLogger(__name__)
 
@@ -167,6 +168,7 @@ async def search_tag_content_any_keyword(self, ctx: Context, *, keywords: Option
     @tags_group.command(name='get', aliases=('show', 'g'))
     async def get_command(self, ctx: Context, *, tag_name: TagNameConverter = None) -> None:
         """Get a specified tag, or a list of all tags if no tag is specified."""
+
         def _command_on_cooldown(tag_name: str) -> bool:
             """
             Check if the command is currently on cooldown, on a per-tag, per-channel basis.
@@ -205,12 +207,22 @@ def _command_on_cooldown(tag_name: str) -> bool:
                         "time": time.time(),
                         "channel": ctx.channel.id
                     }
-                await ctx.send(embed=Embed.from_dict(tag['embed']))
+                await wait_for_deletion(
+                    await ctx.send(embed=Embed.from_dict(tag['embed'])),
+                    [ctx.author.id],
+                    client=self.bot
+                )
             elif founds and len(tag_name) >= 3:
-                await ctx.send(embed=Embed(
-                    title='Did you mean ...',
-                    description='\n'.join(tag['title'] for tag in founds[:10])
-                ))
+                await wait_for_deletion(
+                    await ctx.send(
+                        embed=Embed(
+                            title='Did you mean ...',
+                            description='\n'.join(tag['title'] for tag in founds[:10])
+                        )
+                    ),
+                    [ctx.author.id],
+                    client=self.bot
+                )
 
         else:
             tags = self._cache.values()


Test patch:
Repository: python-discord/bot

Issue:
Syncer not catching asyncio.TimeoutError
It's catching the other `TimeoutError`. Simple fix is to change the exception being caught.

https://github.com/python-discord/bot/blob/7571cabe65e39d231523e713923cd23b927225bc/bot/cogs/sync/syncers.py#L125-L127


Discussion:
Sentry issue: [BOT-2V](https://sentry.io/organizations/python-discord/issues/1598581490/?referrer=github_integration)
Sentry issue: [BOT-2T](https://sentry.io/organizations/python-discord/issues/1598496588/?referrer=github_integration)

Resolving patch:
diff --git a/bot/cogs/sync/syncers.py b/bot/cogs/sync/syncers.py
--- a/bot/cogs/sync/syncers.py
+++ b/bot/cogs/sync/syncers.py
@@ -1,4 +1,5 @@
 import abc
+import asyncio
 import logging
 import typing as t
 from collections import namedtuple
@@ -122,7 +123,7 @@ async def _wait_for_confirmation(self, author: Member, message: Message) -> bool
                 check=partial(self._reaction_check, author, message),
                 timeout=constants.Sync.confirm_timeout
             )
-        except TimeoutError:
+        except asyncio.TimeoutError:
             # reaction will remain none thus sync will be aborted in the finally block below.
             log.debug(f"The {self.name} syncer confirmation prompt timed out.")
 


Test patch:
diff --git a/tests/bot/cogs/sync/test_base.py b/tests/bot/cogs/sync/test_base.py
--- a/tests/bot/cogs/sync/test_base.py
+++ b/tests/bot/cogs/sync/test_base.py
@@ -1,3 +1,4 @@
+import asyncio
 import unittest
 from unittest import mock
 
@@ -211,7 +212,7 @@ async def test_wait_for_confirmation(self):
         subtests = (
             (constants.Emojis.check_mark, True, None),
             ("InVaLiD", False, None),
-            (None, False, TimeoutError),
+            (None, False, asyncio.TimeoutError),
         )
 
         for emoji, ret_val, side_effect in subtests:
Repository: python-discord/bot

Issue:
Tag cog crashes bot on systems with non utf8 default encoding.
The tag cog leaves out encoding when opening files, assuming the default is UTF8  but that is not the case on some OSs and fails with the `UnicodeDecodeError`.

The offending block of code can be found here:
https://github.com/python-discord/bot/blob/7571cabe65e39d231523e713923cd23b927225bc/bot/cogs/tags.py#L43-L48

paging @kmonteith25 here as they mentioned the issue in #dev-contrib


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
@@ -43,7 +43,7 @@ def get_tags() -> dict:
             tag = {
                 "title": tag_title,
                 "embed": {
-                    "description": file.read_text()
+                    "description": file.read_text(encoding="utf-8")
                 }
             }
             cache[tag_title] = tag


Test patch:
Repository: python-discord/bot

Issue:
Check channel type before trying to access category
The fix is the same as https://github.com/python-discord/bot/commit/1509d3b1c39fac825d5e5a09fd0caf780db7c91c. I just overlooked applying it to `help_channels.py` too.

```
Ignoring exception in on_message
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/discord/client.py", line 312, in _run_event
    await coro(*args, **kwargs)
  File "/bot/bot/cogs/help_channels.py", line 516, in on_message
    if channel.category and channel.category.id != constants.Categories.help_available:
AttributeError: 'DMChannel' object has no attribute 'category'
```


Discussion:

Resolving patch:
diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py
--- a/bot/cogs/help_channels.py
+++ b/bot/cogs/help_channels.py
@@ -420,6 +420,12 @@ def is_dormant_message(self, message: t.Optional[discord.Message]) -> bool:
         embed = message.embeds[0]
         return message.author == self.bot.user and embed.description.strip() == DORMANT_MSG.strip()
 
+    @staticmethod
+    def is_in_category(channel: discord.TextChannel, category_id: int) -> bool:
+        """Return True if `channel` is within a category with `category_id`."""
+        actual_category = getattr(channel, "category", None)
+        return actual_category is not None and actual_category.id == category_id
+
     async def move_idle_channel(self, channel: discord.TextChannel, has_task: bool = True) -> None:
         """
         Make the `channel` dormant if idle or schedule the move if still active.
@@ -581,7 +587,7 @@ async def on_message(self, message: discord.Message) -> None:
             return  # Ignore messages sent by bots.
 
         channel = message.channel
-        if channel.category and channel.category.id != constants.Categories.help_available:
+        if not self.is_in_category(channel, constants.Categories.help_available):
             return  # Ignore messages outside the Available category.
 
         log.trace("Waiting for the cog to be ready before processing messages.")
@@ -591,7 +597,7 @@ async def on_message(self, message: discord.Message) -> None:
         async with self.on_message_lock:
             log.trace(f"on_message lock acquired for {message.id}.")
 
-            if channel.category and channel.category.id != constants.Categories.help_available:
+            if not self.is_in_category(channel, constants.Categories.help_available):
                 log.debug(
                     f"Message {message.id} will not make #{channel} ({channel.id}) in-use "
                     f"because another message in the channel already triggered that."


Test patch: